\
- ',
-
- events: {
- 'change form select': 'onEditorSubmit',
- 'click .editor-add': '_onAddSeries',
- 'click .action-remove-series': 'removeSeries'
- },
initialize: function(options) {
var self = this;
@@ -705,6 +677,15 @@ my.Graph = Backbone.View.extend({
options.state
);
this.state = new recline.Model.ObjectState(stateData);
+ this.editor = new my.GraphControls({
+ model: this.model,
+ state: this.state.toJSON()
+ });
+ this.editor.state.bind('change', function() {
+ self.state.set(self.editor.state.toJSON());
+ self.redraw();
+ });
+ this.elSidebar = this.editor.el;
this.render();
},
@@ -714,56 +695,9 @@ my.Graph = Backbone.View.extend({
var htmls = Mustache.render(this.template, tmplData);
$(this.el).html(htmls);
this.$graph = this.el.find('.panel.graph');
-
- // set up editor from state
- if (this.state.get('graphType')) {
- this._selectOption('.editor-type', this.state.get('graphType'));
- }
- if (this.state.get('group')) {
- this._selectOption('.editor-group', this.state.get('group'));
- }
- // ensure at least one series box shows up
- var tmpSeries = [""];
- if (this.state.get('series').length > 0) {
- tmpSeries = this.state.get('series');
- }
- _.each(tmpSeries, function(series, idx) {
- self.addSeries(idx);
- self._selectOption('.editor-series.js-series-' + idx, series);
- });
return this;
},
- // Private: Helper function to select an option from a select list
- //
- _selectOption: function(id,value){
- var options = this.el.find(id + ' select > option');
- if (options) {
- options.each(function(opt){
- if (this.value == value) {
- $(this).attr('selected','selected');
- return false;
- }
- });
- }
- },
-
- onEditorSubmit: function(e) {
- var select = this.el.find('.editor-group select');
- var $editor = this;
- var $series = this.el.find('.editor-series select');
- var series = $series.map(function () {
- return $(this).val();
- });
- var updatedState = {
- series: $.makeArray(series),
- group: this.el.find('.editor-group select').val(),
- graphType: this.el.find('.editor-type select').val()
- };
- this.state.set(updatedState);
- this.redraw();
- },
-
redraw: function() {
// There appear to be issues generating a Flot graph if either:
@@ -778,6 +712,8 @@ my.Graph = Backbone.View.extend({
}
// check we have something to plot
if (this.state.get('group') && this.state.get('series')) {
+ // faff around with width because flot draws axes *outside* of the element width which means graph can get push down as it hits element next to it
+ this.$graph.width(this.el.width() - 20);
var series = this.createSeries();
var options = this.getGraphOptions(this.state.attributes.graphType);
this.plot = $.plot(this.$graph, series, options);
@@ -941,7 +877,10 @@ my.Graph = Backbone.View.extend({
var yfield = self.model.fields.get(field);
var y = doc.getFieldValue(yfield);
if (typeof x === 'string') {
- x = index;
+ x = parseFloat(x);
+ if (isNaN(x)) {
+ x = index;
+ }
}
// horizontal bar chart
if (self.state.attributes.graphType == 'bars') {
@@ -953,6 +892,128 @@ my.Graph = Backbone.View.extend({
series.push({data: points, label: field});
});
return series;
+ }
+});
+
+my.GraphControls = Backbone.View.extend({
+ className: "editor",
+ template: ' \
+
\
+ \
+
\
+',
+ templateSeriesEditor: ' \
+
\
+ \
+
\
+ \
+
\
+
\
+ ',
+ events: {
+ 'change form select': 'onEditorSubmit',
+ 'click .editor-add': '_onAddSeries',
+ 'click .action-remove-series': 'removeSeries'
+ },
+
+ initialize: function(options) {
+ var self = this;
+ this.el = $(this.el);
+ _.bindAll(this, 'render');
+ this.model.fields.bind('reset', this.render);
+ this.model.fields.bind('add', this.render);
+ this.state = new recline.Model.ObjectState(options.state);
+ this.render();
+ },
+
+ render: function() {
+ var self = this;
+ var tmplData = this.model.toTemplateJSON();
+ var htmls = Mustache.render(this.template, tmplData);
+ this.el.html(htmls);
+
+ // set up editor from state
+ if (this.state.get('graphType')) {
+ this._selectOption('.editor-type', this.state.get('graphType'));
+ }
+ if (this.state.get('group')) {
+ this._selectOption('.editor-group', this.state.get('group'));
+ }
+ // ensure at least one series box shows up
+ var tmpSeries = [""];
+ if (this.state.get('series').length > 0) {
+ tmpSeries = this.state.get('series');
+ }
+ _.each(tmpSeries, function(series, idx) {
+ self.addSeries(idx);
+ self._selectOption('.editor-series.js-series-' + idx, series);
+ });
+ return this;
+ },
+
+ // Private: Helper function to select an option from a select list
+ //
+ _selectOption: function(id,value){
+ var options = this.el.find(id + ' select > option');
+ if (options) {
+ options.each(function(opt){
+ if (this.value == value) {
+ $(this).attr('selected','selected');
+ return false;
+ }
+ });
+ }
+ },
+
+ onEditorSubmit: function(e) {
+ var select = this.el.find('.editor-group select');
+ var $editor = this;
+ var $series = this.el.find('.editor-series select');
+ var series = $series.map(function () {
+ return $(this).val();
+ });
+ var updatedState = {
+ series: $.makeArray(series),
+ group: this.el.find('.editor-group select').val(),
+ graphType: this.el.find('.editor-type select').val()
+ };
+ this.state.set(updatedState);
},
// Public: Adds a new empty series select box to the editor.
@@ -1386,82 +1447,18 @@ this.recline.View = this.recline.View || {};
// }
//
my.Map = Backbone.View.extend({
-
tagName: 'div',
className: 'recline-map',
template: ' \
-
\
-
\
- \
- \
-
\
-
\
+ \
',
// These are the default (case-insensitive) names of field that are used if found.
// If not found, the user will need to define the fields via the editor.
latitudeFieldNames: ['lat','latitude'],
longitudeFieldNames: ['lon','longitude'],
- geometryFieldNames: ['geom','the_geom','geometry','spatial','location'],
-
- // Define here events for UI elements
- events: {
- 'click .editor-update-map': 'onEditorSubmit',
- 'change .editor-field-type': 'onFieldTypeChange',
- 'change #editor-auto-zoom': 'onAutoZoomChange'
- },
+ geometryFieldNames: ['geojson', 'geom','the_geom','geometry','spatial','location'],
initialize: function(options) {
var self = this;
@@ -1469,10 +1466,6 @@ my.Map = Backbone.View.extend({
// Listen to changes in the fields
this.model.fields.bind('change', function() {
- self._setupGeometryField();
- });
- this.model.fields.bind('add', this.render);
- this.model.fields.bind('reset', function(){
self._setupGeometryField()
self.render()
});
@@ -1491,7 +1484,7 @@ my.Map = Backbone.View.extend({
// to display properly
if (self.map){
self.map.invalidateSize();
- if (self._zoomPending && self.autoZoom) {
+ if (self._zoomPending && self.state.get('autoZoom')) {
self._zoomToFeatures();
self._zoomPending = false;
}
@@ -1505,39 +1498,36 @@ my.Map = Backbone.View.extend({
var stateData = _.extend({
geomField: null,
lonField: null,
- latField: null
+ latField: null,
+ autoZoom: true
},
options.state
);
this.state = new recline.Model.ObjectState(stateData);
+ this.menu = new my.MapMenu({
+ model: this.model,
+ state: this.state.toJSON()
+ });
+ this.menu.state.bind('change', function() {
+ self.state.set(self.menu.state.toJSON());
+ self.redraw();
+ });
+ this.elSidebar = this.menu.el;
- this.autoZoom = true;
this.mapReady = false;
this.render();
+ this.redraw();
},
// ### Public: Adds the necessary elements to the page.
//
// Also sets up the editor fields and the map if necessary.
render: function() {
-
var self = this;
htmls = Mustache.render(this.template, this.model.toTemplateJSON());
-
$(this.el).html(htmls);
this.$map = this.el.find('.panel.map');
-
- if (this.geomReady && this.model.fields.length){
- if (this.state.get('geomField')){
- this._selectOption('editor-geom-field',this.state.get('geomField'));
- $('#editor-field-type-geom').attr('checked','checked').change();
- } else{
- this._selectOption('editor-lon-field',this.state.get('lonField'));
- this._selectOption('editor-lat-field',this.state.get('latField'));
- $('#editor-field-type-latlon').attr('checked','checked').change();
- }
- }
return this;
},
@@ -1553,14 +1543,14 @@ my.Map = Backbone.View.extend({
var self = this;
action = action || 'refresh';
// try to set things up if not already
- if (!self.geomReady){
+ if (!self._geomReady()){
self._setupGeometryField();
}
if (!self.mapReady){
self._setupMap();
}
- if (this.geomReady && this.mapReady){
+ if (this._geomReady() && this.mapReady){
if (action == 'reset' || action == 'refresh'){
this.features.clearLayers();
this._add(this.model.currentRecords.models);
@@ -1569,7 +1559,7 @@ my.Map = Backbone.View.extend({
} else if (action == 'remove' && doc){
this._remove(doc);
}
- if (this.autoZoom){
+ if (this.state.get('autoZoom')){
if (this.visible){
this._zoomToFeatures();
} else {
@@ -1579,51 +1569,8 @@ my.Map = Backbone.View.extend({
}
},
- //
- // UI Event handlers
- //
-
- // Public: Update map with user options
- //
- // Right now the only configurable option is what field(s) contains the
- // location information.
- //
- onEditorSubmit: function(e){
- e.preventDefault();
- if ($('#editor-field-type-geom').attr('checked')){
- this.state.set({
- geomField: $('.editor-geom-field > select > option:selected').val(),
- lonField: null,
- latField: null
- });
- } else {
- this.state.set({
- geomField: null,
- lonField: $('.editor-lon-field > select > option:selected').val(),
- latField: $('.editor-lat-field > select > option:selected').val()
- });
- }
- this.geomReady = (this.state.get('geomField') || (this.state.get('latField') && this.state.get('lonField')));
- this.redraw();
-
- return false;
- },
-
- // Public: Shows the relevant select lists depending on the location field
- // type selected.
- //
- onFieldTypeChange: function(e){
- if (e.target.value == 'geom'){
- $('.editor-field-type-geom').show();
- $('.editor-field-type-latlon').hide();
- } else {
- $('.editor-field-type-geom').hide();
- $('.editor-field-type-latlon').show();
- }
- },
-
- onAutoZoomChange: function(e){
- this.autoZoom = !this.autoZoom;
+ _geomReady: function() {
+ return Boolean(this.state.get('geomField') || (this.state.get('latField') && this.state.get('lonField')));
},
// Private: Add one or n features to the map
@@ -1703,7 +1650,7 @@ my.Map = Backbone.View.extend({
// Private: Return a GeoJSON geomtry extracted from the record fields
//
_getGeometryFromRecord: function(doc){
- if (this.geomReady){
+ if (this._geomReady()){
if (this.state.get('geomField')){
var value = doc.get(this.state.get('geomField'));
if (typeof(value) === 'string'){
@@ -1742,16 +1689,14 @@ my.Map = Backbone.View.extend({
//
// If not found, the user can define them via the UI form.
_setupGeometryField: function(){
- var geomField, latField, lonField;
- this.geomReady = (this.state.get('geomField') || (this.state.get('latField') && this.state.get('lonField')));
// should not overwrite if we have already set this (e.g. explicitly via state)
- if (!this.geomReady) {
+ if (!this._geomReady()) {
this.state.set({
geomField: this._checkField(this.geometryFieldNames),
latField: this._checkField(this.latitudeFieldNames),
lonField: this._checkField(this.longitudeFieldNames)
});
- this.geomReady = (this.state.get('geomField') || (this.state.get('latField') && this.state.get('lonField')));
+ this.menu.state.set(this.state.toJSON());
}
},
@@ -1789,7 +1734,6 @@ my.Map = Backbone.View.extend({
// on [OpenStreetMap](http://openstreetmap.org).
//
_setupMap: function(){
-
this.map = new L.Map(this.$map.get(0));
var mapUrl = "http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png";
@@ -1845,8 +1789,645 @@ my.Map = Backbone.View.extend({
});
}
}
+});
- });
+my.MapMenu = Backbone.View.extend({
+ className: 'editor',
+
+ template: ' \
+ \
+',
+
+ // Define here events for UI elements
+ events: {
+ 'click .editor-update-map': 'onEditorSubmit',
+ 'change .editor-field-type': 'onFieldTypeChange',
+ 'change #editor-auto-zoom': 'onAutoZoomChange'
+ },
+
+ initialize: function(options) {
+ var self = this;
+ this.el = $(this.el);
+ _.bindAll(this, 'render');
+ this.model.fields.bind('change', this.render);
+ this.state = new recline.Model.ObjectState(options.state);
+ this.state.bind('change', this.render);
+ this.render();
+ },
+
+ // ### Public: Adds the necessary elements to the page.
+ //
+ // Also sets up the editor fields and the map if necessary.
+ render: function() {
+ var self = this;
+ htmls = Mustache.render(this.template, this.model.toTemplateJSON());
+ $(this.el).html(htmls);
+
+ if (this._geomReady() && this.model.fields.length){
+ if (this.state.get('geomField')){
+ this._selectOption('editor-geom-field',this.state.get('geomField'));
+ $('#editor-field-type-geom').attr('checked','checked').change();
+ } else{
+ this._selectOption('editor-lon-field',this.state.get('lonField'));
+ this._selectOption('editor-lat-field',this.state.get('latField'));
+ $('#editor-field-type-latlon').attr('checked','checked').change();
+ }
+ }
+ return this;
+ },
+
+ _geomReady: function() {
+ return Boolean(this.state.get('geomField') || (this.state.get('latField') && this.state.get('lonField')));
+ },
+
+ // ## UI Event handlers
+ //
+
+ // Public: Update map with user options
+ //
+ // Right now the only configurable option is what field(s) contains the
+ // location information.
+ //
+ onEditorSubmit: function(e){
+ e.preventDefault();
+ if (this.el.find('#editor-field-type-geom').attr('checked')){
+ this.state.set({
+ geomField: this.el.find('.editor-geom-field > select > option:selected').val(),
+ lonField: null,
+ latField: null
+ });
+ } else {
+ this.state.set({
+ geomField: null,
+ lonField: this.el.find('.editor-lon-field > select > option:selected').val(),
+ latField: this.el.find('.editor-lat-field > select > option:selected').val()
+ });
+ }
+ return false;
+ },
+
+ // Public: Shows the relevant select lists depending on the location field
+ // type selected.
+ //
+ onFieldTypeChange: function(e){
+ if (e.target.value == 'geom'){
+ this.el.find('.editor-field-type-geom').show();
+ this.el.find('.editor-field-type-latlon').hide();
+ } else {
+ this.el.find('.editor-field-type-geom').hide();
+ this.el.find('.editor-field-type-latlon').show();
+ }
+ },
+
+ onAutoZoomChange: function(e){
+ this.state.set({autoZoom: !this.state.get('autoZoom')});
+ },
+
+ // Private: Helper function to select an option from a select list
+ //
+ _selectOption: function(id,value){
+ var options = this.el.find('.' + id + ' > select > option');
+ if (options){
+ options.each(function(opt){
+ if (this.value == value) {
+ $(this).attr('selected','selected');
+ return false;
+ }
+ });
+ }
+ }
+});
+
+})(jQuery, recline.View);
+
+/*jshint multistr:true */
+
+// Standard JS module setup
+this.recline = this.recline || {};
+this.recline.View = this.recline.View || {};
+
+(function($, my) {
+// ## MultiView
+//
+// Manage multiple views together along with query editor etc. Usage:
+//
+//
+// var myExplorer = new model.recline.MultiView({
+// model: {{recline.Model.Dataset instance}}
+// el: {{an existing dom element}}
+// views: {{dataset views}}
+// state: {{state configuration -- see below}}
+// });
+//
+//
+// ### Parameters
+//
+// **model**: (required) recline.model.Dataset instance.
+//
+// **el**: (required) DOM element to bind to. NB: the element already
+// being in the DOM is important for rendering of some subviews (e.g.
+// Graph).
+//
+// **views**: (optional) the dataset views (Grid, Graph etc) for
+// MultiView to show. This is an array of view hashes. If not provided
+// initialize with (recline.View.)Grid, Graph, and Map views (with obvious id
+// and labels!).
+//
+//
+// var views = [
+// {
+// id: 'grid', // used for routing
+// label: 'Grid', // used for view switcher
+// view: new recline.View.Grid({
+// model: dataset
+// })
+// },
+// {
+// id: 'graph',
+// label: 'Graph',
+// view: new recline.View.Graph({
+// model: dataset
+// })
+// }
+// ];
+//
+//
+// **state**: standard state config for this view. This state is slightly
+// special as it includes config of many of the subviews.
+//
+//
+// state = {
+// query: {dataset query state - see dataset.queryState object}
+// view-{id1}: {view-state for this view}
+// view-{id2}: {view-state for }
+// ...
+// // Explorer
+// currentView: id of current view (defaults to first view if not specified)
+// readOnly: (default: false) run in read-only mode
+// }
+//
+//
+// Note that at present we do *not* serialize information about the actual set
+// of views in use -- e.g. those specified by the views argument -- but instead
+// expect either that the default views are fine or that the client to have
+// initialized the MultiView with the relevant views themselves.
+my.MultiView = Backbone.View.extend({
+ template: ' \
+
';
+ }
+ var _templated = $(Mustache.render(_template, tmplData));
+ _templated = $(_templated).appendTo($('.recline-data-explorer .alert-messages'));
+ if (!flash.persist) {
+ setTimeout(function() {
+ $(_templated).fadeOut(1000, function() {
+ $(this).remove();
+ });
+ }, 1000);
+ }
+ },
+
+ // ### clearNotifications
+ //
+ // Clear all existing notifications
+ clearNotifications: function() {
+ var $notifications = $('.recline-data-explorer .alert-messages .alert');
+ $notifications.fadeOut(1500, function() {
+ $(this).remove();
+ });
+ }
+});
+
+// ### MultiView.restore
+//
+// Restore a MultiView instance from a serialized state including the associated dataset
+my.MultiView.restore = function(state) {
+ var dataset = recline.Model.Dataset.restore(state);
+ var explorer = new my.MultiView({
+ model: dataset,
+ state: state
+ });
+ return explorer;
+}
+
+
+// ## Miscellaneous Utilities
+var urlPathRegex = /^([^?]+)(\?.*)?/;
+
+// Parse the Hash section of a URL into path and query string
+my.parseHashUrl = function(hashUrl) {
+ var parsed = urlPathRegex.exec(hashUrl);
+ if (parsed === null) {
+ return {};
+ } else {
+ return {
+ path: parsed[1],
+ query: parsed[2] || ''
+ };
+ }
+};
+
+// Parse a URL query string (?xyz=abc...) into a dictionary.
+my.parseQueryString = function(q) {
+ if (!q) {
+ return {};
+ }
+ var urlParams = {},
+ e, d = function (s) {
+ return unescape(s.replace(/\+/g, " "));
+ },
+ r = /([^&=]+)=?([^&]*)/g;
+
+ if (q && q.length && q[0] === '?') {
+ q = q.slice(1);
+ }
+ while (e = r.exec(q)) {
+ // TODO: have values be array as query string allow repetition of keys
+ urlParams[d(e[1])] = d(e[2]);
+ }
+ return urlParams;
+};
+
+// Parse the query string out of the URL hash
+my.parseHashQueryString = function() {
+ q = my.parseHashUrl(window.location.hash).query;
+ return my.parseQueryString(q);
+};
+
+// Compse a Query String
+my.composeQueryString = function(queryParams) {
+ var queryString = '?';
+ var items = [];
+ $.each(queryParams, function(key, value) {
+ if (typeof(value) === 'object') {
+ value = JSON.stringify(value);
+ }
+ items.push(key + '=' + encodeURIComponent(value));
+ });
+ queryString += items.join('&');
+ return queryString;
+};
+
+my.getNewHashForQueryString = function(queryParams) {
+ var queryPart = my.composeQueryString(queryParams);
+ if (window.location.hash) {
+ // slice(1) to remove # at start
+ return window.location.hash.split('?')[0].slice(1) + queryPart;
+ } else {
+ return queryPart;
+ }
+};
+
+my.setHashQueryString = function(queryParams) {
+ window.location.hash = my.getNewHashForQueryString(queryParams);
+};
})(jQuery, recline.View);
@@ -2262,6 +2843,14 @@ my.Timeline = Backbone.View.extend({
out.timeline.date.push(tlEntry);
}
});
+ // if no entries create a placeholder entry to prevent Timeline crashing with error
+ if (out.timeline.date.length === 0) {
+ var tlEntry = {
+ "startDate": '2000,1,1',
+ "headline": 'No data to show!'
+ };
+ out.timeline.date.push(tlEntry);
+ }
return out;
},
@@ -2462,460 +3051,6 @@ my.ColumnTransform = Backbone.View.extend({
})(jQuery, recline.View);
/*jshint multistr:true */
-// Standard JS module setup
-this.recline = this.recline || {};
-this.recline.View = this.recline.View || {};
-
-(function($, my) {
-// ## MultiView
-//
-// Manage multiple views together along with query editor etc. Usage:
-//
-//
-// var myExplorer = new model.recline.MultiView({
-// model: {{recline.Model.Dataset instance}}
-// el: {{an existing dom element}}
-// views: {{dataset views}}
-// state: {{state configuration -- see below}}
-// });
-//
-//
-// ### Parameters
-//
-// **model**: (required) recline.model.Dataset instance.
-//
-// **el**: (required) DOM element to bind to. NB: the element already
-// being in the DOM is important for rendering of some subviews (e.g.
-// Graph).
-//
-// **views**: (optional) the dataset views (Grid, Graph etc) for
-// MultiView to show. This is an array of view hashes. If not provided
-// initialize with (recline.View.)Grid, Graph, and Map views (with obvious id
-// and labels!).
-//
-//
-// var views = [
-// {
-// id: 'grid', // used for routing
-// label: 'Grid', // used for view switcher
-// view: new recline.View.Grid({
-// model: dataset
-// })
-// },
-// {
-// id: 'graph',
-// label: 'Graph',
-// view: new recline.View.Graph({
-// model: dataset
-// })
-// }
-// ];
-//
-//
-// **state**: standard state config for this view. This state is slightly
-// special as it includes config of many of the subviews.
-//
-//
-// state = {
-// query: {dataset query state - see dataset.queryState object}
-// view-{id1}: {view-state for this view}
-// view-{id2}: {view-state for }
-// ...
-// // Explorer
-// currentView: id of current view (defaults to first view if not specified)
-// readOnly: (default: false) run in read-only mode
-// }
-//
-//
-// Note that at present we do *not* serialize information about the actual set
-// of views in use -- e.g. those specified by the views argument -- but instead
-// expect either that the default views are fine or that the client to have
-// initialized the MultiView with the relevant views themselves.
-my.MultiView = Backbone.View.extend({
- template: ' \
-
\
+ ',
+
+ events: {
+ 'click .js-show-hide': 'onShowHide'
+ },
+ initialize: function(model) {
+ var self = this;
+ this.el = $(this.el);
+ _.bindAll(this, 'render');
+
+ // TODO: this is quite restrictive in terms of when it is re-run
+ // e.g. a change in type will not trigger a re-run atm.
+ // being more liberal (e.g. binding to all) can lead to being called a lot (e.g. for change:width)
+ this.model.fields.bind('reset', function(action) {
+ self.model.fields.each(function(field) {
+ field.facets.unbind('all', self.render);
+ field.facets.bind('all', self.render);
+ });
+ // fields can get reset or changed in which case we need to recalculate
+ self.model.getFieldsSummary();
+ self.render();
+ });
+ this.render();
+ },
+ render: function() {
+ var self = this;
+ var tmplData = {
+ fields: []
+ };
+ this.model.fields.each(function(field) {
+ var out = field.toJSON();
+ out.facets = field.facets.toJSON();
+ tmplData.fields.push(out);
+ });
+ var templated = Mustache.render(this.template, tmplData);
+ this.el.html(templated);
+ this.el.find('.collapse').collapse('hide');
+ },
+ onShowHide: function(e) {
+ e.preventDefault();
+ var $target = $(e.target);
+ // weird collapse class seems to have been removed (can watch this happen
+ // if you watch dom) but could not work why. Absence of collapse then meant
+ // we could not toggle.
+ // This seems to fix the problem.
+ this.el.find('.accordion-body').addClass('collapse');;
+ if ($target.text() === '+') {
+ this.el.find('.collapse').collapse('show');
+ $target.text('-');
+ } else {
+ this.el.find('.collapse').collapse('hide');
+ $target.text('+');
+ }
+ }
+});
+
})(jQuery, recline.View);
/*jshint multistr:true */
@@ -3004,49 +3254,55 @@ this.recline.View = this.recline.View || {};
my.FilterEditor = Backbone.View.extend({
className: 'recline-filter-editor well',
template: ' \
- × \
-