diff --git a/demo/index.html b/demo/index.html
index 48a0d5e0..c941752a 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -28,8 +28,6 @@
-
-
diff --git a/src/view-data-explorer.js b/src/view-data-explorer.js
deleted file mode 100644
index 7e3c2258..00000000
--- a/src/view-data-explorer.js
+++ /dev/null
@@ -1,171 +0,0 @@
-this.recline = this.recline || {};
-this.recline.View = this.recline.View || {};
-
-// Views module following classic module pattern
-(function($, my) {
-
-// The primary view for the entire application.
-//
-// It should be initialized with a recline.Model.Dataset object and an existing
-// dom element to attach to (the existing DOM element is important for
-// rendering of FlotGraph subview).
-//
-// To pass in configuration options use the config key in initialization hash
-// e.g.
-//
-// var explorer = new DataExplorer({
-// config: {...}
-// })
-//
-// Config options:
-//
-// * displayCount: how many documents to display initially (default: 10)
-// * readOnly: true/false (default: false) value indicating whether to
-// operate in read-only mode (hiding all editing options).
-//
-// All other views as contained in this one.
-my.DataExplorer = Backbone.View.extend({
- template: ' \
-
\
-
\
- \
- \
-
\
-
\
-
\
-
\
- ',
-
- events: {
- 'submit form.display-count': 'onDisplayCountUpdate'
- },
-
- initialize: function(options) {
- var self = this;
- this.el = $(this.el);
- this.config = _.extend({
- displayCount: 50
- , readOnly: false
- },
- options.config);
- if (this.config.readOnly) {
- this.setReadOnly();
- }
- // Hash of 'page' views (i.e. those for whole page) keyed by page name
- this.pageViews = {
- grid: new my.DataTable({
- model: this.model,
- config: this.config
- })
- , graph: new my.FlotGraph({
- model: this.model
- })
- };
- // this must be called after pageViews are created
- this.render();
-
- this.router = new Backbone.Router();
- this.setupRouting();
-
- // retrieve basic data like headers etc
- // note this.model and dataset returned are the same
- this.model.fetch()
- .done(function(dataset) {
- self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
- self.query();
- })
- .fail(function(error) {
- my.notify(error.message, {category: 'error', persist: true});
- });
- },
-
- query: function() {
- this.config.displayCount = parseInt(this.el.find('input[name="displayCount"]').val());
- var queryObj = {
- size: this.config.displayCount
- };
- my.notify('Loading data', {loader: true});
- this.model.query(queryObj)
- .done(function() {
- my.clearNotifications();
- my.notify('Data loaded', {category: 'success'});
- })
- .fail(function(error) {
- my.clearNotifications();
- my.notify(error.message, {category: 'error', persist: true});
- });
- },
-
- onDisplayCountUpdate: function(e) {
- e.preventDefault();
- this.query();
- },
-
- setReadOnly: function() {
- this.el.addClass('read-only');
- },
-
- render: function() {
- var tmplData = this.model.toTemplateJSON();
- tmplData.displayCount = this.config.displayCount;
- var template = $.mustache(this.template, tmplData);
- $(this.el).html(template);
- var $dataViewContainer = this.el.find('.data-view-container');
- _.each(this.pageViews, function(view, pageName) {
- $dataViewContainer.append(view.el)
- });
- },
-
- setupRouting: function() {
- var self = this;
- this.router.route('', 'grid', function() {
- self.updateNav('grid');
- });
- this.router.route(/grid(\?.*)?/, 'view', function(queryString) {
- self.updateNav('grid', queryString);
- });
- this.router.route(/graph(\?.*)?/, 'graph', function(queryString) {
- self.updateNav('graph', queryString);
- // we have to call here due to fact plot may not have been able to draw
- // if it was hidden until now - see comments in FlotGraph.redraw
- qsParsed = parseQueryString(queryString);
- if ('graph' in qsParsed) {
- var chartConfig = JSON.parse(qsParsed['graph']);
- _.extend(self.pageViews['graph'].chartConfig, chartConfig);
- }
- self.pageViews['graph'].redraw();
- });
- },
-
- updateNav: function(pageName, queryString) {
- this.el.find('.navigation li').removeClass('active');
- var $el = this.el.find('.navigation li a[href=#' + pageName + ']');
- $el.parent().addClass('active');
- // show the specific page
- _.each(this.pageViews, function(view, pageViewName) {
- if (pageViewName === pageName) {
- view.el.show();
- } else {
- view.el.hide();
- }
- });
- }
-});
-
-})(jQuery, recline.View);
-
-
diff --git a/src/view-data-table.js b/src/view-data-table.js
deleted file mode 100644
index ff4a8c26..00000000
--- a/src/view-data-table.js
+++ /dev/null
@@ -1,298 +0,0 @@
-this.recline = this.recline || {};
-this.recline.View = this.recline.View || {};
-
-// Views module following classic module pattern
-(function($, my) {
-
-// DataTable provides a tabular view on a Dataset.
-//
-// Initialize it with a recline.Dataset object.
-my.DataTable = Backbone.View.extend({
- tagName: "div",
- className: "data-table-container",
-
- initialize: function() {
- var self = this;
- this.el = $(this.el);
- _.bindAll(this, 'render');
- this.model.currentDocuments.bind('add', this.render);
- this.model.currentDocuments.bind('reset', this.render);
- this.model.currentDocuments.bind('remove', this.render);
- this.state = {};
- this.hiddenHeaders = [];
- },
-
- events: {
- 'click .column-header-menu': 'onColumnHeaderClick'
- , 'click .row-header-menu': 'onRowHeaderClick'
- , 'click .root-header-menu': 'onRootHeaderClick'
- , 'click .data-table-menu li a': 'onMenuClick'
- },
-
- // TODO: delete or re-enable (currently this code is not used from anywhere except deprecated or disabled methods (see above)).
- // showDialog: function(template, data) {
- // if (!data) data = {};
- // util.show('dialog');
- // util.render(template, 'dialog-content', data);
- // util.observeExit($('.dialog-content'), function() {
- // util.hide('dialog');
- // })
- // $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
- // },
-
-
- // ======================================================
- // Column and row menus
-
- onColumnHeaderClick: function(e) {
- this.state.currentColumn = $(e.target).siblings().text();
- util.position('data-table-menu', e);
- util.render('columnActions', 'data-table-menu');
- },
-
- onRowHeaderClick: function(e) {
- this.state.currentRow = $(e.target).parents('tr:first').attr('data-id');
- util.position('data-table-menu', e);
- util.render('rowActions', 'data-table-menu');
- },
-
- onRootHeaderClick: function(e) {
- util.position('data-table-menu', e);
- util.render('rootActions', 'data-table-menu', {'columns': this.hiddenHeaders});
- },
-
- onMenuClick: function(e) {
- var self = this;
- e.preventDefault();
- var actions = {
- bulkEdit: function() { self.showTransformColumnDialog('bulkEdit', {name: self.state.currentColumn}) },
- transform: function() { self.showTransformDialog('transform') },
- sortAsc: function() { self.setColumnSort('asc') },
- sortDesc: function() { self.setColumnSort('desc') },
- hideColumn: function() { self.hideColumn() },
- showColumn: function() { self.showColumn(e) },
- // TODO: Delete or re-implement ...
- csv: function() { window.location.href = app.csvUrl },
- json: function() { window.location.href = "_rewrite/api/json" },
- urlImport: function() { showDialog('urlImport') },
- pasteImport: function() { showDialog('pasteImport') },
- uploadImport: function() { showDialog('uploadImport') },
- // END TODO
- deleteColumn: function() {
- var msg = "Are you sure? This will delete '" + self.state.currentColumn + "' from all documents.";
- // TODO:
- alert('This function needs to be re-implemented');
- return;
- if (confirm(msg)) costco.deleteColumn(self.state.currentColumn);
- },
- deleteRow: function() {
- var doc = _.find(self.model.currentDocuments.models, function(doc) {
- // important this is == as the currentRow will be string (as comes
- // from DOM) while id may be int
- return doc.id == self.state.currentRow
- });
- doc.destroy().then(function() {
- self.model.currentDocuments.remove(doc);
- my.notify("Row deleted successfully");
- })
- .fail(function(err) {
- my.notify("Errorz! " + err)
- })
- }
- }
- util.hide('data-table-menu');
- actions[$(e.target).attr('data-action')]();
- },
-
- showTransformColumnDialog: function() {
- var $el = $('.dialog-content');
- util.show('dialog');
- var view = new my.ColumnTransform({
- model: this.model
- });
- view.state = this.state;
- view.render();
- $el.empty();
- $el.append(view.el);
- util.observeExit($el, function() {
- util.hide('dialog');
- })
- $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
- },
-
- showTransformDialog: function() {
- var $el = $('.dialog-content');
- util.show('dialog');
- var view = new recline.View.DataTransform({
- });
- view.render();
- $el.empty();
- $el.append(view.el);
- util.observeExit($el, function() {
- util.hide('dialog');
- })
- $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
- },
-
- setColumnSort: function(order) {
- var query = _.extend(this.model.queryState, {sort: [[this.state.currentColumn, order]]});
- this.model.query(query);
- },
-
- hideColumn: function() {
- this.hiddenHeaders.push(this.state.currentColumn);
- this.render();
- },
-
- showColumn: function(e) {
- this.hiddenHeaders = _.without(this.hiddenHeaders, $(e.target).data('column'));
- this.render();
- },
-
- // ======================================================
- // Core Templating
- template: ' \
- \
- \
- \
- \
- \
- {{#notEmpty}} \
- | \
- \
- \
- \
- \
- | \
- {{/notEmpty}} \
- {{#headers}} \
- \
- \
- \
- | \
- {{/headers}} \
-
\
- \
- \
-
\
- ',
-
- toTemplateJSON: function() {
- var modelData = this.model.toJSON()
- modelData.notEmpty = ( this.headers.length > 0 )
- modelData.headers = this.headers;
- return modelData;
- },
- render: function() {
- var self = this;
- this.headers = _.filter(this.model.get('headers'), function(header) {
- return _.indexOf(self.hiddenHeaders, header) == -1;
- });
- var htmls = $.mustache(this.template, this.toTemplateJSON());
- this.el.html(htmls);
- this.model.currentDocuments.forEach(function(doc) {
- var tr = $('
');
- self.el.find('tbody').append(tr);
- var newView = new my.DataTableRow({
- model: doc,
- el: tr,
- headers: self.headers,
- });
- newView.render();
- });
- this.el.toggleClass('no-hidden', (self.hiddenHeaders.length == 0));
- return this;
- }
-});
-
-// DataTableRow View for rendering an individual document.
-//
-// Since we want this to update in place it is up to creator to provider the element to attach to.
-// In addition you must pass in a headers in the constructor options. This should be list of headers for the DataTable.
-my.DataTableRow = Backbone.View.extend({
- initialize: function(options) {
- _.bindAll(this, 'render');
- this._headers = options.headers;
- this.el = $(this.el);
- this.model.bind('change', this.render);
- },
-
- template: ' \
- | \
- {{#cells}} \
- \
- \
- | \
- {{/cells}} \
- ',
- events: {
- 'click .data-table-cell-edit': 'onEditClick',
- // cell editor
- 'click .data-table-cell-editor .okButton': 'onEditorOK',
- 'click .data-table-cell-editor .cancelButton': 'onEditorCancel'
- },
-
- toTemplateJSON: function() {
- var doc = this.model;
- var cellData = _.map(this._headers, function(header) {
- return {header: header, value: doc.get(header)}
- })
- return { id: this.id, cells: cellData }
- },
-
- render: function() {
- this.el.attr('data-id', this.model.id);
- var html = $.mustache(this.template, this.toTemplateJSON());
- $(this.el).html(html);
- return this;
- },
-
- // ======================================================
- // Cell Editor
-
- onEditClick: function(e) {
- 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");
- }
- $(e.target).addClass("hidden");
- var cell = $(e.target).siblings('.data-table-cell-value');
- cell.data("previousContents", cell.text());
- util.render('cellEditor', cell, {value: cell.text()});
- },
-
- onEditorOK: function(e) {
- var cell = $(e.target);
- var rowId = cell.parents('tr').attr('data-id');
- var header = cell.parents('td').attr('data-header');
- var newValue = cell.parents('.data-table-cell-editor').find('.data-table-cell-editor-editor').val();
- var newData = {};
- newData[header] = newValue;
- this.model.set(newData);
- my.notify("Updating row...", {loader: true});
- this.model.save().then(function(response) {
- my.notify("Row updated successfully", {category: 'success'});
- })
- .fail(function() {
- my.notify('Error saving row', {
- category: 'error',
- persist: true
- });
- });
- },
-
- onEditorCancel: function(e) {
- var cell = $(e.target).parents('.data-table-cell-value');
- cell.html(cell.data('previousContents')).siblings('.data-table-cell-edit').removeClass("hidden");
- }
-});
-
-})(jQuery, recline.View);
-
-
diff --git a/src/view.js b/src/view.js
index 5e79a62a..166e2a05 100644
--- a/src/view.js
+++ b/src/view.js
@@ -3,6 +3,464 @@ this.recline.View = this.recline.View || {};
// Views module following classic module pattern
(function($, my) {
+// ## DataExplorer
+//
+// The primary view for the entire application.
+//
+// It should be initialized with a recline.Model.Dataset object and an existing
+// dom element to attach to (the existing DOM element is important for
+// rendering of FlotGraph subview).
+//
+// To pass in configuration options use the config key in initialization hash
+// e.g.
+//
+// var explorer = new DataExplorer({
+// config: {...}
+// })
+//
+// Config options:
+//
+// * displayCount: how many documents to display initially (default: 10)
+// * readOnly: true/false (default: false) value indicating whether to
+// operate in read-only mode (hiding all editing options).
+//
+// All other views as contained in this one.
+my.DataExplorer = Backbone.View.extend({
+ template: ' \
+ \
+
\
+ \
+ \
+
\
+
\
+
\
+
\
+ ',
+
+ events: {
+ 'submit form.display-count': 'onDisplayCountUpdate'
+ },
+
+ initialize: function(options) {
+ var self = this;
+ this.el = $(this.el);
+ this.config = _.extend({
+ displayCount: 50
+ , readOnly: false
+ },
+ options.config);
+ if (this.config.readOnly) {
+ this.setReadOnly();
+ }
+ // Hash of 'page' views (i.e. those for whole page) keyed by page name
+ this.pageViews = {
+ grid: new my.DataTable({
+ model: this.model,
+ config: this.config
+ })
+ , graph: new my.FlotGraph({
+ model: this.model
+ })
+ };
+ // this must be called after pageViews are created
+ this.render();
+
+ this.router = new Backbone.Router();
+ this.setupRouting();
+
+ // retrieve basic data like headers etc
+ // note this.model and dataset returned are the same
+ this.model.fetch()
+ .done(function(dataset) {
+ self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
+ self.query();
+ })
+ .fail(function(error) {
+ my.notify(error.message, {category: 'error', persist: true});
+ });
+ },
+
+ query: function() {
+ this.config.displayCount = parseInt(this.el.find('input[name="displayCount"]').val());
+ var queryObj = {
+ size: this.config.displayCount
+ };
+ my.notify('Loading data', {loader: true});
+ this.model.query(queryObj)
+ .done(function() {
+ my.clearNotifications();
+ my.notify('Data loaded', {category: 'success'});
+ })
+ .fail(function(error) {
+ my.clearNotifications();
+ my.notify(error.message, {category: 'error', persist: true});
+ });
+ },
+
+ onDisplayCountUpdate: function(e) {
+ e.preventDefault();
+ this.query();
+ },
+
+ setReadOnly: function() {
+ this.el.addClass('read-only');
+ },
+
+ render: function() {
+ var tmplData = this.model.toTemplateJSON();
+ tmplData.displayCount = this.config.displayCount;
+ var template = $.mustache(this.template, tmplData);
+ $(this.el).html(template);
+ var $dataViewContainer = this.el.find('.data-view-container');
+ _.each(this.pageViews, function(view, pageName) {
+ $dataViewContainer.append(view.el)
+ });
+ },
+
+ setupRouting: function() {
+ var self = this;
+ this.router.route('', 'grid', function() {
+ self.updateNav('grid');
+ });
+ this.router.route(/grid(\?.*)?/, 'view', function(queryString) {
+ self.updateNav('grid', queryString);
+ });
+ this.router.route(/graph(\?.*)?/, 'graph', function(queryString) {
+ self.updateNav('graph', queryString);
+ // we have to call here due to fact plot may not have been able to draw
+ // if it was hidden until now - see comments in FlotGraph.redraw
+ qsParsed = parseQueryString(queryString);
+ if ('graph' in qsParsed) {
+ var chartConfig = JSON.parse(qsParsed['graph']);
+ _.extend(self.pageViews['graph'].chartConfig, chartConfig);
+ }
+ self.pageViews['graph'].redraw();
+ });
+ },
+
+ updateNav: function(pageName, queryString) {
+ this.el.find('.navigation li').removeClass('active');
+ var $el = this.el.find('.navigation li a[href=#' + pageName + ']');
+ $el.parent().addClass('active');
+ // show the specific page
+ _.each(this.pageViews, function(view, pageViewName) {
+ if (pageViewName === pageName) {
+ view.el.show();
+ } else {
+ view.el.hide();
+ }
+ });
+ }
+});
+
+// ## DataTable
+//
+// Provides a tabular view on a Dataset.
+//
+// Initialize it with a recline.Dataset object.
+my.DataTable = Backbone.View.extend({
+ tagName: "div",
+ className: "data-table-container",
+
+ initialize: function() {
+ var self = this;
+ this.el = $(this.el);
+ _.bindAll(this, 'render');
+ this.model.currentDocuments.bind('add', this.render);
+ this.model.currentDocuments.bind('reset', this.render);
+ this.model.currentDocuments.bind('remove', this.render);
+ this.state = {};
+ this.hiddenHeaders = [];
+ },
+
+ events: {
+ 'click .column-header-menu': 'onColumnHeaderClick'
+ , 'click .row-header-menu': 'onRowHeaderClick'
+ , 'click .root-header-menu': 'onRootHeaderClick'
+ , 'click .data-table-menu li a': 'onMenuClick'
+ },
+
+ // TODO: delete or re-enable (currently this code is not used from anywhere except deprecated or disabled methods (see above)).
+ // showDialog: function(template, data) {
+ // if (!data) data = {};
+ // util.show('dialog');
+ // util.render(template, 'dialog-content', data);
+ // util.observeExit($('.dialog-content'), function() {
+ // util.hide('dialog');
+ // })
+ // $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
+ // },
+
+
+ // ======================================================
+ // Column and row menus
+
+ onColumnHeaderClick: function(e) {
+ this.state.currentColumn = $(e.target).siblings().text();
+ util.position('data-table-menu', e);
+ util.render('columnActions', 'data-table-menu');
+ },
+
+ onRowHeaderClick: function(e) {
+ this.state.currentRow = $(e.target).parents('tr:first').attr('data-id');
+ util.position('data-table-menu', e);
+ util.render('rowActions', 'data-table-menu');
+ },
+
+ onRootHeaderClick: function(e) {
+ util.position('data-table-menu', e);
+ util.render('rootActions', 'data-table-menu', {'columns': this.hiddenHeaders});
+ },
+
+ onMenuClick: function(e) {
+ var self = this;
+ e.preventDefault();
+ var actions = {
+ bulkEdit: function() { self.showTransformColumnDialog('bulkEdit', {name: self.state.currentColumn}) },
+ transform: function() { self.showTransformDialog('transform') },
+ sortAsc: function() { self.setColumnSort('asc') },
+ sortDesc: function() { self.setColumnSort('desc') },
+ hideColumn: function() { self.hideColumn() },
+ showColumn: function() { self.showColumn(e) },
+ // TODO: Delete or re-implement ...
+ csv: function() { window.location.href = app.csvUrl },
+ json: function() { window.location.href = "_rewrite/api/json" },
+ urlImport: function() { showDialog('urlImport') },
+ pasteImport: function() { showDialog('pasteImport') },
+ uploadImport: function() { showDialog('uploadImport') },
+ // END TODO
+ deleteColumn: function() {
+ var msg = "Are you sure? This will delete '" + self.state.currentColumn + "' from all documents.";
+ // TODO:
+ alert('This function needs to be re-implemented');
+ return;
+ if (confirm(msg)) costco.deleteColumn(self.state.currentColumn);
+ },
+ deleteRow: function() {
+ var doc = _.find(self.model.currentDocuments.models, function(doc) {
+ // important this is == as the currentRow will be string (as comes
+ // from DOM) while id may be int
+ return doc.id == self.state.currentRow
+ });
+ doc.destroy().then(function() {
+ self.model.currentDocuments.remove(doc);
+ my.notify("Row deleted successfully");
+ })
+ .fail(function(err) {
+ my.notify("Errorz! " + err)
+ })
+ }
+ }
+ util.hide('data-table-menu');
+ actions[$(e.target).attr('data-action')]();
+ },
+
+ showTransformColumnDialog: function() {
+ var $el = $('.dialog-content');
+ util.show('dialog');
+ var view = new my.ColumnTransform({
+ model: this.model
+ });
+ view.state = this.state;
+ view.render();
+ $el.empty();
+ $el.append(view.el);
+ util.observeExit($el, function() {
+ util.hide('dialog');
+ })
+ $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
+ },
+
+ showTransformDialog: function() {
+ var $el = $('.dialog-content');
+ util.show('dialog');
+ var view = new recline.View.DataTransform({
+ });
+ view.render();
+ $el.empty();
+ $el.append(view.el);
+ util.observeExit($el, function() {
+ util.hide('dialog');
+ })
+ $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
+ },
+
+ setColumnSort: function(order) {
+ var query = _.extend(this.model.queryState, {sort: [[this.state.currentColumn, order]]});
+ this.model.query(query);
+ },
+
+ hideColumn: function() {
+ this.hiddenHeaders.push(this.state.currentColumn);
+ this.render();
+ },
+
+ showColumn: function(e) {
+ this.hiddenHeaders = _.without(this.hiddenHeaders, $(e.target).data('column'));
+ this.render();
+ },
+
+ // ======================================================
+ // #### Templating
+ template: ' \
+ \
+ \
+ \
+ \
+ \
+ {{#notEmpty}} \
+ | \
+ \
+ \
+ \
+ \
+ | \
+ {{/notEmpty}} \
+ {{#headers}} \
+ \
+ \
+ \
+ | \
+ {{/headers}} \
+
\
+ \
+ \
+
\
+ ',
+
+ toTemplateJSON: function() {
+ var modelData = this.model.toJSON()
+ modelData.notEmpty = ( this.headers.length > 0 )
+ modelData.headers = this.headers;
+ return modelData;
+ },
+ render: function() {
+ var self = this;
+ this.headers = _.filter(this.model.get('headers'), function(header) {
+ return _.indexOf(self.hiddenHeaders, header) == -1;
+ });
+ var htmls = $.mustache(this.template, this.toTemplateJSON());
+ this.el.html(htmls);
+ this.model.currentDocuments.forEach(function(doc) {
+ var tr = $('
');
+ self.el.find('tbody').append(tr);
+ var newView = new my.DataTableRow({
+ model: doc,
+ el: tr,
+ headers: self.headers,
+ });
+ newView.render();
+ });
+ this.el.toggleClass('no-hidden', (self.hiddenHeaders.length == 0));
+ return this;
+ }
+});
+
+// ## DataTableRow View for rendering an individual document.
+//
+// Since we want this to update in place it is up to creator to provider the element to attach to.
+// In addition you must pass in a headers in the constructor options. This should be list of headers for the DataTable.
+my.DataTableRow = Backbone.View.extend({
+ initialize: function(options) {
+ _.bindAll(this, 'render');
+ this._headers = options.headers;
+ this.el = $(this.el);
+ this.model.bind('change', this.render);
+ },
+
+ template: ' \
+ | \
+ {{#cells}} \
+ \
+ \
+ | \
+ {{/cells}} \
+ ',
+ events: {
+ 'click .data-table-cell-edit': 'onEditClick',
+ // cell editor
+ 'click .data-table-cell-editor .okButton': 'onEditorOK',
+ 'click .data-table-cell-editor .cancelButton': 'onEditorCancel'
+ },
+
+ toTemplateJSON: function() {
+ var doc = this.model;
+ var cellData = _.map(this._headers, function(header) {
+ return {header: header, value: doc.get(header)}
+ })
+ return { id: this.id, cells: cellData }
+ },
+
+ render: function() {
+ this.el.attr('data-id', this.model.id);
+ var html = $.mustache(this.template, this.toTemplateJSON());
+ $(this.el).html(html);
+ return this;
+ },
+
+ // Cell Editor
+ // ===========
+
+ onEditClick: function(e) {
+ 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");
+ }
+ $(e.target).addClass("hidden");
+ var cell = $(e.target).siblings('.data-table-cell-value');
+ cell.data("previousContents", cell.text());
+ util.render('cellEditor', cell, {value: cell.text()});
+ },
+
+ onEditorOK: function(e) {
+ var cell = $(e.target);
+ var rowId = cell.parents('tr').attr('data-id');
+ var header = cell.parents('td').attr('data-header');
+ var newValue = cell.parents('.data-table-cell-editor').find('.data-table-cell-editor-editor').val();
+ var newData = {};
+ newData[header] = newValue;
+ this.model.set(newData);
+ my.notify("Updating row...", {loader: true});
+ this.model.save().then(function(response) {
+ my.notify("Row updated successfully", {category: 'success'});
+ })
+ .fail(function() {
+ my.notify('Error saving row', {
+ category: 'error',
+ persist: true
+ });
+ });
+ },
+
+ onEditorCancel: function(e) {
+ var cell = $(e.target).parents('.data-table-cell-value');
+ cell.html(cell.data('previousContents')).siblings('.data-table-cell-edit').removeClass("hidden");
+ }
+});
+
+
+/* ========================================================== */
+// ## Miscellaneous Utilities
// Parse a URL query string (?xyz=abc...) into a dictionary.
function parseQueryString(q) {
diff --git a/test/index.html b/test/index.html
index 219e2b3d..400d740d 100644
--- a/test/index.html
+++ b/test/index.html
@@ -20,8 +20,6 @@
-
-