/src/header.js
JavaScript | 308 lines | 147 code | 43 blank | 118 comment | 42 complexity | 7f257939d1d7a21606c56dd9a2622305 MD5 | raw file
Possible License(s): MIT
- /*
- backgrid
- http://github.com/wyuenho/backgrid
- Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
- Licensed under the MIT license.
- */
- /**
- HeaderCell is a special cell class that renders a column header cell. If the
- column is sortable, a sorter is also rendered and will trigger a table
- refresh after sorting.
- @class Backgrid.HeaderCell
- @extends Backbone.View
- */
- var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
- /** @property */
- tagName: "th",
- /** @property */
- events: {
- "click a": "onClick"
- },
- /**
- @property {null|"ascending"|"descending"} _direction The current sorting
- direction of this column.
- */
- _direction: null,
- /**
- Initializer.
- @param {Object} options
- @param {Backgrid.Column|Object} options.column
- @throws {TypeError} If options.column or options.collection is undefined.
- */
- initialize: function (options) {
- Backgrid.requireOptions(options, ["column", "collection"]);
- this.column = options.column;
- if (!(this.column instanceof Column)) {
- this.column = new Column(this.column);
- }
- this.listenTo(this.collection, "backgrid:sort", this._resetCellDirection);
- var column = this.column, $el = this.$el;
- this.listenTo(column, "change:editable change:sortable change:renderable",
- function (column) {
- var changed = column.changedAttributes();
- for (var key in changed) {
- if (changed.hasOwnProperty(key)) {
- $el.toggleClass(key, changed[key]);
- }
- }
- });
- if (column.get("editable")) $el.addClass("editable");
- if (column.get("sortable")) $el.addClass("sortable");
- if (column.get("renderable")) $el.addClass("renderable");
- },
- /**
- Gets or sets the direction of this cell. If called directly without
- parameters, returns the current direction of this cell, otherwise sets
- it. If a `null` is given, sets this cell back to the default order.
- @param {null|"ascending"|"descending"} dir
- @return {null|string} The current direction or the changed direction.
- */
- direction: function (dir) {
- if (arguments.length) {
- if (this._direction) this.$el.removeClass(this._direction);
- if (dir) this.$el.addClass(dir);
- this._direction = dir;
- }
- return this._direction;
- },
- /**
- Event handler for the Backbone `backgrid:sort` event. Resets this cell's
- direction to default if sorting is being done on another column.
- @private
- */
- _resetCellDirection: function (columnToSort, direction, comparator, collection) {
- if (collection == this.collection) {
- if (columnToSort !== this.column) this.direction(null);
- else this.direction(direction);
- }
- },
- /**
- Event handler for the `click` event on the cell's anchor. If the column is
- sortable, clicking on the anchor will cycle through 3 sorting orderings -
- `ascending`, `descending`, and default.
- */
- onClick: function (e) {
- e.preventDefault();
- function cycleSort(header, col) {
- if (header.direction() === "ascending") header.sort(col, "descending");
- else if (header.direction() === "descending") header.sort(col, null);
- else header.sort(col, "ascending");
- }
- function toggleSort(header, col) {
- if (header.direction() === "ascending") header.sort(col, "descending");
- else header.sort(col, "ascending");
- }
- var column = this.column;
- var sortable = Backgrid.callByNeed(column.sortable(), column, this.model);
- if (sortable) {
- var sortType = column.get("sortType");
- if (sortType === "toggle") toggleSort(this, column);
- else cycleSort(this, column);
- }
- },
- /**
- If the underlying collection is a Backbone.PageableCollection in
- server-mode or infinite-mode, a page of models is fetched after sorting is
- done on the server.
- If the underlying collection is a Backbone.PageableCollection in
- client-mode, or any
- [Backbone.Collection](http://backbonejs.org/#Collection) instance, sorting
- is done on the client side. If the collection is an instance of a
- Backbone.PageableCollection, sorting will be done globally on all the pages
- and the current page will then be returned.
- Triggers a Backbone `backgrid:sort` event from the collection when done
- with the column, direction, comparator and a reference to the collection.
- @param {Backgrid.Column} column
- @param {null|"ascending"|"descending"} direction
- See [Backbone.Collection#comparator](http://backbonejs.org/#Collection-comparator)
- */
- sort: function (column, direction) {
- var collection = this.collection;
- var order;
- if (direction === "ascending") order = -1;
- else if (direction === "descending") order = 1;
- else order = null;
- var comparator = this.makeComparator(column.get("name"), order,
- order ?
- column.sortValue() :
- function (model) {
- return model.cid;
- });
- if (Backbone.PageableCollection &&
- collection instanceof Backbone.PageableCollection) {
- collection.setSorting(order && column.get("name"), order,
- {sortValue: column.sortValue()});
- if (collection.mode == "client") {
- if (collection.fullCollection.comparator == null) {
- collection.fullCollection.comparator = comparator;
- }
- collection.fullCollection.sort();
- }
- else collection.fetch({reset: true});
- }
- else {
- collection.comparator = comparator;
- collection.sort();
- }
- this.collection.trigger("backgrid:sort", column, direction, comparator,
- this.collection);
- },
- makeComparator: function (attr, order, func) {
- return function (left, right) {
- // extract the values from the models
- var l = func(left, attr), r = func(right, attr), t;
- // if descending order, swap left and right
- if (order === 1) t = l, l = r, r = t;
- // compare as usual
- if (l === r) return 0;
- else if (l < r) return -1;
- return 1;
- };
- },
- /**
- Renders a header cell with a sorter and a label.
- */
- render: function () {
- this.$el.empty();
- var $label = $("<a>").text(this.column.get("label"));
- var sortable = Backgrid.callByNeed(this.column.sortable(), this.column, this.model);
- if (sortable) $label.append("<b class='sort-caret'></b>");
- this.$el.append($label);
- this.delegateEvents();
- return this;
- }
- });
- /**
- HeaderRow is a controller for a row of header cells.
- @class Backgrid.HeaderRow
- @extends Backgrid.Row
- */
- var HeaderRow = Backgrid.HeaderRow = Backgrid.Row.extend({
- requiredOptions: ["columns", "collection"],
- /**
- Initializer.
- @param {Object} options
- @param {Backbone.Collection.<Backgrid.Column>|Array.<Backgrid.Column>|Array.<Object>} options.columns
- @param {Backgrid.HeaderCell} [options.headerCell] Customized default
- HeaderCell for all the columns. Supply a HeaderCell class or instance to a
- the `headerCell` key in a column definition for column-specific header
- rendering.
- @throws {TypeError} If options.columns or options.collection is undefined.
- */
- initialize: function () {
- Backgrid.Row.prototype.initialize.apply(this, arguments);
- },
- makeCell: function (column, options) {
- var headerCell = column.get("headerCell") || options.headerCell || HeaderCell;
- headerCell = new headerCell({
- column: column,
- collection: this.collection
- });
- return headerCell;
- }
- });
- /**
- Header is a special structural view class that renders a table head with a
- single row of header cells.
- @class Backgrid.Header
- @extends Backbone.View
- */
- var Header = Backgrid.Header = Backbone.View.extend({
- /** @property */
- tagName: "thead",
- /**
- Initializer. Initializes this table head view to contain a single header
- row view.
- @param {Object} options
- @param {Backbone.Collection.<Backgrid.Column>|Array.<Backgrid.Column>|Array.<Object>} options.columns Column metadata.
- @param {Backbone.Model} options.model The model instance to render.
- @throws {TypeError} If options.columns or options.model is undefined.
- */
- initialize: function (options) {
- Backgrid.requireOptions(options, ["columns", "collection"]);
- this.columns = options.columns;
- if (!(this.columns instanceof Backbone.Collection)) {
- this.columns = new Columns(this.columns);
- }
- this.row = new Backgrid.HeaderRow({
- columns: this.columns,
- collection: this.collection
- });
- },
- /**
- Renders this table head with a single row of header cells.
- */
- render: function () {
- this.$el.append(this.row.render().$el);
- this.delegateEvents();
- return this;
- },
- /**
- Clean up this header and its row.
- @chainable
- */
- remove: function () {
- this.row.remove.apply(this.row, arguments);
- return Backbone.View.prototype.remove.apply(this, arguments);
- }
- });