PageRenderTime 59ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/header.js

https://github.com/antonyraj/backgrid
JavaScript | 299 lines | 140 code | 34 blank | 125 comment | 40 complexity | 6d1e5b8ed91d615019fb8b92462b66a8 MD5 | raw file
Possible License(s): MIT
  1. /*
  2. backgrid
  3. http://github.com/wyuenho/backgrid
  4. Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  5. Licensed under the MIT @license.
  6. */
  7. /**
  8. HeaderCell is a special cell class that renders a column header cell. If the
  9. column is sortable, a sorter is also rendered and will trigger a table
  10. refresh after sorting.
  11. @class Backgrid.HeaderCell
  12. @extends Backbone.View
  13. */
  14. var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
  15. /** @property */
  16. tagName: "th",
  17. /** @property */
  18. events: {
  19. "click a": "onClick"
  20. },
  21. /**
  22. @property {null|"ascending"|"descending"} _direction The current sorting
  23. direction of this column.
  24. */
  25. _direction: null,
  26. /**
  27. Initializer.
  28. @param {Object} options
  29. @param {Backgrid.Column|Object} options.column
  30. @throws {TypeError} If options.column or options.collection is undefined.
  31. */
  32. initialize: function (options) {
  33. Backgrid.requireOptions(options, ["column", "collection"]);
  34. this.column = options.column;
  35. if (!(this.column instanceof Column)) {
  36. this.column = new Column(this.column);
  37. }
  38. this.listenTo(this.collection, "backgrid:sort", this._resetCellDirection);
  39. },
  40. /**
  41. Gets or sets the direction of this cell. If called directly without
  42. parameters, returns the current direction of this cell, otherwise sets
  43. it. If a `null` is given, sets this cell back to the default order.
  44. @param {null|"ascending"|"descending"} dir
  45. @return {null|string} The current direction or the changed direction.
  46. */
  47. direction: function (dir) {
  48. if (arguments.length) {
  49. if (this._direction) this.$el.removeClass(this._direction);
  50. if (dir) this.$el.addClass(dir);
  51. this._direction = dir;
  52. }
  53. return this._direction;
  54. },
  55. /**
  56. Event handler for the Backbone `backgrid:sort` event. Resets this cell's
  57. direction to default if sorting is being done on another column.
  58. @private
  59. */
  60. _resetCellDirection: function (sortByColName, direction, comparator, collection) {
  61. if (collection == this.collection) {
  62. if (sortByColName !== this.column.get("name")) this.direction(null);
  63. else this.direction(direction);
  64. }
  65. },
  66. /**
  67. Event handler for the `click` event on the cell's anchor. If the column is
  68. sortable, clicking on the anchor will cycle through 3 sorting orderings -
  69. `ascending`, `descending`, and default.
  70. */
  71. onClick: function (e) {
  72. e.preventDefault();
  73. var column = this.column;
  74. var columnName = column.get("name");
  75. var sortable = Backgrid.callByNeed(column.get("sortable"), column, this.model);
  76. if (sortable) {
  77. if (this.direction() === "ascending") {
  78. this.sort(columnName, "descending", function (left, right) {
  79. var leftVal = left.get(columnName);
  80. var rightVal = right.get(columnName);
  81. if (leftVal === rightVal) {
  82. return 0;
  83. }
  84. else if (leftVal > rightVal) { return -1; }
  85. return 1;
  86. });
  87. }
  88. else if (this.direction() === "descending") {
  89. this.sort(columnName, null);
  90. }
  91. else {
  92. this.sort(columnName, "ascending", function (left, right) {
  93. var leftVal = left.get(columnName);
  94. var rightVal = right.get(columnName);
  95. if (leftVal === rightVal) {
  96. return 0;
  97. }
  98. else if (leftVal < rightVal) { return -1; }
  99. return 1;
  100. });
  101. }
  102. }
  103. },
  104. /**
  105. If the underlying collection is a Backbone.PageableCollection in
  106. server-mode or infinite-mode, a page of models is fetched after sorting is
  107. done on the server.
  108. If the underlying collection is a Backbone.PageableCollection in
  109. client-mode, or any
  110. [Backbone.Collection](http://backbonejs.org/#Collection) instance, sorting
  111. is done on the client side. If the collection is an instance of a
  112. Backbone.PageableCollection, sorting will be done globally on all the pages
  113. and the current page will then be returned.
  114. Triggers a Backbone `backgrid:sort` event from the collection when done
  115. with the column name, direction, comparator and a reference to the
  116. collection.
  117. @param {string} columnName
  118. @param {null|"ascending"|"descending"} direction
  119. @param {function(*, *): number} [comparator]
  120. See [Backbone.Collection#comparator](http://backbonejs.org/#Collection-comparator)
  121. */
  122. sort: function (columnName, direction, comparator) {
  123. comparator = comparator || this._cidComparator;
  124. var collection = this.collection;
  125. if (Backbone.PageableCollection && collection instanceof Backbone.PageableCollection) {
  126. var order;
  127. if (direction === "ascending") order = -1;
  128. else if (direction === "descending") order = 1;
  129. else order = null;
  130. collection.setSorting(order ? columnName : null, order);
  131. if (collection.mode == "client") {
  132. if (!collection.fullCollection.comparator) {
  133. collection.fullCollection.comparator = comparator;
  134. }
  135. collection.fullCollection.sort();
  136. }
  137. else collection.fetch({reset: true});
  138. }
  139. else {
  140. collection.comparator = comparator;
  141. collection.sort();
  142. }
  143. this.collection.trigger("backgrid:sort", columnName, direction, comparator, this.collection);
  144. },
  145. /**
  146. Default comparator for Backbone.Collections. Sorts cids in ascending
  147. order. The cids of the models are assumed to be in insertion order.
  148. @private
  149. @param {*} left
  150. @param {*} right
  151. */
  152. _cidComparator: function (left, right) {
  153. var lcid = left.cid, rcid = right.cid;
  154. if (!_.isUndefined(lcid) && !_.isUndefined(rcid)) {
  155. lcid = lcid.slice(1) * 1, rcid = rcid.slice(1) * 1;
  156. if (lcid < rcid) return -1;
  157. else if (lcid > rcid) return 1;
  158. }
  159. return 0;
  160. },
  161. /**
  162. Renders a header cell with a sorter and a label.
  163. */
  164. render: function () {
  165. this.$el.empty();
  166. var $label = $("<a>").text(this.column.get("label"));
  167. var sortable = Backgrid.callByNeed(this.column.get("sortable"), this.column, this.model);
  168. if (sortable) $label.append("<b class='sort-caret'></b>");
  169. this.$el.append($label);
  170. this.delegateEvents();
  171. return this;
  172. }
  173. });
  174. /**
  175. HeaderRow is a controller for a row of header cells.
  176. @class Backgrid.HeaderRow
  177. @extends Backgrid.Row
  178. */
  179. var HeaderRow = Backgrid.HeaderRow = Backgrid.Row.extend({
  180. requiredOptions: ["columns", "collection"],
  181. /**
  182. Initializer.
  183. @param {Object} options
  184. @param {Backbone.Collection.<Backgrid.Column>|Array.<Backgrid.Column>|Array.<Object>} options.columns
  185. @param {Backgrid.HeaderCell} [options.headerCell] Customized default
  186. HeaderCell for all the columns. Supply a HeaderCell class or instance to a
  187. the `headerCell` key in a column definition for column-specific header
  188. rendering.
  189. @throws {TypeError} If options.columns or options.collection is undefined.
  190. */
  191. initialize: function () {
  192. Backgrid.Row.prototype.initialize.apply(this, arguments);
  193. },
  194. makeCell: function (column, options) {
  195. var headerCell = column.get("headerCell") || options.headerCell || HeaderCell;
  196. headerCell = new headerCell({
  197. column: column,
  198. collection: this.collection
  199. });
  200. return headerCell;
  201. }
  202. });
  203. /**
  204. Header is a special structural view class that renders a table head with a
  205. single row of header cells.
  206. @class Backgrid.Header
  207. @extends Backbone.View
  208. */
  209. var Header = Backgrid.Header = Backbone.View.extend({
  210. /** @property */
  211. tagName: "thead",
  212. /**
  213. Initializer. Initializes this table head view to contain a single header
  214. row view.
  215. @param {Object} options
  216. @param {Backbone.Collection.<Backgrid.Column>|Array.<Backgrid.Column>|Array.<Object>} options.columns Column metadata.
  217. @param {Backbone.Model} options.model The model instance to render.
  218. @throws {TypeError} If options.columns or options.model is undefined.
  219. */
  220. initialize: function (options) {
  221. Backgrid.requireOptions(options, ["columns", "collection"]);
  222. this.columns = options.columns;
  223. if (!(this.columns instanceof Backbone.Collection)) {
  224. this.columns = new Columns(this.columns);
  225. }
  226. this.row = new Backgrid.HeaderRow({
  227. columns: this.columns,
  228. collection: this.collection
  229. });
  230. },
  231. /**
  232. Renders this table head with a single row of header cells.
  233. */
  234. render: function () {
  235. this.$el.append(this.row.render().$el);
  236. this.delegateEvents();
  237. return this;
  238. },
  239. /**
  240. Clean up this header and its row.
  241. @chainable
  242. */
  243. remove: function () {
  244. this.row.remove.apply(this.row, arguments);
  245. return Backbone.View.prototype.remove.apply(this, arguments);
  246. }
  247. });