diff --git a/lib/backgrid.css b/lib/backgrid.css
index 4fd93371..80db7c9a 100644
--- a/lib/backgrid.css
+++ b/lib/backgrid.css
@@ -2,7 +2,7 @@
backgrid
http://github.com/wyuenho/backgrid
- Copyright (c) 2012 Jimmy Yuen Ho Wong
+ Copyright (c) 2013 Jimmy Yuen Ho Wong
Licensed under the MIT @license.
*/
diff --git a/lib/backgrid.js b/lib/backgrid.js
index 2bf56064..d608cb9c 100644
--- a/lib/backgrid.js
+++ b/lib/backgrid.js
@@ -9,57 +9,73 @@
"use strict";
- var window = root;
-
- var Backgrid = root.Backgrid = {
- VERSION: "0.1",
- Extension: {}
- };
-
- function trim(s) {
- if (String.prototype.trim) {
- return String.prototype.trim.call(s, s);
- } else {
- return s.replace(/^\s+|\s+$/g, "");
- }
- }
+/*
+ backgrid
+ http://github.com/wyuenho/backgrid
- function capitalize(s) {
- return String.fromCharCode(s.charCodeAt(0) - 32) + s.slice(1);
- }
+ Copyright (c) 2013 Jimmy Yuen Ho Wong
+ Licensed under the MIT @license.
+*/
- function lpad(str, length, padstr) {
- var paddingLen = length - (str + "").length;
- paddingLen = paddingLen < 0 ? 0 : paddingLen;
- var padding = "";
- for (var i = 0; i < paddingLen; i++) {
- padding += padstr;
- }
- return padding + str;
+var window = root;
+
+var Backgrid = root.Backgrid = {
+ VERSION: "0.1",
+ Extension: {}
+};
+
+function trim(s) {
+ if (String.prototype.trim) {
+ return String.prototype.trim.call(s, s);
}
- function requireOptions(options, requireOptionKeys) {
- for (var i = 0; i < requireOptionKeys.length; i++) {
- var key = requireOptionKeys[i];
- if (_.isUndefined(options[key])) {
- throw new TypeError("'" + key + "' is required");
- }
+ return s.replace(/^\s+|\s+$/g, "");
+}
+
+function capitalize(s) {
+ return String.fromCharCode(s.charCodeAt(0) - 32) + s.slice(1);
+}
+
+function lpad(str, length, padstr) {
+ var paddingLen = length - (str + '').length;
+ paddingLen = paddingLen < 0 ? 0 : paddingLen;
+ var padding = '';
+ for (var i = 0; i < paddingLen; i++) {
+ padding = padding + padstr;
+ }
+ return padding + str;
+}
+
+function requireOptions(options, requireOptionKeys) {
+ for (var i = 0; i < requireOptionKeys.length; i++) {
+ var key = requireOptionKeys[i];
+ if (_.isUndefined(options[key])) {
+ throw new TypeError("'" + key + "' is required");
}
}
-
- function resolveNameToClass(name, suffix) {
- if (_.isString(name)) {
- var key = capitalize(name) + suffix;
- var klass = Backgrid[key] || Backgrid.Extension[key];
- if (_.isUndefined(klass)) {
- throw new ReferenceError("Class '" + key + "' not found");
- }
- return klass;
+}
+
+function resolveNameToClass(name, suffix) {
+ if (_.isString(name)) {
+ var key = capitalize(name) + suffix;
+ var klass = Backgrid[key] || Backgrid.Extension[key];
+ if (_.isUndefined(klass)) {
+ throw new ReferenceError("Class '" + key + "' not found");
}
- return name;
+ return klass;
}
- /**
+ return name;
+}
+/*
+ backgrid
+ http://github.com/wyuenho/backgrid
+
+ Copyright (c) 2013 Jimmy Yuen Ho Wong
+ Licensed under the MIT @license.
+*/
+
+/**
Just a convenient class for interested parties to subclass.
The default Cell classes don't require the formatter to be a subclass of
@@ -70,20 +86,21 @@
@class Backgrid.CellFormatter
@constructor
*/
- var CellFormatter = Backgrid.CellFormatter = function() {};
+var CellFormatter = Backgrid.CellFormatter = function () {};
+_.extend(CellFormatter.prototype, {
- _.extend(CellFormatter.prototype, {
- /**
+ /**
Takes a raw value from a model and returns a formatted string for display.
@member Backgrid.CellFormatter
@param {*} rawData
@return {string}
*/
- fromRaw: function(rawData) {
- return rawData;
- },
- /**
+ fromRaw: function (rawData) {
+ return rawData;
+ },
+
+ /**
Takes a formatted string, usually from user input, and returns a
appropriately typed value for persistence in the model.
@@ -94,12 +111,13 @@
@param {string} formattedData
@return {*|undefined}
*/
- toRaw: function(formattedData) {
- return formattedData;
- }
- });
+ toRaw: function (formattedData) {
+ return formattedData;
+ }
- /**
+});
+
+/**
A floating point number formatter. Doesn't understand notation at the moment.
@class Backgrid.NumberFormatter
@@ -107,18 +125,18 @@
@constructor
@throws {RangeError} If decimals < 0 or > 20.
*/
- var NumberFormatter = Backgrid.NumberFormatter = function(options) {
- options = options ? _.clone(options) : {};
- _.extend(this, this.defaults, options);
- if (this.decimals < 0 || this.decimals > 20) {
- throw new RangeError("decimals must be between 0 and 20");
- }
- };
+var NumberFormatter = Backgrid.NumberFormatter = function (options) {
+ options = options ? _.clone(options) : {};
+ _.extend(this, this.defaults, options);
- NumberFormatter.prototype = new CellFormatter();
+ if (this.decimals < 0 || this.decimals > 20) {
+ throw new RangeError("decimals must be between 0 and 20");
+ }
+};
+NumberFormatter.prototype = new CellFormatter;
+_.extend(NumberFormatter.prototype, {
- _.extend(NumberFormatter.prototype, {
- /**
+ /**
@member Backgrid.NumberFormatter
@cfg {Object} options
@@ -130,13 +148,15 @@
@cfg {string} [options.orderSeparator=','] The separator to use to
separator thousands. May be an empty string.
*/
- defaults: {
- decimals: 2,
- decimalSeparator: ".",
- orderSeparator: ","
- },
- HUMANIZED_NUM_RE: /(\d)(?=(?:\d{3})+$)/g,
- /**
+ defaults: {
+ decimals: 2,
+ decimalSeparator: '.',
+ orderSeparator: ','
+ },
+
+ HUMANIZED_NUM_RE: /(\d)(?=(?:\d{3})+$)/g,
+
+ /**
Takes a floating point number and convert it to a formatted string where
every thousand is separated by `orderSeparator`, with a `decimal` number of
decimals separated by `decimalSeparator`. The number returned is rounded
@@ -146,17 +166,19 @@
@param {number} number
@return {string}
*/
- fromRaw: function(number) {
- if (isNaN(number) || null === number) {
- return "";
- }
- number = number.toFixed(~~this.decimals);
- var parts = number.split(".");
- var integerPart = parts[0];
- var decimalPart = parts[1] ? (this.decimalSeparator || ".") + parts[1] : "";
- return integerPart.replace(this.HUMANIZED_NUM_RE, "$1" + this.orderSeparator) + decimalPart;
- },
- /**
+ fromRaw: function (number) {
+ if (isNaN(number) || number === null) return '';
+
+ number = number.toFixed(~~this.decimals);
+
+ var parts = number.split('.');
+ var integerPart = parts[0];
+ var decimalPart = parts[1] ? (this.decimalSeparator || '.') + parts[1] : '';
+
+ return integerPart.replace(this.HUMANIZED_NUM_RE, '$1' + this.orderSeparator) + decimalPart;
+ },
+
+ /**
Takes a string, possibly formatted with `orderSeparator` and/or
`decimalSeparator`, and convert it back to a number.
@@ -165,30 +187,31 @@
@return {number|undefined} Undefined if the string cannot be converted to
a number.
*/
- toRaw: function(formattedData) {
- var rawData = "";
- var thousands = trim(formattedData).split(this.orderSeparator);
- for (var i = 0; i < thousands.length; i++) {
- rawData += thousands[i];
- }
- var decimalParts = rawData.split(this.decimalSeparator);
- rawData = "";
- for (var i = 0; i < decimalParts.length; i++) {
- rawData = rawData + decimalParts[i] + ".";
- }
- if ("." === rawData[rawData.length - 1]) {
- rawData = rawData.slice(0, rawData.length - 1);
- }
- var result = 1 * (1 * rawData).toFixed(~~this.decimals);
- if (_.isNumber(result) && !_.isNaN(result)) {
- return result;
- } else {
- return void 0;
- }
+ toRaw: function (formattedData) {
+ var rawData = '';
+
+ var thousands = trim(formattedData).split(this.orderSeparator);
+ for (var i = 0; i < thousands.length; i++) {
+ rawData += thousands[i];
}
- });
- /**
+ var decimalParts = rawData.split(this.decimalSeparator);
+ rawData = '';
+ for (var i = 0; i < decimalParts.length; i++) {
+ rawData = rawData + decimalParts[i] + '.';
+ }
+
+ if (rawData[rawData.length - 1] === '.') {
+ rawData = rawData.slice(0, rawData.length - 1);
+ }
+
+ var result = (rawData * 1).toFixed(~~this.decimals) * 1;
+ if (_.isNumber(result) && !_.isNaN(result)) return result;
+ }
+
+});
+
+/**
Formatter to converts between various datetime string formats.
This class only understands ISO-8601 formatted datetime strings. See
@@ -200,18 +223,18 @@
@constructor
@throws {Error} If both `includeDate` and `includeTime` are false.
*/
- var DatetimeFormatter = Backgrid.DatetimeFormatter = function(options) {
- options = options ? _.clone(options) : {};
- _.extend(this, this.defaults, options);
- if (!this.includeDate && !this.includeTime) {
- throw new Error("Either includeDate or includeTime must be true");
- }
- };
+var DatetimeFormatter = Backgrid.DatetimeFormatter = function (options) {
+ options = options ? _.clone(options) : {};
+ _.extend(this, this.defaults, options);
- DatetimeFormatter.prototype = new CellFormatter();
+ if (!this.includeDate && !this.includeTime) {
+ throw new Error("Either includeDate or includeTime must be true");
+ }
+};
+DatetimeFormatter.prototype = new CellFormatter;
+_.extend(DatetimeFormatter.prototype, {
- _.extend(DatetimeFormatter.prototype, {
- /**
+ /**
@member Backgrid.DatetimeFormatter
@cfg {Object} options
@@ -225,52 +248,63 @@
@cfg {boolean} [options.includeMilli=false] If `includeTime` is true,
whether to include the millisecond part, if it exists.
*/
- defaults: {
- includeDate: true,
- includeTime: true,
- includeMilli: false
- },
- DATE_RE: /^([+\-]?\d{4})-(\d{2})-(\d{2})$/,
- TIME_RE: /^(\d{2}):(\d{2}):(\d{2})(\.(\d{3}))?$/,
- ISO_SPLITTER_RE: /T|Z| +/,
- _convert: function(data, validate) {
- data = trim(data);
- var parts = data.split(this.ISO_SPLITTER_RE) || [];
- var date = this.DATE_RE.test(parts[0]) ? parts[0] : "";
- var time = date && parts[1] ? parts[1] : this.TIME_RE.test(parts[0]) ? parts[0] : "";
- var YYYYMMDD = this.DATE_RE.exec(date) || [];
- var HHmmssSSS = this.TIME_RE.exec(time) || [];
- if (validate) {
- if (this.includeDate && _.isUndefined(YYYYMMDD[0])) {
- return;
- }
- if (this.includeTime && _.isUndefined(HHmmssSSS[0])) {
- return;
- }
- if (!this.includeDate && date) {
- return;
- }
- if (!this.includeTime && time) {
- return;
- }
- }
- var jsDate = new Date(Date.UTC(1 * YYYYMMDD[1] || 0, 1 * YYYYMMDD[2] - 1 || 0, 1 * YYYYMMDD[3] || 0, 1 * HHmmssSSS[1] || null, 1 * HHmmssSSS[2] || null, 1 * HHmmssSSS[3] || null, 1 * HHmmssSSS[5] || null));
- var result = "";
- if (this.includeDate) {
- result = lpad(jsDate.getUTCFullYear(), 4, 0) + "-" + lpad(jsDate.getUTCMonth() + 1, 2, 0) + "-" + lpad(jsDate.getUTCDate(), 2, 0);
- }
- if (this.includeTime) {
- result = result + (this.includeDate ? "T" : "") + lpad(jsDate.getUTCHours(), 2, 0) + ":" + lpad(jsDate.getUTCMinutes(), 2, 0) + ":" + lpad(jsDate.getUTCSeconds(), 2, 0);
- if (this.includeMilli) {
- result = result + "." + lpad(jsDate.getUTCMilliseconds(), 3, 0);
- }
- }
- if (this.includeDate && this.includeTime) {
- result += "Z";
+ defaults: {
+ includeDate: true,
+ includeTime: true,
+ includeMilli: false
+ },
+
+ DATE_RE: /^([+\-]?\d{4})-(\d{2})-(\d{2})$/,
+ TIME_RE: /^(\d{2}):(\d{2}):(\d{2})(\.(\d{3}))?$/,
+ ISO_SPLITTER_RE: /T|Z| +/,
+
+ _convert: function (data, validate) {
+ data = trim(data);
+ var parts = data.split(this.ISO_SPLITTER_RE) || [];
+
+ var date = this.DATE_RE.test(parts[0]) ? parts[0] : '';
+ var time = date && parts[1] ? parts[1] : this.TIME_RE.test(parts[0]) ? parts[0] : '';
+
+ var YYYYMMDD = this.DATE_RE.exec(date) || [];
+ var HHmmssSSS = this.TIME_RE.exec(time) || [];
+
+ if (validate) {
+ if (this.includeDate && _.isUndefined(YYYYMMDD[0])) return;
+ if (this.includeTime && _.isUndefined(HHmmssSSS[0])) return;
+ if (!this.includeDate && date) return;
+ if (!this.includeTime && time) return;
+ }
+
+ var jsDate = new Date(Date.UTC(YYYYMMDD[1] * 1 || 0,
+ YYYYMMDD[2] * 1 - 1 || 0,
+ YYYYMMDD[3] * 1 || 0,
+ HHmmssSSS[1] * 1 || null,
+ HHmmssSSS[2] * 1 || null,
+ HHmmssSSS[3] * 1 || null,
+ HHmmssSSS[5] * 1 || null));
+
+ var result = '';
+
+ if (this.includeDate) {
+ result = lpad(jsDate.getUTCFullYear(), 4, 0) + '-' + lpad(jsDate.getUTCMonth() + 1, 2, 0) + '-' + lpad(jsDate.getUTCDate(), 2, 0);
+ }
+
+ if (this.includeTime) {
+ result = result + (this.includeDate ? 'T' : '') + lpad(jsDate.getUTCHours(), 2, 0) + ':' + lpad(jsDate.getUTCMinutes(), 2, 0) + ':' + lpad(jsDate.getUTCSeconds(), 2, 0);
+
+ if (this.includeMilli) {
+ result = result + '.' + lpad(jsDate.getUTCMilliseconds(), 3, 0);
}
- return result;
- },
- /**
+ }
+
+ if (this.includeDate && this.includeTime) {
+ result += "Z";
+ }
+
+ return result;
+ },
+
+ /**
Converts an ISO-8601 formatted datetime string to a datetime string, date
string or a time string. The timezone is ignored if supplied.
@@ -278,10 +312,11 @@
@param {string} rawData
@return {string} ISO-8601 string in UTC.
*/
- fromRaw: function(rawData) {
- return this._convert(rawData);
- },
- /**
+ fromRaw: function (rawData) {
+ return this._convert(rawData);
+ },
+
+ /**
Converts an ISO-8601 formatted datetime string to a datetime string, date
string or a time string. The timezone is ignored if supplied. This method
parses the input values exactly the same way as
@@ -295,12 +330,21 @@
or if `includeDate` is true and a date is not found, or if `includeTime` is
true and a time is not found.
*/
- toRaw: function(formattedData) {
- return this._convert(formattedData, true);
- }
- });
+ toRaw: function (formattedData) {
+ return this._convert(formattedData, true);
+ }
- /**
+});
+
+/*
+ backgrid
+ http://github.com/wyuenho/backgrid
+
+ Copyright (c) 2013 Jimmy Yuen Ho Wong
+ Licensed under the MIT @license.
+*/
+
+/**
Generic cell editor base class. Only defines an initializer for a number of
required parameters.
@@ -308,8 +352,9 @@
@class Backgrid.CellEditor
@extends Backbone.View
*/
- var CellEditor = Backgrid.CellEditor = Backbone.View.extend({
- /**
+var CellEditor = Backgrid.CellEditor = Backbone.View.extend({
+
+ /**
Initializer.
@param {Object} options
@@ -321,30 +366,32 @@
@throws {TypeError} If `formatter` is not a formatter instance, or when
`model` or `column` are undefined.
*/
- initialize: function(options) {
- requireOptions(options, [ "formatter", "column", "model" ]);
- this.parent = options.parent;
- this.formatter = options.formatter;
- this.column = options.column;
- if (!(this.column instanceof Column)) {
- this.column = new Column(this.column);
- }
- if (this.parent && _.isFunction(this.parent.on)) {
- this.listenTo(this.parent, "editing", this.postRender);
- }
- },
- /**
+ initialize: function (options) {
+ requireOptions(options, ["formatter", "column", "model"]);
+ this.parent = options.parent;
+ this.formatter = options.formatter;
+ this.column = options.column;
+ if (!(this.column instanceof Column)) {
+ this.column = new Column(this.column);
+ }
+ if (this.parent && _.isFunction(this.parent.on)) {
+ this.listenTo(this.parent, "editing", this.postRender);
+ }
+ },
+
+ /**
Post-rendering setup and initialization. Focuses the cell editor's `el` in
this default implementation. **Should** be called by Cell classes after
calling Backgrid.CellEditor#render.
*/
- postRender: function() {
- this.$el.focus();
- return this;
- }
- });
+ postRender: function () {
+ this.$el.focus();
+ return this;
+ }
- /**
+});
+
+/**
InputCellEditor the cell editor type used by most core cell types. This cell
editor renders a text input box as its editor. The input will render a
placeholder if the value is empty on supported browsers.
@@ -352,19 +399,23 @@
@class Backgrid.InputCellEditor
@extends Backgrid.CellEditor
*/
- var InputCellEditor = Backgrid.InputCellEditor = CellEditor.extend({
- /** @property */
- tagName: "input",
- /** @property */
- attributes: {
- type: "text"
- },
- /** @property */
- events: {
- blur: "saveOrCancel",
- keydown: "saveOrCancel"
- },
- /**
+var InputCellEditor = Backgrid.InputCellEditor = CellEditor.extend({
+
+ /** @property */
+ tagName: "input",
+
+ /** @property */
+ attributes: {
+ type: "text"
+ },
+
+ /** @property */
+ events: {
+ "blur": "saveOrCancel",
+ "keydown": "saveOrCancel"
+ },
+
+ /**
Initializer. Removes this `el` from the DOM when a `done` event is
triggered.
@@ -374,22 +425,26 @@
@param {Backbone.Model} options.model
@param {string} [options.placeholder]
*/
- initialize: function(options) {
- CellEditor.prototype.initialize.apply(this, arguments);
- if (options.placeholder) {
- this.$el.attr("placeholder", options.placeholder);
- }
- this.listenTo(this, "done", this.remove);
- },
- /**
+ initialize: function (options) {
+ CellEditor.prototype.initialize.apply(this, arguments);
+
+ if (options.placeholder) {
+ this.$el.attr("placeholder", options.placeholder);
+ }
+
+ this.listenTo(this, "done", this.remove);
+ },
+
+ /**
Renders a text input with the cell value formatted for display, if it
exists.
*/
- render: function() {
- this.$el.val(this.formatter.fromRaw(this.model.get(this.column.get("name"))));
- return this;
- },
- /**
+ render: function () {
+ this.$el.val(this.formatter.fromRaw(this.model.get(this.column.get("name"))));
+ return this;
+ },
+
+ /**
If the key pressed is `enter` or `tab`, converts the value in the editor to
a raw value for the model using the formatter.
@@ -406,53 +461,58 @@
@param {Event} e
*/
- saveOrCancel: function(e) {
- if ("keydown" === e.type) {
- // enter or tab
- if (13 === e.keyCode || 9 === e.keyCode) {
- e.preventDefault();
- var valueToSet = this.formatter.toRaw(this.$el.val());
- if (_.isUndefined(valueToSet) || !this.model.set(this.column.get("name"), valueToSet, {
- validate: true
- })) {
- this.trigger("error");
- } else {
- this.trigger("done");
- }
- } else {
- if (27 === e.keyCode) {
- // undo
- e.stopPropagation();
- this.trigger("done");
- }
+ saveOrCancel: function (e) {
+ if (e.type === "keydown") {
+ // enter or tab
+ if (e.keyCode === 13 || e.keyCode === 9) {
+ e.preventDefault();
+ var valueToSet = this.formatter.toRaw(this.$el.val());
+
+ if (_.isUndefined(valueToSet) ||
+ !this.model.set(this.column.get("name"), valueToSet,
+ {validate: true})) {
+ this.trigger("error");
}
- } else {
- if ("blur" === e.type) {
- if (this.formatter.fromRaw(this.model.get(this.column.get("name"))) === this.$el.val()) {
- this.trigger("done");
- } else {
- var self = this;
- var timeout = window.setTimeout(function() {
- self.$el.focus();
- window.clearTimeout(timeout);
- }, 1);
- }
+ else {
+ this.trigger("done");
}
}
- },
- postRender: function() {
- // move the cursor to the end on firefox if text is right aligned
- if ("right" === this.$el.css("text-align")) {
- var val = this.$el.val();
- this.$el.focus().val(null).val(val);
- } else {
- this.$el.focus();
+ // esc
+ else if (e.keyCode === 27) {
+ // undo
+ e.stopPropagation();
+ this.trigger("done");
}
- return this;
}
- });
+ else if (e.type === "blur") {
+ if (this.formatter.fromRaw(this.model.get(this.column.get("name"))) === this.$el.val()) {
+ this.trigger("done");
+ }
+ else {
+ var self = this;
+ var timeout = window.setTimeout(function () {
+ self.$el.focus();
+ window.clearTimeout(timeout);
+ }, 1);
+ }
+ }
+ },
- /**
+ postRender: function () {
+ // move the cursor to the end on firefox if text is right aligned
+ if (this.$el.css("text-align") === "right") {
+ var val = this.$el.val();
+ this.$el.focus().val(null).val(val);
+ }
+ else {
+ this.$el.focus();
+ }
+ return this;
+ }
+
+});
+
+/**
The super-class for all Cell types. By default, this class renders a plain
table cell with the model value converted to a string using the
formatter. The table cell is clickable, upon which the cell will go into
@@ -464,26 +524,31 @@
@class Backgrid.Cell
@extends Backbone.View
*/
- var Cell = Backgrid.Cell = Backbone.View.extend({
- /** @property */
- tagName: "td",
- /**
+var Cell = Backgrid.Cell = Backbone.View.extend({
+
+ /** @property */
+ tagName: "td",
+
+ /**
@property {Backgrid.CellFormatter|Object|string} [formatter=new CellFormatter()]
*/
- formatter: new CellFormatter(),
- /**
+ formatter: new CellFormatter(),
+
+ /**
@property {Backgrid.CellEditor} [editor=Backgrid.InputCellEditor] The
default editor for all cell instances of this class. This value must be a
class, it will be automatically instantiated upon entering edit mode.
See Backgrid.CellEditor
*/
- editor: InputCellEditor,
- /** @property */
- events: {
- click: "enterEditMode"
- },
- /**
+ editor: InputCellEditor,
+
+ /** @property */
+ events: {
+ "click": "enterEditMode"
+ },
+
+ /**
Initializer.
@param {Object} options
@@ -493,24 +558,26 @@
@throws {ReferenceError} If formatter is a string but a formatter class of
said name cannot be found in the Backgrid module.
*/
- initialize: function(options) {
- requireOptions(options, [ "model", "column" ]);
- this.column = options.column;
- if (!(this.column instanceof Column)) {
- this.column = new Column(this.column);
- }
- this.formatter = resolveNameToClass(this.formatter, "Formatter");
- this.editor = resolveNameToClass(this.editor, "CellEditor");
- },
- /**
+ initialize: function (options) {
+ requireOptions(options, ["model", "column"]);
+ this.column = options.column;
+ if (!(this.column instanceof Column)) {
+ this.column = new Column(this.column);
+ }
+ this.formatter = resolveNameToClass(this.formatter, "Formatter");
+ this.editor = resolveNameToClass(this.editor, "CellEditor");
+ },
+
+ /**
Render a text string in a table cell. The text is converted from the
model's raw value for this cell's column.
*/
- render: function() {
- this.$el.empty().text(this.formatter.fromRaw(this.model.get(this.column.get("name"))));
- return this;
- },
- /**
+ render: function () {
+ this.$el.empty().text(this.formatter.fromRaw(this.model.get(this.column.get("name"))));
+ return this;
+ },
+
+ /**
If this column is editable, a new CellEditor instance is instantiated with
its required parameters and listens on the editor's `done` and `error`
events. When the editor is `done`, edit mode is exited. When the editor
@@ -518,15 +585,17 @@
current user input to an apprpriate value for the model's column. An
`editor` CSS class is added to the cell upon entering edit mode.
*/
- enterEditMode: function(e) {
- if (this.column.get("editable")) {
- this.currentEditor = new this.editor({
- parent: this,
- column: this.column,
- model: this.model,
- formatter: this.formatter
- });
- /**
+ enterEditMode: function (e) {
+ if (this.column.get("editable")) {
+
+ this.currentEditor = new this.editor({
+ parent: this,
+ column: this.column,
+ model: this.model,
+ formatter: this.formatter
+ });
+
+ /**
Backbone Event. Fired when a cell is entering edit mode and an editor
instance has been constructed, but before it is rendered and inserted
into the DOM.
@@ -535,63 +604,74 @@
@param {Backgrid.Cell} cell This cell instance.
@param {Backgrid.CellEditor} editor The cell editor constructed.
*/
- this.trigger("edit", this, this.currentEditor);
- this.listenTo(this.currentEditor, "done", this.exitEditMode);
- this.listenTo(this.currentEditor, "error", this.renderError);
- this.$el.empty();
- this.undelegateEvents();
- this.$el.append(this.currentEditor.$el);
- this.currentEditor.render();
- this.$el.addClass("editor");
- /**
+ this.trigger("edit", this, this.currentEditor);
+
+ this.listenTo(this.currentEditor, "done", this.exitEditMode);
+ this.listenTo(this.currentEditor, "error", this.renderError);
+
+ this.$el.empty();
+ this.undelegateEvents();
+ this.$el.append(this.currentEditor.$el);
+ this.currentEditor.render();
+ this.$el.addClass("editor");
+
+ /**
Backbone Event. Fired when a cell has finished switching to edit mode.
@event editing
@param {Backgrid.Cell} cell This cell instance.
@param {Backgrid.CellEditor} editor The cell editor constructed.
*/
- this.trigger("editing", this, this.currentEditor);
- }
- },
- /**
+ this.trigger("editing", this, this.currentEditor);
+ }
+ },
+
+ /**
Put an `error` CSS class on the table cell.
*/
- renderError: function() {
- this.$el.addClass("error");
- },
- /**
+ renderError: function () {
+ this.$el.addClass("error");
+ },
+
+ /**
Removes the editor and re-render in display mode.
*/
- exitEditMode: function() {
- this.$el.removeClass("error");
- this.currentEditor.off(null, null, this);
+ exitEditMode: function () {
+ this.$el.removeClass("error");
+ this.currentEditor.off(null, null, this);
+ this.currentEditor.remove();
+ delete this.currentEditor;
+ this.$el.removeClass("editor");
+ this.render();
+ this.delegateEvents();
+ },
+
+ remove: function () {
+ Backbone.View.prototype.remove.apply(this, arguments);
+ if (this.currentEditor) {
this.currentEditor.remove();
delete this.currentEditor;
- this.$el.removeClass("editor");
- this.render();
- this.delegateEvents();
- },
- remove: function() {
- Backbone.View.prototype.remove.apply(this, arguments);
- if (this.currentEditor) {
- this.currentEditor.remove();
- delete this.currentEditor;
- }
}
- });
+ }
- /**
+});
+
+/**
StringCell displays HTML escaped strings and accepts anything typed in.
@class Backgrid.StringCell
@extends Backgrid.Cell
*/
- var StringCell = Backgrid.StringCell = Cell.extend({
- /** @property */
- className: "string-cell"
- });
+var StringCell = Backgrid.StringCell = Cell.extend({
- /**
+ /** @property */
+ className: "string-cell"
+
+ // No formatter needed. Strings call auto-escaped by jQuery on insertion.
+
+});
+
+/**
UriCell renders an HTML `` anchor for the value and accepts URIs as user
input values. A URI input is URI encoded using `encodeURI()` before writing
to the underlying model.
@@ -599,31 +679,35 @@
@class Backgrid.UriCell
@extends Backgrid.Cell
*/
- var UriCell = Backgrid.UriCell = Cell.extend({
- /** @property */
- className: "uri-cell",
- formatter: {
- fromRaw: function(rawData) {
- return rawData;
- },
- toRaw: function(formattedData) {
- var result = encodeURI(formattedData);
- return "undefined" === result ? void 0 : result;
- }
+var UriCell = Backgrid.UriCell = Cell.extend({
+
+ /** @property */
+ className: "uri-cell",
+
+ formatter: {
+ fromRaw: function (rawData) {
+ return rawData;
},
- render: function() {
- this.$el.empty();
- var formattedValue = this.formatter.fromRaw(this.model.get(this.column.get("name")));
- this.$el.append($("", {
- href: formattedValue,
- title: formattedValue,
- target: "_blank"
- }).text(formattedValue));
- return this;
+ toRaw: function (formattedData) {
+ var result = encodeURI(formattedData);
+ return result === "undefined" ? undefined : result;
}
- });
+ },
+
+ render: function () {
+ this.$el.empty();
+ var formattedValue = this.formatter.fromRaw(this.model.get(this.column.get("name")));
+ this.$el.append($("", {
+ href: formattedValue,
+ title: formattedValue,
+ target: "_blank"
+ }).text(formattedValue));
+ return this;
+ }
- /**
+});
+
+/**
Like Backgrid.UriCell, EmailCell renders an HTML `` anchor for the
value. The `href` in the anchor is prefixed with `mailto:`. EmailCell will
complain if the user enters a string that doesn't contain the `@` sign.
@@ -631,71 +715,80 @@
@class Backgrid.EmailCell
@extends Backgrid.Cell
*/
- var EmailCell = Backgrid.EmailCell = Cell.extend({
- /** @property */
- className: "email-cell",
- formatter: {
- fromRaw: function(rawData) {
- return rawData;
- },
- toRaw: function(formattedData) {
- var parts = formattedData.split("@");
- if (2 === parts.length && _.all(parts)) {
- return formattedData;
- } else {
- return void 0;
- }
- }
+var EmailCell = Backgrid.EmailCell = Cell.extend({
+
+ /** @property */
+ className: "email-cell",
+
+ formatter: {
+ fromRaw: function (rawData) {
+ return rawData;
},
- render: function() {
- this.$el.empty();
- var formattedValue = this.formatter.fromRaw(this.model.get(this.column.get("name")));
- this.$el.append($("", {
- href: "mailto:" + formattedValue,
- title: formattedValue
- }).text(formattedValue));
- return this;
+ toRaw: function (formattedData) {
+ var parts = formattedData.split("@");
+ if (parts.length === 2 && _.all(parts)) {
+ return formattedData;
+ }
}
- });
+ },
+
+ render: function () {
+ this.$el.empty();
+ var formattedValue = this.formatter.fromRaw(this.model.get(this.column.get("name")));
+ this.$el.append($("", {
+ href: "mailto:" + formattedValue,
+ title: formattedValue
+ }).text(formattedValue));
+ return this;
+ }
- /**
+});
+
+/**
NumberCell is a generic cell that renders all numbers. Numbers are formatted
using a Backgrid.NumberFormatter.
@class Backgrid.NumberCell
@extends Backgrid.Cell
*/
- var NumberCell = Backgrid.NumberCell = Cell.extend({
- /** @property */
- className: "number-cell",
- /**
+var NumberCell = Backgrid.NumberCell = Cell.extend({
+
+ /** @property */
+ className: "number-cell",
+
+ /**
@property {number} [decimals=2] Must be an integer.
*/
- decimals: NumberFormatter.prototype.defaults.decimals,
- /** @property {string} [decimalSeparator='.'] */
- decimalSeparator: NumberFormatter.prototype.defaults.decimalSeparator,
- /** @property {string} [orderSeparator=','] */
- orderSeparator: NumberFormatter.prototype.defaults.orderSeparator,
- /** @property {Backgrid.CellFormatter} [formatter=Backgrid.NumberFormatter] */
- formatter: NumberFormatter,
- /**
+ decimals: NumberFormatter.prototype.defaults.decimals,
+
+ /** @property {string} [decimalSeparator='.'] */
+ decimalSeparator: NumberFormatter.prototype.defaults.decimalSeparator,
+
+ /** @property {string} [orderSeparator=','] */
+ orderSeparator: NumberFormatter.prototype.defaults.orderSeparator,
+
+ /** @property {Backgrid.CellFormatter} [formatter=Backgrid.NumberFormatter] */
+ formatter: NumberFormatter,
+
+ /**
Initializes this cell and the number formatter.
@param {Object} options
@param {Backbone.Model} options.model
@param {Backgrid.Column} options.column
*/
- initialize: function(options) {
- Cell.prototype.initialize.apply(this, arguments);
- this.formatter = new this.formatter({
- decimals: this.decimals,
- decimalSeparator: this.decimalSeparator,
- orderSeparator: this.orderSeparator
- });
- }
- });
+ initialize: function (options) {
+ Cell.prototype.initialize.apply(this, arguments);
+ this.formatter = new this.formatter({
+ decimals: this.decimals,
+ decimalSeparator: this.decimalSeparator,
+ orderSeparator: this.orderSeparator
+ });
+ }
- /**
+});
+
+/**
An IntegerCell is just a Backgrid.NumberCell with 0 decimals. If a floating point
number is supplied, the number is simply rounded the usual way when
displayed.
@@ -703,16 +796,18 @@
@class Backgrid.IntegerCell
@extends Backgrid.NumberCell
*/
- var IntegerCell = Backgrid.IntegerCell = NumberCell.extend({
- /** @property */
- className: "integer-cell",
- /**
+var IntegerCell = Backgrid.IntegerCell = NumberCell.extend({
+
+ /** @property */
+ className: "integer-cell",
+
+ /**
@property {number} decimals Must be an integer.
*/
- decimals: 0
- });
+ decimals: 0
+});
- /**
+/**
DatetimeCell is a basic cell that accepts datetime string values in RFC-2822
or W3C's subset of ISO-8601 and displays them in ISO-8601 format. For a much
more sophisticated date time cell with better datetime formatting, take a
@@ -726,76 +821,91 @@
- Backgrid.Extension.MomentCell
- Backgrid.DatetimeFormatter
*/
- var DatetimeCell = Backgrid.DatetimeCell = Cell.extend({
- /** @property */
- className: "datetime-cell",
- /**
+var DatetimeCell = Backgrid.DatetimeCell = Cell.extend({
+
+ /** @property */
+ className: "datetime-cell",
+
+ /**
@property {boolean} [includeDate=true]
*/
- includeDate: DatetimeFormatter.prototype.defaults.includeDate,
- /**
+ includeDate: DatetimeFormatter.prototype.defaults.includeDate,
+
+ /**
@property {boolean} [includeTime=true]
*/
- includeTime: DatetimeFormatter.prototype.defaults.includeTime,
- /**
+ includeTime: DatetimeFormatter.prototype.defaults.includeTime,
+
+ /**
@property {boolean} [includeMilli=false]
*/
- includeMilli: DatetimeFormatter.prototype.defaults.includeMilli,
- /** @property {Backgrid.CellFormatter} [formatter=Backgrid.DatetimeFormatter] */
- formatter: DatetimeFormatter,
- /**
+ includeMilli: DatetimeFormatter.prototype.defaults.includeMilli,
+
+ /** @property {Backgrid.CellFormatter} [formatter=Backgrid.DatetimeFormatter] */
+ formatter: DatetimeFormatter,
+
+ /**
Initializes this cell and the datetime formatter.
@param {Object} options
@param {Backbone.Model} options.model
@param {Backgrid.Column} options.column
*/
- initialize: function(options) {
- Cell.prototype.initialize.apply(this, arguments);
- this.formatter = new this.formatter({
- includeDate: this.includeDate,
- includeTime: this.includeTime,
- includeMilli: this.includeMilli
- });
- var placeholder = this.includeDate ? "YYYY-MM-DD" : "";
- placeholder += this.includeDate && this.includeTime ? "T" : "";
- placeholder += this.includeTime ? "HH:mm:ss" : "";
- placeholder += this.includeTime && this.includeMilli ? ".SSS" : "";
- this.editor = this.editor.extend({
- attributes: _.extend({}, this.editor.prototype.attributes, this.editor.attributes, {
- placeholder: placeholder
- })
- });
- }
- });
+ initialize: function (options) {
+ Cell.prototype.initialize.apply(this, arguments);
+ this.formatter = new this.formatter({
+ includeDate: this.includeDate,
+ includeTime: this.includeTime,
+ includeMilli: this.includeMilli
+ });
+
+ var placeholder = this.includeDate ? "YYYY-MM-DD" : "";
+ placeholder += (this.includeDate && this.includeTime) ? "T" : "";
+ placeholder += this.includeTime ? "HH:mm:ss" : "";
+ placeholder += (this.includeTime && this.includeMilli) ? ".SSS" : "";
+
+ this.editor = this.editor.extend({
+ attributes: _.extend({}, this.editor.prototype.attributes, this.editor.attributes, {
+ placeholder: placeholder
+ })
+ });
+ }
- /**
+});
+
+/**
DateCell is a Backgrid.DatetimeCell without the time part.
@class Backgrid.DateCell
@extends Backgrid.DatetimeCell
*/
- var DateCell = Backgrid.DateCell = DatetimeCell.extend({
- /** @property */
- className: "date-cell",
- /** @property */
- includeTime: false
- });
+var DateCell = Backgrid.DateCell = DatetimeCell.extend({
- /**
+ /** @property */
+ className: "date-cell",
+
+ /** @property */
+ includeTime: false
+
+});
+
+/**
TimeCell is a Backgrid.DatetimeCell without the date part.
@class Backgrid.TimeCell
@extends Backgrid.DatetimeCell
*/
- var TimeCell = Backgrid.TimeCell = DatetimeCell.extend({
- /** @property */
- className: "time-cell",
- /** @property */
- includeDate: false
- });
+var TimeCell = Backgrid.TimeCell = DatetimeCell.extend({
- /**
+ /** @property */
+ className: "time-cell",
+
+ /** @property */
+ includeDate: false
+
+});
+
+/**
BooleanCell is a different kind of cell in that there's no difference between
display mode and edit mode and this cell type always renders a checkbox for
selection.
@@ -803,146 +913,164 @@
@class Backgrid.BooleanCell
@extends Backgrid.Cell
*/
- var BooleanCell = Backgrid.BooleanCell = Cell.extend({
- /** @property */
- className: "boolean-cell",
- /**
+var BooleanCell = Backgrid.BooleanCell = Cell.extend({
+
+ /** @property */
+ className: "boolean-cell",
+
+ /**
BooleanCell simple uses a default HTML checkbox template instead of a
CellEditor instance.
@property {function(Object, ?Object=): string} editor The Underscore.js template to
render the editor.
*/
- editor: _.template(" />'"),
- /**
+ editor: _.template(" />'"),
+
+ /**
Since the editor is not an instance of a CellEditor subclass, more things
need to be done in BooleanCell class to listen to editor mode events.
*/
- events: {
- click: "enterEditMode",
- "blur input[type=checkbox]": "exitEditMode",
- "change input[type=checkbox]": "save"
- },
- /**
+ events: {
+ "click": "enterEditMode",
+ "blur input[type=checkbox]": "exitEditMode",
+ "change input[type=checkbox]": "save"
+ },
+
+ /**
Renders a checkbox and check it if the model value of this column is true,
uncheck otherwise.
*/
- render: function() {
- this.$el.empty();
- this.currentEditor = $(this.editor({
- checked: this.formatter.fromRaw(this.model.get(this.column.get("name")))
- }));
- this.$el.append(this.currentEditor);
- return this;
- },
- /**
+ render: function () {
+ this.$el.empty();
+ this.currentEditor = $(this.editor({
+ checked: this.formatter.fromRaw(this.model.get(this.column.get("name")))
+ }));
+ this.$el.append(this.currentEditor);
+ return this;
+ },
+
+ /**
Simple focuses the checkbox and add an `editor` CSS class to the cell.
*/
- enterEditMode: function(e) {
- this.$el.addClass("editor");
- this.currentEditor.focus();
- },
- /**
+ enterEditMode: function (e) {
+ this.$el.addClass("editor");
+ this.currentEditor.focus();
+ },
+
+ /**
Removed the `editor` CSS class from the cell.
*/
- exitEditMode: function(e) {
- this.$el.removeClass("editor");
- },
- /**
+ exitEditMode: function (e) {
+ this.$el.removeClass("editor");
+ },
+
+ /**
Set true to the model attribute if the checkbox is checked, false
otherwise.
*/
- save: function(e) {
- var val = this.formatter.toRaw(this.currentEditor.prop("checked"));
- this.model.set(this.column.get("name"), val);
- }
- });
+ save: function (e) {
+ var val = this.formatter.toRaw(this.currentEditor.prop("checked"));
+ this.model.set(this.column.get("name"), val);
+ }
- /**
+});
+
+/**
SelectCellEditor renders an HTML `