diff --git a/demo/index.html b/demo/index.html
index c941752a..fdbc8d2b 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -28,6 +28,7 @@
+
diff --git a/src/view-grid.js b/src/view-grid.js
new file mode 100644
index 00000000..80d9820b
--- /dev/null
+++ b/src/view-grid.js
@@ -0,0 +1,325 @@
+this.recline = this.recline || {};
+this.recline.View = this.recline.View || {};
+
+(function($, my) {
+// ## DataTable
+//
+// Provides a tabular view on a Dataset.
+//
+// Initialize it with a recline.Dataset object.
+//
+// Additional options passed in second arguments. Options:
+//
+// * cellRenderer: function used to render individual cells. See DataTableRow for more.
+my.DataTable = Backbone.View.extend({
+ tagName: "div",
+ className: "data-table-container",
+
+ initialize: function(modelEtc, options) {
+ 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.hiddenFields = [];
+ this.options = options;
+ },
+
+ 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).closest('.column-header').attr('data-field');
+ 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.hiddenFields});
+ },
+
+ 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) {
+ this.model.query({
+ sort: [
+ [this.state.currentColumn, order]
+ ]
+ });
+ },
+
+ hideColumn: function() {
+ this.hiddenFields.push(this.state.currentColumn);
+ this.render();
+ },
+
+ showColumn: function(e) {
+ this.hiddenFields = _.without(this.hiddenFields, $(e.target).data('column'));
+ this.render();
+ },
+
+ // ======================================================
+ // #### Templating
+ template: ' \
+
\
+ \
+ \
+ \
+ \
+ {{#notEmpty}} \
+ | \
+ \
+ \
+ \
+ \
+ | \
+ {{/notEmpty}} \
+ {{#fields}} \
+ \
+ \
+ \
+ | \
+ {{/fields}} \
+
\
+ \
+ \
+
\
+ ',
+
+ toTemplateJSON: function() {
+ 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) { return field.toJSON() });
+ return modelData;
+ },
+ render: function() {
+ var self = this;
+ this.fields = this.model.fields.filter(function(field) {
+ return _.indexOf(self.hiddenFields, field.id) == -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,
+ fields: self.fields,
+ },
+ self.options
+ );
+ newView.render();
+ });
+ this.el.toggleClass('no-hidden', (self.hiddenFields.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 fields in the constructor options. This should be list of fields for the DataTable.
+//
+// Additional options can be passed in a second hash argument. Options:
+//
+// * cellRenderer: function to render cells. Signature: function(value,
+// field, doc) where value is the value of this cell, field is
+// corresponding field object and document is the document object. Note
+// that implementing functions can ignore arguments (e.g.
+// function(value) would be a valid cellRenderer function).
+my.DataTableRow = Backbone.View.extend({
+ initialize: function(initData, options) {
+ _.bindAll(this, 'render');
+ this._fields = initData.fields;
+ if (options && options.cellRenderer) {
+ this._cellRenderer = options.cellRenderer;
+ } else {
+ this._cellRenderer = function(value) {
+ return value;
+ }
+ }
+ this.el = $(this.el);
+ this.model.bind('change', this.render);
+ },
+
+ template: ' \
+ | \
+ {{#cells}} \
+ \
+ \
+ | \
+ {{/cells}} \
+ ',
+ events: {
+ 'click .data-table-cell-edit': 'onEditClick',
+ 'click .data-table-cell-editor .okButton': 'onEditorOK',
+ 'click .data-table-cell-editor .cancelButton': 'onEditorCancel'
+ },
+
+ toTemplateJSON: function() {
+ var self = this;
+ var doc = this.model;
+ var cellData = this._fields.map(function(field) {
+ return {
+ field: field.id,
+ value: self._cellRenderer(doc.get(field.id), field, doc)
+ }
+ })
+ 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 field = cell.parents('td').attr('data-field');
+ var newValue = cell.parents('.data-table-cell-editor').find('.data-table-cell-editor-editor').val();
+ var newData = {};
+ newData[field] = 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 31418cdc..bb35adc9 100644
--- a/src/view.js
+++ b/src/view.js
@@ -190,326 +190,6 @@ my.DataExplorer = Backbone.View.extend({
}
});
-// ## DataTable
-//
-// Provides a tabular view on a Dataset.
-//
-// Initialize it with a recline.Dataset object.
-//
-// Additional options passed in second arguments. Options:
-//
-// * cellRenderer: function used to render individual cells. See DataTableRow for more.
-my.DataTable = Backbone.View.extend({
- tagName: "div",
- className: "data-table-container",
-
- initialize: function(modelEtc, options) {
- 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.hiddenFields = [];
- this.options = options;
- },
-
- 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).closest('.column-header').attr('data-field');
- 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.hiddenFields});
- },
-
- 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) {
- this.model.query({
- sort: [
- [this.state.currentColumn, order]
- ]
- });
- },
-
- hideColumn: function() {
- this.hiddenFields.push(this.state.currentColumn);
- this.render();
- },
-
- showColumn: function(e) {
- this.hiddenFields = _.without(this.hiddenFields, $(e.target).data('column'));
- this.render();
- },
-
- // ======================================================
- // #### Templating
- template: ' \
- \
- \
- \
- \
- \
- {{#notEmpty}} \
- | \
- \
- \
- \
- \
- | \
- {{/notEmpty}} \
- {{#fields}} \
- \
- \
- \
- | \
- {{/fields}} \
-
\
- \
- \
-
\
- ',
-
- toTemplateJSON: function() {
- 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) { return field.toJSON() });
- return modelData;
- },
- render: function() {
- var self = this;
- this.fields = this.model.fields.filter(function(field) {
- return _.indexOf(self.hiddenFields, field.id) == -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,
- fields: self.fields,
- },
- self.options
- );
- newView.render();
- });
- this.el.toggleClass('no-hidden', (self.hiddenFields.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 fields in the constructor options. This should be list of fields for the DataTable.
-//
-// Additional options can be passed in a second hash argument. Options:
-//
-// * cellRenderer: function to render cells. Signature: function(value,
-// field, doc) where value is the value of this cell, field is
-// corresponding field object and document is the document object. Note
-// that implementing functions can ignore arguments (e.g.
-// function(value) would be a valid cellRenderer function).
-my.DataTableRow = Backbone.View.extend({
- initialize: function(initData, options) {
- _.bindAll(this, 'render');
- this._fields = initData.fields;
- if (options && options.cellRenderer) {
- this._cellRenderer = options.cellRenderer;
- } else {
- this._cellRenderer = function(value) {
- return value;
- }
- }
- this.el = $(this.el);
- this.model.bind('change', this.render);
- },
-
- template: ' \
- | \
- {{#cells}} \
- \
- \
- | \
- {{/cells}} \
- ',
- events: {
- 'click .data-table-cell-edit': 'onEditClick',
- 'click .data-table-cell-editor .okButton': 'onEditorOK',
- 'click .data-table-cell-editor .cancelButton': 'onEditorCancel'
- },
-
- toTemplateJSON: function() {
- var self = this;
- var doc = this.model;
- var cellData = this._fields.map(function(field) {
- return {
- field: field.id,
- value: self._cellRenderer(doc.get(field.id), field, doc)
- }
- })
- 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 field = cell.parents('td').attr('data-field');
- var newValue = cell.parents('.data-table-cell-editor').find('.data-table-cell-editor-editor').val();
- var newData = {};
- newData[field] = 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
diff --git a/test/index.html b/test/index.html
index cd87c9af..a00c9f81 100644
--- a/test/index.html
+++ b/test/index.html
@@ -21,6 +21,7 @@
+