diff --git a/dist/recline.dataset.js b/dist/recline.dataset.js index 3ca6fd41..bd311509 100644 --- a/dist/recline.dataset.js +++ b/dist/recline.dataset.js @@ -3,9 +3,10 @@ this.recline = this.recline || {}; this.recline.Model = this.recline.Model || {}; (function(my) { + "use strict"; // use either jQuery or Underscore Deferred depending on what is available -var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; +var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## Dataset my.Dataset = Backbone.Model.extend({ @@ -298,7 +299,7 @@ my.Record = Backbone.Model.extend({ // // NB: if field is undefined a default '' value will be returned getFieldValue: function(field) { - val = this.getFieldValueUnrendered(field); + var val = this.getFieldValueUnrendered(field); if (field && !_.isUndefined(field.renderer)) { val = field.renderer(val, field, this.toJSON()); } @@ -588,10 +589,11 @@ this.recline.Backend = this.recline.Backend || {}; this.recline.Backend.Memory = this.recline.Backend.Memory || {}; (function(my) { + "use strict"; my.__type__ = 'memory'; // private data - use either jQuery or Underscore Deferred depending on what is available - var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; + var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## Data Wrapper // @@ -693,7 +695,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {}; 'float': function (e) { return parseFloat(e, 10); }, number: function (e) { return parseFloat(e, 10); }, string : function (e) { return e.toString() }, - date : function (e) { return new Date(e).valueOf() }, + date : function (e) { return moment(e).valueOf() }, datetime : function (e) { return new Date(e).valueOf() } }; var keyedFields = {}; diff --git a/dist/recline.js b/dist/recline.js index 9df9cacc..87af5a40 100644 --- a/dist/recline.js +++ b/dist/recline.js @@ -4,10 +4,11 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {}; // Note that provision of jQuery is optional (it is **only** needed if you use fetch on a remote file) (function(my) { + "use strict"; my.__type__ = 'csv'; // use either jQuery or Underscore Deferred depending on what is available - var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; + var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## fetch // @@ -295,6 +296,7 @@ this.recline.Backend = this.recline.Backend || {}; this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {}; (function(my) { + "use strict"; my.__type__ = 'dataproxy'; // URL for the dataproxy my.dataproxy_url = '//jsonpdataproxy.appspot.com'; @@ -304,7 +306,7 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {}; // use either jQuery or Underscore Deferred depending on what is available - var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; + var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## load // @@ -370,10 +372,11 @@ this.recline.Backend = this.recline.Backend || {}; this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {}; (function($, my) { + "use strict"; my.__type__ = 'elasticsearch'; // use either jQuery or Underscore Deferred depending on what is available - var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; + var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## ElasticSearch Wrapper // @@ -567,8 +570,8 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {}; fields: fieldData }); }) - .fail(function(arguments) { - dfd.reject(arguments); + .fail(function(args) { + dfd.reject(args); }); return dfd.promise(); }; @@ -655,10 +658,11 @@ this.recline.Backend = this.recline.Backend || {}; this.recline.Backend.GDocs = this.recline.Backend.GDocs || {}; (function(my) { + "use strict"; my.__type__ = 'gdocs'; // use either jQuery or Underscore Deferred depending on what is available - var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; + var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## Google spreadsheet backend // @@ -822,10 +826,11 @@ this.recline.Backend = this.recline.Backend || {}; this.recline.Backend.Memory = this.recline.Backend.Memory || {}; (function(my) { + "use strict"; my.__type__ = 'memory'; // private data - use either jQuery or Underscore Deferred depending on what is available - var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; + var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## Data Wrapper // @@ -927,7 +932,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {}; 'float': function (e) { return parseFloat(e, 10); }, number: function (e) { return parseFloat(e, 10); }, string : function (e) { return e.toString() }, - date : function (e) { return new Date(e).valueOf() }, + date : function (e) { return moment(e).valueOf() }, datetime : function (e) { return new Date(e).valueOf() } }; var keyedFields = {}; @@ -1123,9 +1128,10 @@ this.recline = this.recline || {}; this.recline.Model = this.recline.Model || {}; (function(my) { + "use strict"; // use either jQuery or Underscore Deferred depending on what is available -var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred; +var Deferred = _.isUndefined(window.jQuery) ? _.Deferred : jQuery.Deferred; // ## Dataset my.Dataset = Backbone.Model.extend({ @@ -1418,7 +1424,7 @@ my.Record = Backbone.Model.extend({ // // NB: if field is undefined a default '' value will be returned getFieldValue: function(field) { - val = this.getFieldValueUnrendered(field); + var val = this.getFieldValueUnrendered(field); if (field && !_.isUndefined(field.renderer)) { val = field.renderer(val, field, this.toJSON()); } @@ -1709,7 +1715,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { - + "use strict"; // ## Graph view for a Dataset using Flot graphing library. // // Initialization arguments (in a hash in first parameter): @@ -1743,14 +1749,11 @@ my.Flot = Backbone.View.extend({ var self = this; this.graphColors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"]; - this.el = $(this.el); _.bindAll(this, 'render', 'redraw', '_toolTip', '_xaxisLabel'); this.needToRedraw = false; - this.model.bind('change', this.render); - this.model.fields.bind('reset', this.render); - this.model.fields.bind('add', this.render); - this.model.records.bind('add', this.redraw); - this.model.records.bind('reset', this.redraw); + this.listenTo(this.model, 'change', this.render); + this.listenTo(this.model.fields, 'reset add', this.render); + this.listenTo(this.model.records, 'reset add', this.redraw); var stateData = _.extend({ group: null, // so that at least one series chooser box shows up @@ -1765,29 +1768,34 @@ my.Flot = Backbone.View.extend({ model: this.model, state: this.state.toJSON() }); - this.editor.state.bind('change', function() { + this.listenTo(this.editor.state, 'change', function() { self.state.set(self.editor.state.toJSON()); self.redraw(); }); - this.elSidebar = this.editor.el; + this.elSidebar = this.editor.$el; }, render: function() { var self = this; var tmplData = this.model.toTemplateJSON(); var htmls = Mustache.render(this.template, tmplData); - $(this.el).html(htmls); - this.$graph = this.el.find('.panel.graph'); + this.$el.html(htmls); + this.$graph = this.$el.find('.panel.graph'); this.$graph.on("plothover", this._toolTip); return this; }, + remove: function () { + this.editor.remove(); + Backbone.View.prototype.remove.apply(this, arguments); + }, + redraw: function() { // There are issues generating a Flot graph if either: // * The relevant div that graph attaches to his hidden at the moment of creating the plot -- Flot will complain with // Uncaught Invalid dimensions for plot, width = 0, height = 0 // * There is no data for the plot -- either same error or may have issues later with errors like 'non-existent node-value' - var areWeVisible = !jQuery.expr.filters.hidden(this.el[0]); + var areWeVisible = !jQuery.expr.filters.hidden(this.el); if ((!areWeVisible || this.model.records.length === 0)) { this.needToRedraw = true; return; @@ -2108,10 +2116,8 @@ my.FlotControls = Backbone.View.extend({ 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.listenTo(this.model.fields, 'reset add', this.render); this.state = new recline.Model.ObjectState(options.state); this.render(); }, @@ -2120,7 +2126,7 @@ my.FlotControls = Backbone.View.extend({ var self = this; var tmplData = this.model.toTemplateJSON(); var htmls = Mustache.render(this.template, tmplData); - this.el.html(htmls); + this.$el.html(htmls); // set up editor from state if (this.state.get('graphType')) { @@ -2144,7 +2150,7 @@ my.FlotControls = Backbone.View.extend({ // Private: Helper function to select an option from a select list // _selectOption: function(id,value){ - var options = this.el.find(id + ' select > option'); + var options = this.$el.find(id + ' select > option'); if (options) { options.each(function(opt){ if (this.value == value) { @@ -2156,16 +2162,16 @@ my.FlotControls = Backbone.View.extend({ }, onEditorSubmit: function(e) { - var select = this.el.find('.editor-group select'); + var select = this.$el.find('.editor-group select'); var $editor = this; - var $series = this.el.find('.editor-series select'); + 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() + group: this.$el.find('.editor-group select').val(), + graphType: this.$el.find('.editor-type select').val() }; this.state.set(updatedState); }, @@ -2182,7 +2188,7 @@ my.FlotControls = Backbone.View.extend({ }, this.model.toTemplateJSON()); var htmls = Mustache.render(this.templateSeriesEditor, data); - this.el.find('.editor-series-group').append(htmls); + this.$el.find('.editor-series-group').append(htmls); return this; }, @@ -2213,6 +2219,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; // ## (Data) Grid Dataset View // // Provides a tabular view on a Dataset. @@ -2224,11 +2231,8 @@ my.Grid = Backbone.View.extend({ initialize: function(modelEtc) { var self = this; - this.el = $(this.el); _.bindAll(this, 'render', 'onHorizontalScroll'); - this.model.records.bind('add', this.render); - this.model.records.bind('reset', this.render); - this.model.records.bind('remove', this.render); + this.listenTo(this.model.records, 'add reset remove', this.render); this.tempState = {}; var state = _.extend({ hiddenFields: [] @@ -2268,7 +2272,7 @@ my.Grid = Backbone.View.extend({ onHorizontalScroll: function(e) { var currentScroll = $(e.target).scrollLeft(); - this.el.find('.recline-grid thead tr').scrollLeft(currentScroll); + this.$el.find('.recline-grid thead tr').scrollLeft(currentScroll); }, // ====================================================== @@ -2296,7 +2300,7 @@ my.Grid = Backbone.View.extend({ var modelData = this.model.toJSON(); modelData.notEmpty = ( this.fields.length > 0 ); // TODO: move this sort of thing into a toTemplateJSON method on Dataset? - modelData.fields = _.map(this.fields, function(field) { + modelData.fields = this.fields.map(function(field) { return field.toJSON(); }); // last header width = scroll bar - border (2px) */ @@ -2305,17 +2309,18 @@ my.Grid = Backbone.View.extend({ }, render: function() { var self = this; - this.fields = this.model.fields.filter(function(field) { + this.fields = new recline.Model.FieldList(this.model.fields.filter(function(field) { return _.indexOf(self.state.get('hiddenFields'), field.id) == -1; - }); + })); + this.scrollbarDimensions = this.scrollbarDimensions || this._scrollbarSize(); // skip measurement if already have dimensions var numFields = this.fields.length; // compute field widths (-20 for first menu col + 10px for padding on each col and finally 16px for the scrollbar) - var fullWidth = self.el.width() - 20 - 10 * numFields - this.scrollbarDimensions.width; + var fullWidth = self.$el.width() - 20 - 10 * numFields - this.scrollbarDimensions.width; var width = parseInt(Math.max(50, fullWidth / numFields), 10); // if columns extend outside viewport then remainder is 0 var remainder = Math.max(fullWidth - numFields * width,0); - _.each(this.fields, function(field, idx) { + this.fields.each(function(field, idx) { // add the remainder to the first field width so we make up full col if (idx === 0) { field.set({width: width+remainder}); @@ -2324,10 +2329,10 @@ my.Grid = Backbone.View.extend({ } }); var htmls = Mustache.render(this.template, this.toTemplateJSON()); - this.el.html(htmls); + this.$el.html(htmls); this.model.records.forEach(function(doc) { var tr = $(''); - self.el.find('tbody').append(tr); + self.$el.find('tbody').append(tr); var newView = new my.GridRow({ model: doc, el: tr, @@ -2336,12 +2341,12 @@ my.Grid = Backbone.View.extend({ newView.render(); }); // hide extra header col if no scrollbar to avoid unsightly overhang - var $tbody = this.el.find('tbody')[0]; + var $tbody = this.$el.find('tbody')[0]; if ($tbody.scrollHeight <= $tbody.offsetHeight) { - this.el.find('th.last-header').hide(); + this.$el.find('th.last-header').hide(); } - this.el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0)); - this.el.find('.recline-grid tbody').scroll(this.onHorizontalScroll); + this.$el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0)); + this.$el.find('.recline-grid tbody').scroll(this.onHorizontalScroll); return this; }, @@ -2377,8 +2382,7 @@ my.GridRow = Backbone.View.extend({ initialize: function(initData) { _.bindAll(this, 'render'); this._fields = initData.fields; - this.el = $(this.el); - this.model.bind('change', this.render); + this.listenTo(this.model, 'change', this.render); }, template: ' \ @@ -2411,9 +2415,9 @@ my.GridRow = Backbone.View.extend({ }, render: function() { - this.el.attr('data-id', this.model.id); + this.$el.attr('data-id', this.model.id); var html = Mustache.render(this.template, this.toTemplateJSON()); - $(this.el).html(html); + this.$el.html(html); return this; }, @@ -2433,7 +2437,7 @@ my.GridRow = Backbone.View.extend({ ', onEditClick: function(e) { - var editing = this.el.find('.data-table-cell-editor-editor'); + var editing = this.$el.find('.data-table-cell-editor-editor'); if (editing.length > 0) { editing.parents('.data-table-cell-value').html(editing.text()).siblings('.data-table-cell-edit').removeClass("hidden"); } @@ -2479,7 +2483,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { - + "use strict"; // ## Map view for a Dataset using Leaflet mapping library. // // This view allows to plot gereferenced records on a map. The location @@ -2526,7 +2530,6 @@ my.Map = Backbone.View.extend({ initialize: function(options) { var self = this; - this.el = $(this.el); this.visible = true; this.mapReady = false; // this will be the Leaflet L.Map object (setup below) @@ -2553,32 +2556,32 @@ my.Map = Backbone.View.extend({ }; // Listen to changes in the fields - this.model.fields.bind('change', function() { + this.listenTo(this.model.fields, 'change', function() { self._setupGeometryField(); self.render(); }); // Listen to changes in the records - this.model.records.bind('add', function(doc){self.redraw('add',doc);}); - this.model.records.bind('change', function(doc){ + this.listenTo(this.model.records, 'add', function(doc){self.redraw('add',doc);}); + this.listenTo(this.model.records, 'change', function(doc){ self.redraw('remove',doc); self.redraw('add',doc); }); - this.model.records.bind('remove', function(doc){self.redraw('remove',doc);}); - this.model.records.bind('reset', function(){self.redraw('reset');}); + this.listenTo(this.model.records, 'remove', function(doc){self.redraw('remove',doc);}); + this.listenTo(this.model.records, 'reset', function(){self.redraw('reset');}); this.menu = new my.MapMenu({ model: this.model, state: this.state.toJSON() }); - this.menu.state.bind('change', function() { + this.listenTo(this.menu.state, 'change', function() { self.state.set(self.menu.state.toJSON()); self.redraw(); }); - this.state.bind('change', function() { + this.listenTo(this.state, 'change', function() { self.redraw(); }); - this.elSidebar = this.menu.el; + this.elSidebar = this.menu.$el; }, // ## Customization Functions @@ -2598,7 +2601,7 @@ my.Map = Backbone.View.extend({ // } infobox: function(record) { var html = ''; - for (key in record.attributes){ + for (var key in record.attributes){ if (!(this.state.get('geomField') && key == this.state.get('geomField'))){ html += '
' + key + ': '+ record.attributes[key] + '
'; } @@ -2647,10 +2650,9 @@ my.Map = Backbone.View.extend({ // 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'); + var htmls = Mustache.render(this.template, this.model.toTemplateJSON()); + this.$el.html(htmls); + this.$map = this.$el.find('.panel.map'); this.redraw(); return this; }, @@ -2801,7 +2803,7 @@ my.Map = Backbone.View.extend({ if (!(docs instanceof Array)) docs = [docs]; _.each(docs,function(doc){ - for (key in self.features._layers){ + for (var key in self.features._layers){ if (self.features._layers[key].feature.properties.cid == doc.cid){ self.features.removeLayer(self.features._layers[key]); } @@ -3008,7 +3010,6 @@ my.MapMenu = Backbone.View.extend({ Cluster markers \ \ \ - \ \ ', @@ -3022,11 +3023,10 @@ my.MapMenu = Backbone.View.extend({ initialize: function(options) { var self = this; - this.el = $(this.el); _.bindAll(this, 'render'); - this.model.fields.bind('change', this.render); + this.listenTo(this.model.fields, 'change', this.render); this.state = new recline.Model.ObjectState(options.state); - this.state.bind('change', this.render); + this.listenTo(this.state, 'change', this.render); this.render(); }, @@ -3035,28 +3035,28 @@ my.MapMenu = Backbone.View.extend({ // 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); + var 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')); - this.el.find('#editor-field-type-geom').attr('checked','checked').change(); + this.$el.find('#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')); - this.el.find('#editor-field-type-latlon').attr('checked','checked').change(); + this.$el.find('#editor-field-type-latlon').attr('checked','checked').change(); } } if (this.state.get('autoZoom')) { - this.el.find('#editor-auto-zoom').attr('checked', 'checked'); + this.$el.find('#editor-auto-zoom').attr('checked', 'checked'); } else { - this.el.find('#editor-auto-zoom').removeAttr('checked'); + this.$el.find('#editor-auto-zoom').removeAttr('checked'); } if (this.state.get('cluster')) { - this.el.find('#editor-cluster').attr('checked', 'checked'); + this.$el.find('#editor-cluster').attr('checked', 'checked'); } else { - this.el.find('#editor-cluster').removeAttr('checked'); + this.$el.find('#editor-cluster').removeAttr('checked'); } return this; }, @@ -3075,17 +3075,17 @@ my.MapMenu = Backbone.View.extend({ // onEditorSubmit: function(e){ e.preventDefault(); - if (this.el.find('#editor-field-type-geom').attr('checked')){ + if (this.$el.find('#editor-field-type-geom').attr('checked')){ this.state.set({ - geomField: this.el.find('.editor-geom-field > select > option:selected').val(), + 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() + lonField: this.$el.find('.editor-lon-field > select > option:selected').val(), + latField: this.$el.find('.editor-lat-field > select > option:selected').val() }); } return false; @@ -3096,11 +3096,11 @@ my.MapMenu = Backbone.View.extend({ // onFieldTypeChange: function(e){ if (e.target.value == 'geom'){ - this.el.find('.editor-field-type-geom').show(); - this.el.find('.editor-field-type-latlon').hide(); + 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(); + this.$el.find('.editor-field-type-geom').hide(); + this.$el.find('.editor-field-type-latlon').show(); } }, @@ -3115,7 +3115,7 @@ my.MapMenu = Backbone.View.extend({ // Private: Helper function to select an option from a select list // _selectOption: function(id,value){ - var options = this.el.find('.' + id + ' > select > option'); + var options = this.$el.find('.' + id + ' > select > option'); if (options){ options.each(function(opt){ if (this.value == value) { @@ -3136,6 +3136,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; // ## MultiView // // Manage multiple views together along with query editor etc. Usage: @@ -3260,7 +3261,6 @@ my.MultiView = Backbone.View.extend({ initialize: function(options) { var self = this; - this.el = $(this.el); this._setupState(options.state); // Hash of 'page' views (i.e. those for whole page) keyed by page name @@ -3330,30 +3330,30 @@ my.MultiView = Backbone.View.extend({ } this._showHideSidebar(); - this.model.bind('query:start', function() { - self.notify({loader: true, persist: true}); - }); - this.model.bind('query:done', function() { - self.clearNotifications(); - self.el.find('.doc-count').text(self.model.recordCount || 'Unknown'); - }); - this.model.bind('query:fail', function(error) { - self.clearNotifications(); - var msg = ''; - if (typeof(error) == 'string') { - msg = error; - } else if (typeof(error) == 'object') { - if (error.title) { - msg = error.title + ': '; - } - if (error.message) { - msg += error.message; - } - } else { - msg = 'There was an error querying the backend'; + this.listenTo(this.model, 'query:start', function() { + self.notify({loader: true, persist: true}); + }); + this.listenTo(this.model, 'query:done', function() { + self.clearNotifications(); + self.$el.find('.doc-count').text(self.model.recordCount || 'Unknown'); + }); + this.listenTo(this.model, 'query:fail', function(error) { + self.clearNotifications(); + var msg = ''; + if (typeof(error) == 'string') { + msg = error; + } else if (typeof(error) == 'object') { + if (error.title) { + msg = error.title + ': '; } - self.notify({message: msg, category: 'error', persist: true}); - }); + if (error.message) { + msg += error.message; + } + } else { + msg = 'There was an error querying the backend'; + } + self.notify({message: msg, category: 'error', persist: true}); + }); // retrieve basic data like fields etc // note this.model and dataset returned are the same @@ -3366,7 +3366,7 @@ my.MultiView = Backbone.View.extend({ }, setReadOnly: function() { - this.el.addClass('recline-read-only'); + this.$el.addClass('recline-read-only'); }, render: function() { @@ -3374,11 +3374,11 @@ my.MultiView = Backbone.View.extend({ tmplData.views = this.pageViews; tmplData.sidebarViews = this.sidebarViews; var template = Mustache.render(this.template, tmplData); - $(this.el).html(template); + this.$el.html(template); // now create and append other views - var $dataViewContainer = this.el.find('.data-view-container'); - var $dataSidebar = this.el.find('.data-view-sidebar'); + var $dataViewContainer = this.$el.find('.data-view-container'); + var $dataSidebar = this.$el.find('.data-view-sidebar'); // the main views _.each(this.pageViews, function(view, pageName) { @@ -3390,25 +3390,37 @@ my.MultiView = Backbone.View.extend({ }); _.each(this.sidebarViews, function(view) { - this['$'+view.id] = view.view.el; + this['$'+view.id] = view.view.$el; $dataSidebar.append(view.view.el); }, this); - var pager = new recline.View.Pager({ + this.pager = new recline.View.Pager({ model: this.model.queryState }); - this.el.find('.recline-results-info').after(pager.el); + this.$el.find('.recline-results-info').after(this.pager.el); - var queryEditor = new recline.View.QueryEditor({ + this.queryEditor = new recline.View.QueryEditor({ model: this.model.queryState }); - this.el.find('.query-editor-here').append(queryEditor.el); + this.$el.find('.query-editor-here').append(this.queryEditor.el); }, + remove: function () { + _.each(this.pageViews, function (view) { + view.view.remove(); + }); + _.each(this.sidebarViews, function (view) { + view.view.remove(); + }); + this.pager.remove(); + this.queryEditor.remove(); + Backbone.View.prototype.remove.apply(this, arguments); + }, + // hide the sidebar if empty _showHideSidebar: function() { - var $dataSidebar = this.el.find('.data-view-sidebar'); + var $dataSidebar = this.$el.find('.data-view-sidebar'); var visibleChildren = $dataSidebar.children().filter(function() { return $(this).css("display") != "none"; }).length; @@ -3421,19 +3433,19 @@ my.MultiView = Backbone.View.extend({ }, updateNav: function(pageName) { - this.el.find('.navigation a').removeClass('active'); - var $el = this.el.find('.navigation a[data-view="' + pageName + '"]'); + this.$el.find('.navigation a').removeClass('active'); + var $el = this.$el.find('.navigation a[data-view="' + pageName + '"]'); $el.addClass('active'); // add/remove sidebars and hide inactive views _.each(this.pageViews, function(view, idx) { if (view.id === pageName) { - view.view.el.show(); + view.view.$el.show(); if (view.view.elSidebar) { view.view.elSidebar.show(); } } else { - view.view.el.hide(); + view.view.$el.hide(); if (view.view.elSidebar) { view.view.elSidebar.hide(); } @@ -3502,7 +3514,7 @@ my.MultiView = Backbone.View.extend({ _bindStateChanges: function() { var self = this; // finally ensure we update our state object when state of sub-object changes so that state is always up to date - this.model.queryState.bind('change', function() { + this.listenTo(this.model.queryState, 'change', function() { self.state.set({query: self.model.queryState.toJSON()}); }); _.each(this.pageViews, function(pageView) { @@ -3510,7 +3522,7 @@ my.MultiView = Backbone.View.extend({ var update = {}; update['view-' + pageView.id] = pageView.view.state.toJSON(); self.state.set(update); - pageView.view.state.bind('change', function() { + self.listenTo(pageView.view.state, 'change', function() { var update = {}; update['view-' + pageView.id] = pageView.view.state.toJSON(); // had problems where change not being triggered for e.g. grid view so let's do it explicitly @@ -3524,7 +3536,7 @@ my.MultiView = Backbone.View.extend({ _bindFlashNotifications: function() { var self = this; _.each(this.pageViews, function(pageView) { - pageView.view.bind('recline:flash', function(flash) { + self.listenTo(pageView.view, 'recline:flash', function(flash) { self.notify(flash); }); }); @@ -3650,7 +3662,7 @@ my.parseQueryString = function(q) { // Parse the query string out of the URL hash my.parseHashQueryString = function() { - q = my.parseHashUrl(window.location.hash).query; + var q = my.parseHashUrl(window.location.hash).query; return my.parseQueryString(q); }; @@ -3690,6 +3702,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; // ## SlickGrid Dataset View // // Provides a tabular view on a Dataset, based on SlickGrid. @@ -3719,13 +3732,10 @@ this.recline.View = this.recline.View || {}; my.SlickGrid = Backbone.View.extend({ initialize: function(modelEtc) { var self = this; - this.el = $(this.el); - this.el.addClass('recline-slickgrid'); - _.bindAll(this, 'render'); - this.model.records.bind('add', this.render); - this.model.records.bind('reset', this.render); - this.model.records.bind('remove', this.render); - this.model.records.bind('change', this.onRecordChanged, this); + this.$el.addClass('recline-slickgrid'); + _.bindAll(this, 'render', 'onRecordChanged'); + this.listenTo(this.model.records, 'add remove reset', this.render); + this.listenTo(this.model.records, 'change', this.onRecordChanged); var state = _.extend({ hiddenColumns: [], @@ -3739,6 +3749,8 @@ my.SlickGrid = Backbone.View.extend({ ); this.state = new recline.Model.ObjectState(state); + + this._slickHandler = new Slick.EventHandler(); }, events: { @@ -3804,7 +3816,7 @@ my.SlickGrid = Backbone.View.extend({ }); // Restrict the visible columns - var visibleColumns = columns.filter(function(column) { + var visibleColumns = _.filter(columns, function(column) { return _.indexOf(self.state.get('hiddenColumns'), column.id) === -1; }); @@ -3850,7 +3862,7 @@ my.SlickGrid = Backbone.View.extend({ this.getItem = function(index) {return rows[index];}; this.getItemMetadata = function(index) {return {};}; this.getModel = function(index) {return models[index];}; - this.getModelRow = function(m) {return models.indexOf(m);}; + this.getModelRow = function(m) {return _.indexOf(models, m);}; this.updateItem = function(m,i) { rows[i] = toRow(m); models[i] = m; @@ -3873,7 +3885,7 @@ my.SlickGrid = Backbone.View.extend({ this.grid.setSortColumn(column, sortAsc); } - this.grid.onSort.subscribe(function(e, args){ + this._slickHandler.subscribe(this.grid.onSort, function(e, args){ var order = (args.sortAsc) ? 'asc':'desc'; var sort = [{ field: args.sortCol.field, @@ -3882,7 +3894,7 @@ my.SlickGrid = Backbone.View.extend({ self.model.query({sort: sort}); }); - this.grid.onColumnsReordered.subscribe(function(e, args){ + this._slickHandler.subscribe(this.grid.onColumnsReordered, function(e, args){ self.state.set({columnsOrder: _.pluck(self.grid.getColumns(),'id')}); }); @@ -3898,7 +3910,7 @@ my.SlickGrid = Backbone.View.extend({ self.state.set({columnsWidth:columnsWidth}); }); - this.grid.onCellChange.subscribe(function (e, args) { + this._slickHandler.subscribe(this.grid.onCellChange, function (e, args) { // We need to change the model associated value // var grid = args.grid; @@ -3921,7 +3933,12 @@ my.SlickGrid = Backbone.View.extend({ } return this; - }, + }, + + remove: function () { + this._slickHandler.unsubscribeAll(); + Backbone.View.prototype.remove.apply(this, arguments); + }, show: function() { // If the div is hidden, SlickGrid will calculate wrongly some @@ -4067,6 +4084,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; // turn off unnecessary logging from VMM Timeline if (typeof VMM !== 'undefined') { VMM.debug = false; @@ -4090,13 +4108,12 @@ my.Timeline = Backbone.View.extend({ initialize: function(options) { var self = this; - this.el = $(this.el); this.timeline = new VMM.Timeline(); this._timelineIsInitialized = false; - this.model.fields.bind('reset', function() { + this.listenTo(this.model.fields, 'reset', function() { self._setupTemporalField(); }); - this.model.records.bind('all', function() { + this.listenTo(this.model.records, 'all', function() { self.reloadData(); }); var stateData = _.extend({ @@ -4113,7 +4130,7 @@ my.Timeline = Backbone.View.extend({ render: function() { var tmplData = {}; var htmls = Mustache.render(this.template, tmplData); - this.el.html(htmls); + this.$el.html(htmls); // can only call _initTimeline once view in DOM as Timeline uses $ // internally to look up element if ($(this.elementId).length > 0) { @@ -4129,7 +4146,7 @@ my.Timeline = Backbone.View.extend({ }, _initTimeline: function() { - var $timeline = this.el.find(this.elementId); + var $timeline = this.$el.find(this.elementId); var data = this._timelineJSON(); this.timeline.init(data, this.elementId, this.state.get("timelineJSOptions")); this._timelineIsInitialized = true @@ -4197,14 +4214,14 @@ my.Timeline = Backbone.View.extend({ if (!date) { return null; } - var out = date.trim(); + var out = $.trim(date); out = out.replace(/(\d)th/g, '$1'); out = out.replace(/(\d)st/g, '$1'); - out = out.trim() ? moment(out) : null; - if (out.toDate() == 'Invalid Date') { - return null; - } else { + out = $.trim(out) ? moment(out) : null; + if (out && out.isValid()) { return out.toDate(); + } else { + return null; } }, @@ -4234,6 +4251,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; // ## FacetViewer // @@ -4271,9 +4289,8 @@ my.FacetViewer = Backbone.View.extend({ }, initialize: function(model) { _.bindAll(this, 'render'); - this.el = $(this.el); - this.model.facets.bind('all', this.render); - this.model.fields.bind('all', this.render); + this.listenTo(this.model.facets, 'all', this.render); + this.listenTo(this.model.fields, 'all', this.render); this.render(); }, render: function() { @@ -4290,17 +4307,17 @@ my.FacetViewer = Backbone.View.extend({ return facet; }); var templated = Mustache.render(this.template, tmplData); - this.el.html(templated); + this.$el.html(templated); // are there actually any facets to show? if (this.model.facets.length > 0) { - this.el.show(); + this.$el.show(); } else { - this.el.hide(); + this.$el.hide(); } }, onHide: function(e) { e.preventDefault(); - this.el.hide(); + this.$el.hide(); }, onFacetFilter: function(e) { e.preventDefault(); @@ -4338,7 +4355,8 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { - + "use strict"; + my.Fields = Backbone.View.extend({ className: 'recline-fields-view', template: ' \ @@ -4377,13 +4395,12 @@ my.Fields = Backbone.View.extend({ 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) { + this.listenTo(this.model.fields, 'reset', function(action) { self.model.fields.each(function(field) { field.facets.unbind('all', self.render); field.facets.bind('all', self.render); @@ -4392,7 +4409,7 @@ my.Fields = Backbone.View.extend({ self.model.getFieldsSummary(); self.render(); }); - this.el.find('.collapse').collapse(); + this.$el.find('.collapse').collapse(); this.render(); }, render: function() { @@ -4406,7 +4423,7 @@ my.Fields = Backbone.View.extend({ tmplData.fields.push(out); }); var templated = Mustache.render(this.template, tmplData); - this.el.html(templated); + this.$el.html(templated); } }); @@ -4417,6 +4434,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; my.FilterEditor = Backbone.View.extend({ className: 'recline-filter-editor well', @@ -4501,11 +4519,9 @@ my.FilterEditor = Backbone.View.extend({ 'submit form.js-add': 'onAddFilter' }, initialize: function() { - this.el = $(this.el); _.bindAll(this, 'render'); - this.model.fields.bind('all', this.render); - this.model.queryState.bind('change', this.render); - this.model.queryState.bind('change:filters:new-blank', this.render); + this.listenTo(this.model.fields, 'all', this.render); + this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render); this.render(); }, render: function() { @@ -4521,13 +4537,13 @@ my.FilterEditor = Backbone.View.extend({ return Mustache.render(self.filterTemplates[this.type], this); }; var out = Mustache.render(this.template, tmplData); - this.el.html(out); + this.$el.html(out); }, onAddFilterShow: function(e) { e.preventDefault(); var $target = $(e.target); $target.hide(); - this.el.find('form.js-add').show(); + this.$el.find('form.js-add').show(); }, onAddFilter: function(e) { e.preventDefault(); @@ -4587,6 +4603,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; my.Pager = Backbone.View.extend({ className: 'recline-pager', @@ -4607,14 +4624,13 @@ my.Pager = Backbone.View.extend({ initialize: function() { _.bindAll(this, 'render'); - this.el = $(this.el); - this.model.bind('change', this.render); + this.listenTo(this.model, 'change', this.render); this.render(); }, onFormSubmit: function(e) { e.preventDefault(); - var newFrom = parseInt(this.el.find('input[name="from"]').val()); - var newSize = parseInt(this.el.find('input[name="to"]').val()) - newFrom; + var newFrom = parseInt(this.$el.find('input[name="from"]').val()); + var newSize = parseInt(this.$el.find('input[name="to"]').val()) - newFrom; newFrom = Math.max(newFrom, 0); newSize = Math.max(newSize, 1); this.model.set({size: newSize, from: newFrom}); @@ -4635,7 +4651,7 @@ my.Pager = Backbone.View.extend({ var tmplData = this.model.toJSON(); tmplData.to = this.model.get('from') + this.model.get('size'); var templated = Mustache.render(this.template, tmplData); - this.el.html(templated); + this.$el.html(templated); } }); @@ -4647,6 +4663,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; my.QueryEditor = Backbone.View.extend({ className: 'recline-query-editor', @@ -4666,19 +4683,18 @@ my.QueryEditor = Backbone.View.extend({ initialize: function() { _.bindAll(this, 'render'); - this.el = $(this.el); - this.model.bind('change', this.render); + this.listenTo(this.model, 'change', this.render); this.render(); }, onFormSubmit: function(e) { e.preventDefault(); - var query = this.el.find('.text-query input').val(); + var query = this.$el.find('.text-query input').val(); this.model.set({q: query}); }, render: function() { var tmplData = this.model.toJSON(); var templated = Mustache.render(this.template, tmplData); - this.el.html(templated); + this.$el.html(templated); } }); @@ -4690,6 +4706,7 @@ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { + "use strict"; my.ValueFilter = Backbone.View.extend({ className: 'recline-filter-editor well', @@ -4736,11 +4753,9 @@ my.ValueFilter = Backbone.View.extend({ 'submit form.js-add': 'onAddFilter' }, initialize: function() { - this.el = $(this.el); _.bindAll(this, 'render'); - this.model.fields.bind('all', this.render); - this.model.queryState.bind('change', this.render); - this.model.queryState.bind('change:filters:new-blank', this.render); + this.listenTo(this.model.fields, 'all', this.render); + this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render); this.render(); }, render: function() { @@ -4756,7 +4771,7 @@ my.ValueFilter = Backbone.View.extend({ return Mustache.render(self.filterTemplates.term, this); }; var out = Mustache.render(this.template, tmplData); - this.el.html(out); + this.$el.html(out); }, updateFilter: function(input) { var self = this; @@ -4770,7 +4785,7 @@ my.ValueFilter = Backbone.View.extend({ e.preventDefault(); var $target = $(e.target); $target.hide(); - this.el.find('form.js-add').show(); + this.$el.find('form.js-add').show(); }, onAddFilter: function(e) { e.preventDefault();