PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/libcommon/DTK/dgrid/List.js

https://bitbucket.org/sasha.firsov/dojoplay2012
JavaScript | 732 lines | 483 code | 82 blank | 167 comment | 130 complexity | ce52aa4d9e8d8d6e79d4dbbd92d5e585 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, Apache-2.0, MIT
  1. define(["dojo/_base/array","dojo/_base/kernel", "dojo/_base/declare", "dojo/on", "dojo/has", "./util/misc", "dojo/has!touch?./TouchScroll", "xstyle/has-class", "put-selector/put", "dojo/_base/sniff", "xstyle/css!./css/dgrid.css"],
  2. function(arrayUtil, kernel, declare, listen, has, miscUtil, TouchScroll, hasClass, put){
  3. // Add user agent/feature CSS classes
  4. hasClass("mozilla", "opera", "webkit", "ie", "ie-6", "ie-6-7", "quirks", "no-quirks", "touch");
  5. var scrollbarWidth;
  6. // establish an extra stylesheet which addCssRule calls will use,
  7. // plus an array to track actual indices in stylesheet for removal
  8. var
  9. extraSheet = put(document.getElementsByTagName("head")[0], "style"),
  10. extraRules = [],
  11. oddClass = "dgrid-row-odd",
  12. evenClass = "dgrid-row-even";
  13. // keep reference to actual StyleSheet object (.styleSheet for IE < 9)
  14. extraSheet = extraSheet.sheet || extraSheet.styleSheet;
  15. // functions for adding and removing extra style rules.
  16. // addExtraRule is exposed on the List prototype as addCssRule.
  17. function addExtraRule(selector, css){
  18. var index = extraRules.length;
  19. extraRules[index] = (extraSheet.cssRules || extraSheet.rules).length;
  20. extraSheet.addRule ?
  21. extraSheet.addRule(selector, css) :
  22. extraSheet.insertRule(selector + '{' + css + '}', extraRules[index]);
  23. return {
  24. remove: function(){ removeExtraRule(index); }
  25. };
  26. }
  27. function removeExtraRule(index){
  28. var
  29. realIndex = extraRules[index],
  30. i, l = extraRules.length;
  31. if (realIndex === undefined) { return; } // already removed
  32. // remove rule indicated in internal array at index
  33. extraSheet.deleteRule ?
  34. extraSheet.deleteRule(realIndex) :
  35. extraSheet.removeRule(realIndex); // IE < 9
  36. // Clear internal array item representing rule that was just deleted.
  37. // NOTE: we do NOT splice, since the point of this array is specifically
  38. // to negotiate the splicing that occurs in the stylesheet itself!
  39. extraRules[index] = undefined;
  40. // Then update array items as necessary to downshift remaining rule indices.
  41. // Can start at index, since array is sparse but strictly increasing.
  42. for(i = index; i < l; i++){
  43. if(extraRules[i] > realIndex){ extraRules[i]--; }
  44. }
  45. }
  46. function byId(id){
  47. return document.getElementById(id);
  48. }
  49. function move(item, steps, targetClass, visible){
  50. var nextSibling, current, element;
  51. element = current = item.element;
  52. steps = steps || 1;
  53. do{
  54. // move in the correct direction
  55. if(nextSibling = current[steps < 0 ? 'previousSibling' : 'nextSibling']){
  56. do{
  57. current = nextSibling;
  58. if(((current && current.className) + ' ').indexOf(targetClass + ' ') > -1){
  59. // it's an element with the correct class name, counts as a real move
  60. element = current;
  61. steps += steps < 0 ? 1 : -1;
  62. break;
  63. }
  64. // if the next sibling isn't a match, drill down to search
  65. }while(nextSibling = (!visible || !current.hidden) && current[steps < 0 ? 'lastChild' : 'firstChild']);
  66. }else if((current = current.parentNode) == this.domNode || (current.className + ' ').indexOf("dgrid-row ") > -1){ // intentional assignment
  67. // we stepped all the way out of the grid, given up now
  68. break;
  69. }
  70. }while(steps);
  71. return element;
  72. }
  73. // var and function for autogenerating ID when one isn't provided
  74. var autogen = 0;
  75. function generateId(){
  76. return "dgrid_" + autogen++;
  77. }
  78. // common functions for class and className setters/getters
  79. // (these are run in instance context)
  80. var spaceRx = / +/g;
  81. function setClass(cls){
  82. // Format input appropriately for use with put...
  83. var putClass = cls ? "." + cls.replace(spaceRx, ".") : "";
  84. // Remove any old classes, and add new ones.
  85. if(this._class){
  86. putClass = "!" + this._class.replace(spaceRx, "!") + putClass;
  87. }
  88. put(this.domNode, putClass);
  89. // Store for later retrieval/removal.
  90. this._class = cls;
  91. }
  92. function getClass(){
  93. return this._class;
  94. }
  95. // window resize event handler, run in context of List instance
  96. var winResizeHandler = has("ie") < 7 && !has("quirks") ? function(){
  97. // IE6 triggers window.resize on any element resize;
  98. // avoid useless calls (and infinite loop if height: auto).
  99. // The measurement logic here is based on dojo/window logic.
  100. var root, w, h, dims;
  101. if(!this._started){ return; } // no sense calling resize yet
  102. root = document.documentElement;
  103. w = root.clientWidth;
  104. h = root.clientHeight;
  105. dims = this._prevWinDims || [];
  106. if(dims[0] !== w || dims[1] !== h){
  107. this.resize();
  108. this._prevWinDims = [w, h];
  109. }
  110. } :
  111. function(){
  112. if(this._started){ this.resize(); }
  113. };
  114. return declare(TouchScroll ? [TouchScroll] : [], {
  115. tabableHeader: false,
  116. // showHeader: Boolean
  117. // Whether to render header (sub)rows.
  118. showHeader: false,
  119. // showFooter: Boolean
  120. // Whether to render footer area. Extensions which display content
  121. // in the footer area should set this to true.
  122. showFooter: false,
  123. // maintainOddEven: Boolean
  124. // Indicates whether to maintain the odd/even classes when new rows are inserted.
  125. // This can be disabled to improve insertion performance if odd/even styling is not employed.
  126. maintainOddEven: true,
  127. postscript: function(params, srcNodeRef){
  128. // perform setup and invoke create in postScript to allow descendants to
  129. // perform logic before create/postCreate happen (a la dijit/_WidgetBase)
  130. var grid = this;
  131. (this._Row = function(id, object, element){
  132. this.id = id;
  133. this.data = object;
  134. this.element = element;
  135. }).prototype.remove = function(){
  136. grid.removeRow(this.element);
  137. };
  138. if(srcNodeRef){
  139. // normalize srcNodeRef and store on instance during create process.
  140. // Doing this in postscript is a bit earlier than dijit would do it,
  141. // but allows subclasses to access it pre-normalized during create.
  142. this.srcNodeRef = srcNodeRef =
  143. srcNodeRef.nodeType ? srcNodeRef : byId(srcNodeRef);
  144. }
  145. this.create(params, srcNodeRef);
  146. },
  147. listType: "list",
  148. create: function(params, srcNodeRef){
  149. var domNode = this.domNode = srcNodeRef || put("div"),
  150. cls;
  151. if(params){
  152. this.params = params;
  153. declare.safeMixin(this, params);
  154. // Check for initial class or className in params or on domNode
  155. cls = params["class"] || params.className || domNode.className;
  156. // handle sort param - TODO: revise @ 1.0 when _sort -> sort
  157. this._sort = params.sort || [];
  158. delete this.sort; // ensure back-compat method isn't shadowed
  159. }else{
  160. this._sort = [];
  161. }
  162. // ensure arrays and hashes are initialized
  163. this.observers = [];
  164. this._listeners = [];
  165. this._rowIdToObject = {};
  166. this.postMixInProperties && this.postMixInProperties();
  167. // Apply id to widget and domNode,
  168. // from incoming node, widget params, or autogenerated.
  169. this.id = domNode.id = domNode.id || this.id || generateId();
  170. // Perform initial rendering, and apply classes if any were specified.
  171. this.buildRendering();
  172. if(cls){ setClass.call(this, cls); }
  173. this.postCreate && this.postCreate();
  174. // remove srcNodeRef instance property post-create
  175. delete this.srcNodeRef;
  176. // to preserve "it just works" behavior, call startup if we're visible
  177. if(this.domNode.offsetHeight){
  178. this.startup();
  179. }
  180. },
  181. buildRendering: function(){
  182. var domNode = this.domNode,
  183. headerNode, spacerNode, bodyNode, footerNode, isRTL;
  184. // Detect RTL on html/body nodes; taken from dojo/dom-geometry
  185. isRTL = this.isRTL = (document.body.dir || document.documentElement.dir ||
  186. document.body.style.direction).toLowerCase() == "rtl";
  187. // Clear out className (any pre-applied classes will be re-applied via the
  188. // class / className setter), then apply standard classes/attributes
  189. domNode.className = "";
  190. put(domNode, "[role=grid].ui-widget.dgrid.dgrid-" + this.listType);
  191. headerNode = this.headerNode = put(domNode,
  192. "div.dgrid-header.dgrid-header-row.ui-widget-header" +
  193. (this.showHeader ? "" : ".dgrid-header-hidden"));
  194. if(has("quirks") || has("ie") < 8){
  195. spacerNode = put(domNode, "div.dgrid-spacer");
  196. }
  197. bodyNode = this.bodyNode = this.touchNode = put(domNode, "div.dgrid-scroller");
  198. // firefox 4 until at least 10 adds overflow: auto elements to the tab index by default for some
  199. // reason; force them to be not tabbable
  200. bodyNode.tabIndex = -1;
  201. this.headerScrollNode = put(domNode, "div.dgrid-header-scroll.dgrid-scrollbar-width.ui-widget-header");
  202. footerNode = this.footerNode = put("div.dgrid-footer");
  203. // hide unless showFooter is true (set by extensions which use footer)
  204. if (!this.showFooter) { footerNode.style.display = "none"; }
  205. put(domNode, footerNode);
  206. if(isRTL){
  207. domNode.className += " dgrid-rtl" + (has("webkit") ? "" : " dgrid-rtl-nonwebkit");
  208. }
  209. listen(bodyNode, "scroll", function(event){
  210. // keep the header aligned with the body
  211. headerNode.scrollLeft = bodyNode.scrollLeft;
  212. // re-fire, since browsers are not consistent about propagation here
  213. event.stopPropagation();
  214. listen.emit(domNode, "scroll", {scrollTarget: bodyNode});
  215. });
  216. this.configStructure();
  217. this.renderHeader();
  218. this.contentNode = put(this.bodyNode, "div.dgrid-content.ui-widget-content");
  219. // add window resize handler, with reference for later removal if needed
  220. this._listeners.push(this._resizeHandle = listen(window, "resize",
  221. miscUtil.throttleDelayed(winResizeHandler, this)));
  222. },
  223. startup: function(){
  224. // summary:
  225. // Called automatically after postCreate if the component is already
  226. // visible; otherwise, should be called manually once placed.
  227. this.inherited(arguments);
  228. if(this._started){ return; } // prevent double-triggering
  229. this._started = true;
  230. this.resize();
  231. // apply sort (and refresh) now that we're ready to render
  232. this.set("sort", this._sort);
  233. },
  234. configStructure: function(){
  235. // does nothing in List, this is more of a hook for the Grid
  236. },
  237. resize: function(){
  238. var
  239. bodyNode = this.bodyNode,
  240. headerNode = this.headerNode,
  241. footerNode = this.footerNode,
  242. headerHeight = headerNode.offsetHeight,
  243. footerHeight = this.showFooter ? footerNode.offsetHeight : 0,
  244. quirks = has("quirks") || has("ie") < 7;
  245. this.headerScrollNode.style.height = bodyNode.style.marginTop = headerHeight + "px";
  246. if(footerHeight){ bodyNode.style.marginBottom = footerHeight + "px"; }
  247. if(quirks){
  248. // in IE6 and quirks mode, the "bottom" CSS property is ignored.
  249. // We guard against negative values in case of issues with external CSS.
  250. bodyNode.style.height = ""; // reset first
  251. bodyNode.style.height =
  252. Math.max((this.domNode.offsetHeight - headerHeight - footerHeight), 0) + "px";
  253. if (footerHeight) {
  254. // Work around additional glitch where IE 6 / quirks fails to update
  255. // the position of the bottom-aligned footer; this jogs its memory.
  256. footerNode.style.bottom = '1px';
  257. setTimeout(function(){ footerNode.style.bottom = ''; }, 0);
  258. }
  259. }
  260. if(!scrollbarWidth){
  261. // Measure the browser's scrollbar width using a DIV we'll delete right away
  262. var scrollDiv = put(document.body, "div.dgrid-scrollbar-measure");
  263. scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
  264. put(scrollDiv, "!");
  265. // avoid crazy issues in IE7 only, with certain widgets inside
  266. if(has("ie") === 7){ scrollbarWidth++; }
  267. // add rules that can be used where scrollbar width/height is needed
  268. this.addCssRule(".dgrid-scrollbar-width", "width: " + scrollbarWidth + "px");
  269. this.addCssRule(".dgrid-scrollbar-height", "height: " + scrollbarWidth + "px");
  270. if(scrollbarWidth != 17 && !quirks){
  271. // for modern browsers, we can perform a one-time operation which adds
  272. // a rule to account for scrollbar width in all grid headers.
  273. this.addCssRule(".dgrid-header", "right: " + scrollbarWidth + "px");
  274. // add another for RTL grids
  275. this.addCssRule(".dgrid-rtl-nonwebkit .dgrid-header", "left: " + scrollbarWidth + "px");
  276. }
  277. }
  278. if(quirks){
  279. // old IE doesn't support left + right + width:auto; set width directly
  280. headerNode.style.width = bodyNode.clientWidth + "px";
  281. setTimeout(function(){
  282. // sync up (after the browser catches up with the new width)
  283. headerNode.scrollLeft = bodyNode.scrollLeft;
  284. }, 0);
  285. }
  286. },
  287. addCssRule: addExtraRule,
  288. on: function(eventType, listener){
  289. // delegate events to the domNode
  290. var signal = listen(this.domNode, eventType, listener);
  291. if(!has("dom-addeventlistener")){
  292. this._listeners.push(signal);
  293. }
  294. return signal;
  295. },
  296. cleanup: function(){
  297. // summary:
  298. // Clears out all rows currently in the list.
  299. var observers = this.observers,
  300. i;
  301. for(i in this._rowIdToObject){
  302. if(this._rowIdToObject[i] != this.columns){
  303. var rowElement = byId(i);
  304. if(rowElement){
  305. this.removeRow(rowElement, true);
  306. }
  307. }
  308. }
  309. // remove any store observers
  310. for(i = 0;i < observers.length; i++){
  311. var observer = observers[i];
  312. observer && observer.cancel();
  313. }
  314. this.observers = [];
  315. this.preload = null;
  316. },
  317. destroy: function(){
  318. // summary:
  319. // Destroys this grid
  320. // remove any event listeners
  321. if(this._listeners){ // guard against accidental subsequent calls to destroy
  322. for(var i = this._listeners.length; i--;){
  323. this._listeners[i].remove();
  324. }
  325. delete this._listeners;
  326. }
  327. this.cleanup();
  328. // destroy DOM
  329. put("!", this.domNode);
  330. },
  331. refresh: function(){
  332. // summary:
  333. // refreshes the contents of the grid
  334. this.cleanup();
  335. this._rowIdToObject = {};
  336. this._autoId = 0;
  337. // make sure all the content has been removed so it can be recreated
  338. this.contentNode.innerHTML = "";
  339. },
  340. newRow: function(object, before, to, options){
  341. if(before.parentNode){
  342. var i = options.start + to;
  343. var row = this.insertRow(object, before.parentNode, before, i, options);
  344. put(row, ".ui-state-highlight");
  345. setTimeout(function(){
  346. put(row, "!ui-state-highlight");
  347. }, 250);
  348. return row;
  349. }
  350. },
  351. adjustRowIndices: function(firstRow){
  352. if(this.maintainOddEven){
  353. // this traverses through rows to maintain odd/even classes on the rows when indexes shift;
  354. var next = firstRow;
  355. var rowIndex = next.rowIndex;
  356. do{
  357. if(next.rowIndex > -1){
  358. // skip non-numeric, non-rows
  359. put(next, '.' + (rowIndex % 2 == 1 ? oddClass : evenClass) + '!' + (rowIndex % 2 == 0 ? oddClass : evenClass));
  360. next.rowIndex = rowIndex++;
  361. }
  362. }while((next = next.nextSibling) && next.rowIndex != rowIndex);
  363. }
  364. },
  365. renderArray: function(results, beforeNode, options){
  366. // summary:
  367. // This renders an array or collection of objects as rows in the grid, before the
  368. // given node. This will listen for changes in the collection if an observe method
  369. // is available (as it should be if it comes from an Observable data store).
  370. options = options || {};
  371. var self = this,
  372. start = options.start || 0,
  373. row, rows, container;
  374. if(!beforeNode){
  375. this._lastCollection = results;
  376. }
  377. if(results.observe){
  378. // observe the results for changes
  379. var observerIndex = this.observers.push(results.observe(function(object, from, to){
  380. var firstRow;
  381. // a change in the data took place
  382. if(from > -1 && rows[from]){
  383. // remove from old slot
  384. row = rows.splice(from, 1)[0];
  385. // check to make the sure the node is still there before we try to remove it, (in case it was moved to a different place in the DOM)
  386. if(row.parentNode == container){
  387. firstRow = row.nextSibling;
  388. if(firstRow){ // it's possible for this to have been already removed if it is in overlapping query results
  389. firstRow.rowIndex--; // adjust the rowIndex so adjustRowIndices has the right starting point
  390. self.removeRow(row); // now remove
  391. }
  392. }
  393. // the removal of rows could cause us to need to page in more items
  394. if(self._processScroll){
  395. self._processScroll();
  396. }
  397. }
  398. if(to > -1){
  399. // add to new slot (either before an existing row, or at the end)
  400. row = self.newRow(object, rows[to] || (rows[to-1] && rows[to-1].nextSibling) || beforeNode, to, options);
  401. if(row){
  402. row.observerIndex = observerIndex;
  403. rows.splice(to, 0, row);
  404. if(!firstRow || to < firstRow.rowIndex){
  405. firstRow = row;
  406. }
  407. }
  408. options.count++;
  409. }
  410. from != to && firstRow && self.adjustRowIndices(firstRow);
  411. }, true)) - 1;
  412. }
  413. var rowsFragment = document.createDocumentFragment();
  414. // now render the results
  415. if(results.map){
  416. rows = results.map(mapEach, console.error);
  417. if(rows.then){
  418. return rows.then(whenDone);
  419. }
  420. }else{
  421. rows = [];
  422. for(var i = 0, l = results.length; i < l; i++){
  423. rows[i] = mapEach(results[i]);
  424. }
  425. }
  426. var lastRow;
  427. function mapEach(object){
  428. lastRow = self.insertRow(object, rowsFragment, null, start++, options);
  429. lastRow.observerIndex = observerIndex;
  430. return lastRow;
  431. }
  432. function whenDone(resolvedRows){
  433. container = beforeNode ? beforeNode.parentNode : self.contentNode;
  434. if(container){
  435. container.insertBefore(rowsFragment, beforeNode || null);
  436. lastRow = resolvedRows[resolvedRows.length - 1];
  437. lastRow && self.adjustRowIndices(lastRow);
  438. }
  439. return (rows = resolvedRows);
  440. }
  441. return whenDone(rows);
  442. },
  443. renderHeader: function(){
  444. // no-op in a plain list
  445. },
  446. _autoId: 0,
  447. insertRow: function(object, parent, beforeNode, i, options){
  448. // summary:
  449. // Creates a single row in the grid.
  450. var id = this.id + "-row-" + ((this.store && this.store.getIdentity) ?
  451. this.store.getIdentity(object) : this._autoId++);
  452. var row = byId(id);
  453. if(!row || // we must create a row if it doesn't exist, or if it previously belonged to a different container
  454. (beforeNode && row.parentNode != beforeNode.parentNode)){
  455. if(row){// if it existed elsewhere in the DOM, we will remove it, so we can recreate it
  456. this.removeRow(row);
  457. }
  458. row = this.renderRow(object, options);
  459. row.className = (row.className || "") + " ui-state-default dgrid-row " + (i % 2 == 1 ? oddClass : evenClass);
  460. // get the row id for easy retrieval
  461. this._rowIdToObject[row.id = id] = object;
  462. }
  463. parent.insertBefore(row, beforeNode);
  464. row.rowIndex = i;
  465. return row;
  466. },
  467. renderRow: function(value, options){
  468. // summary:
  469. // Responsible for returning the DOM for a single row in the grid.
  470. return put("div", "" + value);
  471. },
  472. removeRow: function(rowElement, justCleanup){
  473. // summary:
  474. // Simply deletes the node in a plain List.
  475. // Column plugins may aspect this to implement their own cleanup routines.
  476. // rowElement: Object|DOMNode
  477. // Object or element representing the row to be removed.
  478. // justCleanup: Boolean
  479. // If true, the row element will not be removed from the DOM; this can
  480. // be used by extensions/plugins in cases where the DOM will be
  481. // massively cleaned up at a later point in time.
  482. rowElement = rowElement.element || rowElement;
  483. delete this._rowIdToObject[rowElement.id];
  484. if(!justCleanup){
  485. put(rowElement, "!");
  486. }
  487. },
  488. row: function(target){
  489. // summary:
  490. // Get the row object by id, object, node, or event
  491. var id;
  492. if(target instanceof this._Row){ return target; } // no-op; already a row
  493. if(target.target && target.target.nodeType){
  494. // event
  495. target = target.target;
  496. }
  497. if(target.nodeType){
  498. var object;
  499. do{
  500. var rowId = target.id;
  501. if((object = this._rowIdToObject[rowId])){
  502. return new this._Row(rowId.substring(this.id.length + 5), object, target);
  503. }
  504. target = target.parentNode;
  505. }while(target && target != this.domNode);
  506. return;
  507. }
  508. if(typeof target == "object"){
  509. // assume target represents a store item
  510. id = this.store.getIdentity(target);
  511. }else{
  512. // assume target is a row ID
  513. id = target;
  514. target = this._rowIdToObject[this.id + "-row-" + id];
  515. }
  516. return new this._Row(id, target, byId(this.id + "-row-" + id));
  517. },
  518. cell: function(target){
  519. // this doesn't do much in a plain list
  520. return {
  521. row: this.row(target)
  522. };
  523. },
  524. _move: move,
  525. up: function(row, steps, visible){
  526. return this.row(move(row, -(steps || 1), "dgrid-row", visible));
  527. },
  528. down: function(row, steps, visible){
  529. return this.row(move(row, steps || 1, "dgrid-row", visible));
  530. },
  531. get: function(/*String*/ name /*, ... */){
  532. // summary:
  533. // Get a property on a List instance.
  534. // name:
  535. // The property to get.
  536. // returns:
  537. // The property value on this List instance.
  538. // description:
  539. // Get a named property on a List object. The property may
  540. // potentially be retrieved via a getter method in subclasses. In the base class
  541. // this just retrieves the object's property.
  542. var fn = "_get" + name.charAt(0).toUpperCase() + name.slice(1);
  543. if(typeof this[fn] === "function"){
  544. return this[fn].apply(this, [].slice.call(arguments, 1));
  545. }
  546. // Alert users that try to use Dijit-style getter/setters so they don’t get confused
  547. // if they try to use them and it does not work
  548. if(!has("dojo-built") && typeof this[fn + "Attr"] === "function"){
  549. console.warn("dgrid: Use " + fn + " instead of " + fn + "Attr for getting " + name);
  550. }
  551. return this[name];
  552. },
  553. set: function(/*String*/ name, /*Object*/ value /*, ... */){
  554. // summary:
  555. // Set a property on a List instance
  556. // name:
  557. // The property to set.
  558. // value:
  559. // The value to set in the property.
  560. // returns:
  561. // The function returns this List instance.
  562. // description:
  563. // Sets named properties on a List object.
  564. // A programmatic setter may be defined in subclasses.
  565. //
  566. // set() may also be called with a hash of name/value pairs, ex:
  567. // | myObj.set({
  568. // | foo: "Howdy",
  569. // | bar: 3
  570. // | })
  571. // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
  572. if(typeof name === "object"){
  573. for(var k in name){
  574. this.set(k, name[k]);
  575. }
  576. }else{
  577. var fn = "_set" + name.charAt(0).toUpperCase() + name.slice(1);
  578. if(typeof this[fn] === "function"){
  579. this[fn].apply(this, [].slice.call(arguments, 1));
  580. }else{
  581. // Alert users that try to use Dijit-style getter/setters so they don’t get confused
  582. // if they try to use them and it does not work
  583. if(!has("dojo-built") && typeof this[fn + "Attr"] === "function"){
  584. console.warn("dgrid: Use " + fn + " instead of " + fn + "Attr for setting " + name);
  585. }
  586. this[name] = value;
  587. }
  588. }
  589. return this;
  590. },
  591. // Accept both class and className programmatically to set domNode class.
  592. _getClass: getClass,
  593. _setClass: setClass,
  594. _getClassName: getClass,
  595. _setClassName: setClass,
  596. _setSort: function(property, descending){
  597. // summary:
  598. // Sort the content
  599. // property: String|Array
  600. // String specifying field to sort by, or actual array of objects
  601. // with attribute and descending properties
  602. // descending: boolean
  603. // In the case where property is a string, this argument
  604. // specifies whether to sort ascending (false) or descending (true)
  605. this._sort = typeof property != "string" ? property :
  606. [{attribute: property, descending: descending}];
  607. this.refresh();
  608. if(this._lastCollection){
  609. if(property.length){
  610. // if an array was passed in, flatten to just first sort attribute
  611. // for default array sort logic
  612. if(typeof property != "string"){
  613. descending = property[0].descending;
  614. property = property[0].attribute;
  615. }
  616. this._lastCollection.sort(function(a,b){
  617. var aVal = a[property], bVal = b[property];
  618. // fall back undefined values to "" for more consistent behavior
  619. if(aVal === undefined){ aVal = ""; }
  620. if(bVal === undefined){ bVal = ""; }
  621. return aVal == bVal ? 0 : (aVal > bVal == !descending ? 1 : -1);
  622. });
  623. }
  624. this.renderArray(this._lastCollection);
  625. }
  626. },
  627. // TODO: remove the following two (and rename _sort to sort) in 1.0
  628. sort: function(property, descending){
  629. kernel.deprecated("sort(...)", 'use set("sort", ...) instead', "dgrid 1.0");
  630. this.set("sort", property, descending);
  631. },
  632. _getSort: function(){
  633. return this._sort;
  634. },
  635. _setShowHeader: function(show){
  636. // this is in List rather than just in Grid, primarily for two reasons:
  637. // (1) just in case someone *does* want to show a header in a List
  638. // (2) helps address IE < 8 header display issue in List
  639. this.showHeader = show;
  640. // add/remove class which has styles for "hiding" header
  641. put(this.headerNode, (show ? "!" : ".") + "dgrid-header-hidden");
  642. this.renderHeader();
  643. this.resize(); // to account for (dis)appearance of header
  644. },
  645. setShowHeader: function(show){
  646. kernel.deprecated("setShowHeader(...)", 'use set("showHeader", ...) instead', "dgrid 1.0");
  647. this.set("showHeader", show);
  648. }
  649. });
  650. });