/*
backgrid
http://github.com/wyuenho/backgrid
Copyright (c) 2013 Jimmy Yuen Ho Wong
Licensed under the MIT @license.
*/
(function (root, $, _, Backbone) {
"use strict";
/*
backgrid
http://github.com/wyuenho/backgrid
Copyright (c) 2013 Jimmy Yuen Ho Wong
Licensed under the MIT @license.
*/
var window = root;
var Backgrid = root.Backgrid = {
VERSION: "0.1.4",
Extension: {}
};
// Copyright 2009, 2010 Kristopher Michael Kowal
// https://github.com/kriskowal/es5-shim
// ES5 15.5.4.20
// http://es5.github.com/#x15.5.4.20
var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
"\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
"\u2029\uFEFF";
if (!String.prototype.trim || ws.trim()) {
// http://blog.stevenlevithan.com/archives/faster-trim-javascript
// http://perfectionkills.com/whitespace-deviations/
ws = "[" + ws + "]";
var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
trimEndRegexp = new RegExp(ws + ws + "*$");
String.prototype.trim = function trim() {
if (this === undefined || this === null) {
throw new TypeError("can't convert " + this + " to object");
}
return String(this)
.replace(trimBeginRegexp, "")
.replace(trimEndRegexp, "");
};
}
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;
}
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
Formatter as long as the fromRaw(rawData) and toRaw(formattedData) methods
are defined.
@abstract
@class Backgrid.CellFormatter
@constructor
*/
var CellFormatter = Backgrid.CellFormatter = function () {};
_.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;
},
/**
Takes a formatted string, usually from user input, and returns a
appropriately typed value for persistence in the model.
If the user input is invalid or unable to be converted to a raw value
suitable for persistence in the model, toRaw must return `undefined`.
@member Backgrid.CellFormatter
@param {string} formattedData
@return {*|undefined}
*/
toRaw: function (formattedData) {
return formattedData;
}
});
/**
A floating point number formatter. Doesn't understand notation at the moment.
@class Backgrid.NumberFormatter
@extends Backgrid.CellFormatter
@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");
}
};
NumberFormatter.prototype = new CellFormatter;
_.extend(NumberFormatter.prototype, {
/**
@member Backgrid.NumberFormatter
@cfg {Object} options
@cfg {number} [options.decimals=2] Number of decimals to display. Must be an integer.
@cfg {string} [options.decimalSeparator='.'] The separator to use when
displaying decimals.
@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,
/**
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
the usual way.
@member Backgrid.NumberFormatter
@param {number} number
@return {string}
*/
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.
@member Backgrid.NumberFormatter
@param {string} formattedData
@return {number|undefined} Undefined if the string cannot be converted to
a number.
*/
toRaw: function (formattedData) {
var rawData = '';
var thousands = formattedData.trim().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
Backgrid.Extension.MomentFormatter if you need a much more flexible datetime
formatter.
@class Backgrid.DatetimeFormatter
@extends Backgrid.CellFormatter
@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");
}
};
DatetimeFormatter.prototype = new CellFormatter;
_.extend(DatetimeFormatter.prototype, {
/**
@member Backgrid.DatetimeFormatter
@cfg {Object} options
@cfg {boolean} [options.includeDate=true] Whether the values include the
date part.
@cfg {boolean} [options.includeTime=true] Whether the values include the
time part.
@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) {
if (_.isNull(data) || _.isUndefined(data)) return data;
data = data.trim();
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);
}
}
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.
@member Backgrid.DatetimeFormatter
@param {string} rawData
@return {string|null|undefined} ISO-8601 string in UTC. Null and undefined values are returned as is.
*/
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
Backgrid.Extension.MomentFormatter#fromRaw(), in addition to doing some
sanity checks.
@member Backgrid.DatetimeFormatter
@param {string} formattedData
@return {string|undefined} ISO-8601 string in UTC. Undefined if a date is
found `includeDate` is false, or a time is found if `includeTime` is false,
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);
}
});
/*
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.
@abstract
@class Backgrid.CellEditor
@extends Backbone.View
*/
var CellEditor = Backgrid.CellEditor = Backbone.View.extend({
/**
Initializer.
@param {Object} options
@param {*} options.parent
@param {Backgrid.CellFormatter} options.formatter
@param {Backgrid.Column} options.column
@param {Backbone.Model} options.model
@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);
}
},
/**
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;
}
});
/**
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.
@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"
},
/**
Initializer. Removes this `el` from the DOM when a `done` event is
triggered.
@param {Object} options
@param {Backgrid.CellFormatter} options.formatter
@param {Backgrid.Column} options.column
@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);
},
/**
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;
},
/**
If the key pressed is `enter` or `tab`, converts the value in the editor to
a raw value for the model using the formatter.
If the key pressed is `esc` the changes are undone.
If the editor's value was changed and goes out of focus (`blur`), the event
is intercepted, cancelled so the cell remains in focus pending for further
action.
Triggers a Backbone `done` event when successful. `error` if the value
cannot be converted. Classes listening to the `error` event, usually the
Cell classes, should respond appropriately, usually by rendering some kind
of error feedback.
@param {Event} e
*/
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 {
this.trigger("done");
}
}
// esc
else if (e.keyCode === 27) {
// undo
e.stopPropagation();
this.trigger("done");
}
}
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
editor mode, which is rendered by a Backgrid.InputCellEditor instance by
default. Upon any formatting errors, this class will add a `error` CSS class
to the table cell.
@abstract
@class Backgrid.Cell
@extends Backbone.View
*/
var Cell = Backgrid.Cell = Backbone.View.extend({
/** @property */
tagName: "td",
/**
@property {Backgrid.CellFormatter|Object|string} [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"
},
/**
Initializer.
@param {Object} options
@param {Backbone.Model} options.model
@param {Backgrid.Column} options.column
@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");
this.listenTo(this.model, "change:" + this.column.get("name"), function () {
if (!this.$el.hasClass("editor")) this.render();
});
},
/**
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;
},
/**
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
triggers an `error` event, it means the editor is unable to convert the
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
});
/**
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.
@event edit
@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");
/**
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);
}
},
/**
Put an `error` CSS class on the table cell.
*/
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);
this.currentEditor.remove();
delete this.currentEditor;
this.$el.removeClass("editor");
this.render();
this.delegateEvents();
},
/**
Clean up this cell.
@chainable
*/
remove: function () {
if (this.currentEditor) {
this.currentEditor.remove.apply(this, arguments);
delete this.currentEditor;
}
return Backbone.View.prototype.remove.apply(this, arguments);
}
});
/**
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"
// 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.
@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 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.
@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 (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",
/**
@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,
/**
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
});
}
});
/**
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.
@class Backgrid.IntegerCell
@extends Backgrid.NumberCell
*/
var IntegerCell = Backgrid.IntegerCell = NumberCell.extend({
/** @property */
className: "integer-cell",
/**
@property {number} decimals Must be an integer.
*/
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
look at the Backgrid.Extension.MomentCell extension.
@class Backgrid.DatetimeCell
@extends Backgrid.Cell
See:
- Backgrid.Extension.MomentCell
- Backgrid.DatetimeFormatter
*/
var DatetimeCell = Backgrid.DatetimeCell = Cell.extend({
/** @property */
className: "datetime-cell",
/**
@property {boolean} [includeDate=true]
*/
includeDate: DatetimeFormatter.prototype.defaults.includeDate,
/**
@property {boolean} [includeTime=true]
*/
includeTime: DatetimeFormatter.prototype.defaults.includeTime,
/**
@property {boolean} [includeMilli=false]
*/
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
})
});
}
});
/**
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
});
/**
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
});
/**
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.
@class Backgrid.BooleanCell
@extends Backgrid.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(" />'"),
/**
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"
},
/**
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;
},
/**
Simple focuses the checkbox and add an `editor` CSS class to the cell.
*/
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");
},
/**
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);
}
});
/**
SelectCellEditor renders an HTML `