Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

datatable-base-debug.js 46KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. YUI.add('datatable-base', function(Y) {
  9. var YLang = Y.Lang,
  10. YisValue = YLang.isValue,
  11. fromTemplate = Y.Lang.sub,
  12. YNode = Y.Node,
  13. Ycreate = YNode.create,
  14. YgetClassName = Y.ClassNameManager.getClassName,
  15. DATATABLE = "datatable",
  16. COLUMN = "column",
  17. FOCUS = "focus",
  18. KEYDOWN = "keydown",
  19. MOUSEENTER = "mouseenter",
  20. MOUSELEAVE = "mouseleave",
  21. MOUSEUP = "mouseup",
  22. MOUSEDOWN = "mousedown",
  23. CLICK = "click",
  24. DBLCLICK = "dblclick",
  25. CLASS_COLUMNS = YgetClassName(DATATABLE, "columns"),
  26. CLASS_DATA = YgetClassName(DATATABLE, "data"),
  27. CLASS_MSG = YgetClassName(DATATABLE, "msg"),
  28. CLASS_LINER = YgetClassName(DATATABLE, "liner"),
  29. CLASS_FIRST = YgetClassName(DATATABLE, "first"),
  30. CLASS_LAST = YgetClassName(DATATABLE, "last"),
  31. CLASS_EVEN = YgetClassName(DATATABLE, "even"),
  32. CLASS_ODD = YgetClassName(DATATABLE, "odd"),
  33. TEMPLATE_TABLE = '<table></table>',
  34. TEMPLATE_COL = '<col></col>',
  35. TEMPLATE_THEAD = '<thead class="'+CLASS_COLUMNS+'"></thead>',
  36. TEMPLATE_TBODY = '<tbody class="'+CLASS_DATA+'"></tbody>',
  37. TEMPLATE_TH = '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}" abbr="{abbr}"><div class="'+CLASS_LINER+'">{value}</div></th>',
  38. TEMPLATE_TR = '<tr id="{id}"></tr>',
  39. TEMPLATE_TD = '<td headers="{headers}" class="{classnames}"><div class="'+CLASS_LINER+'">{value}</div></td>',
  40. TEMPLATE_VALUE = '{value}',
  41. TEMPLATE_MSG = '<tbody class="'+CLASS_MSG+'"></tbody>';
  42. /**
  43. * The Column class defines and manages attributes of Columns for DataTable.
  44. *
  45. * @class Column
  46. * @extends Widget
  47. * @constructor
  48. */
  49. function Column(config) {
  50. Column.superclass.constructor.apply(this, arguments);
  51. }
  52. /////////////////////////////////////////////////////////////////////////////
  53. //
  54. // STATIC PROPERTIES
  55. //
  56. /////////////////////////////////////////////////////////////////////////////
  57. Y.mix(Column, {
  58. /**
  59. * Class name.
  60. *
  61. * @property NAME
  62. * @type {String}
  63. * @static
  64. * @final
  65. * @value "column"
  66. */
  67. NAME: "column",
  68. /////////////////////////////////////////////////////////////////////////////
  69. //
  70. // ATTRIBUTES
  71. //
  72. /////////////////////////////////////////////////////////////////////////////
  73. ATTRS: {
  74. /**
  75. Unique internal identifier, used to stamp ID on TH element.
  76. @attribute id
  77. @type {String}
  78. @readOnly
  79. **/
  80. id: {
  81. valueFn: "_defaultId",
  82. readOnly: true
  83. },
  84. /**
  85. User-supplied identifier. Defaults to id.
  86. @attribute key
  87. @type {String}
  88. **/
  89. key: {
  90. valueFn: "_defaultKey"
  91. },
  92. /**
  93. Points to underlying data field (for sorting or formatting, for
  94. example). Useful when column doesn't hold any data itself, but is just
  95. a visual representation of data from another column or record field.
  96. Defaults to key.
  97. @attribute field
  98. @type {String}
  99. @default (column key)
  100. **/
  101. field: {
  102. valueFn: "_defaultField"
  103. },
  104. /**
  105. Display label for column header. Defaults to key.
  106. @attribute label
  107. @type {String}
  108. **/
  109. label: {
  110. valueFn: "_defaultLabel"
  111. },
  112. /**
  113. Array of child column definitions (for nested headers).
  114. @attribute children
  115. @type {String}
  116. @default null
  117. **/
  118. children: {
  119. value: null
  120. },
  121. /**
  122. TH abbr attribute.
  123. @attribute abbr
  124. @type {String}
  125. @default ""
  126. **/
  127. abbr: {
  128. value: ""
  129. },
  130. //TODO: support custom classnames
  131. // TH CSS classnames
  132. classnames: {
  133. readOnly: true,
  134. getter: "_getClassnames"
  135. },
  136. /**
  137. Formating template string or function for cells in this column.
  138. Function formatters receive a single object (described below) and are
  139. expected to output the `innerHTML` of the cell.
  140. String templates can include markup and {placeholder} tokens to be
  141. filled in from the object passed to function formatters.
  142. @attribute formatter
  143. @type {String|Function}
  144. @param {Object} data Data relevant to the rendering of this cell
  145. @param {String} data.classnames CSS classes to add to the cell
  146. @param {Column} data.column This Column instance
  147. @param {Object} data.data The raw object data from the Record
  148. @param {String} data.field This Column's "field" attribute value
  149. @param {String} data.headers TH ids to reference in the cell's
  150. "headers" attribute
  151. @param {Record} data.record The Record instance for this row
  152. @param {Number} data.rowindex The index for this row
  153. @param {Node} data.tbody The TBODY Node that will house the cell
  154. @param {Node} data.tr The row TR Node that will house the cell
  155. @param {Any} data.value The raw Record data for this cell
  156. **/
  157. formatter: {},
  158. /**
  159. The default markup to display in cells that have no corresponding record
  160. data or content from formatters.
  161. @attribute emptyCellValue
  162. @type {String}
  163. @default ''
  164. **/
  165. emptyCellValue: {
  166. value: '',
  167. validator: Y.Lang.isString
  168. },
  169. //requires datatable-sort
  170. sortable: {
  171. value: false
  172. },
  173. //sortOptions:defaultDir, sortFn, field
  174. //TODO: support editable columns
  175. // Column editor
  176. editor: {},
  177. //TODO: support resizeable columns
  178. //TODO: support setting widths
  179. // requires datatable-colresize
  180. width: {},
  181. resizeable: {},
  182. minimized: {},
  183. minWidth: {},
  184. maxAutoWidth: {}
  185. }
  186. });
  187. /////////////////////////////////////////////////////////////////////////////
  188. //
  189. // PROTOTYPE
  190. //
  191. /////////////////////////////////////////////////////////////////////////////
  192. Y.extend(Column, Y.Widget, {
  193. /////////////////////////////////////////////////////////////////////////////
  194. //
  195. // ATTRIBUTE HELPERS
  196. //
  197. /////////////////////////////////////////////////////////////////////////////
  198. /**
  199. * Return ID for instance.
  200. *
  201. * @method _defaultId
  202. * @return {String}
  203. * @private
  204. */
  205. _defaultId: function() {
  206. return Y.guid();
  207. },
  208. /**
  209. * Return key for instance. Defaults to ID if one was not provided.
  210. *
  211. * @method _defaultKey
  212. * @return {String}
  213. * @private
  214. */
  215. _defaultKey: function(key) {
  216. return key || Y.guid();
  217. },
  218. /**
  219. * Return field for instance. Defaults to key if one was not provided.
  220. *
  221. * @method _defaultField
  222. * @return {String}
  223. * @private
  224. */
  225. _defaultField: function(field) {
  226. return field || this.get("key");
  227. },
  228. /**
  229. * Return label for instance. Defaults to key if one was not provided.
  230. *
  231. * @method _defaultLabel
  232. * @return {String}
  233. * @private
  234. */
  235. _defaultLabel: function(label) {
  236. return label || this.get("key");
  237. },
  238. /**
  239. * Updates the UI if changes are made to abbr.
  240. *
  241. * @method _afterAbbrChange
  242. * @param e {Event} Custom event for the attribute change.
  243. * @private
  244. */
  245. _afterAbbrChange: function (e) {
  246. this._uiSetAbbr(e.newVal);
  247. },
  248. /////////////////////////////////////////////////////////////////////////////
  249. //
  250. // PROPERTIES
  251. //
  252. /////////////////////////////////////////////////////////////////////////////
  253. /**
  254. * Reference to Column's current position index within its Columnset's keys
  255. * array, if applicable. This property only applies to non-nested and bottom-
  256. * level child Columns. Value is set by Columnset code.
  257. *
  258. * @property keyIndex
  259. * @type {Number}
  260. */
  261. keyIndex: null,
  262. /**
  263. * Array of TH IDs associated with this column, for TD "headers" attribute.
  264. * Value is set by Columnset code
  265. *
  266. * @property headers
  267. * @type {String[]}
  268. */
  269. headers: null,
  270. /**
  271. * Number of cells the header spans. Value is set by Columnset code.
  272. *
  273. * @property colSpan
  274. * @type {Number}
  275. * @default 1
  276. */
  277. colSpan: 1,
  278. /**
  279. * Number of rows the header spans. Value is set by Columnset code.
  280. *
  281. * @property rowSpan
  282. * @type {Number}
  283. * @default 1
  284. */
  285. rowSpan: 1,
  286. /**
  287. * Column's parent Column instance, if applicable. Value is set by Columnset
  288. * code.
  289. *
  290. * @property parent
  291. * @type {Column}
  292. */
  293. parent: null,
  294. /**
  295. * The Node reference to the associated TH element.
  296. *
  297. * @property thNode
  298. * @type {Node}
  299. */
  300. thNode: null,
  301. /*TODO
  302. * The Node reference to the associated liner element.
  303. *
  304. * @property thLinerNode
  305. * @type {Node}
  306. thLinerNode: null,*/
  307. /////////////////////////////////////////////////////////////////////////////
  308. //
  309. // METHODS
  310. //
  311. /////////////////////////////////////////////////////////////////////////////
  312. /**
  313. * Initializer.
  314. *
  315. * @method initializer
  316. * @param config {Object} Config object.
  317. * @private
  318. */
  319. initializer: function(config) {
  320. },
  321. /**
  322. * Destructor.
  323. *
  324. * @method destructor
  325. * @private
  326. */
  327. destructor: function() {
  328. },
  329. /**
  330. * Returns classnames for Column.
  331. *
  332. * @method _getClassnames
  333. * @private
  334. */
  335. _getClassnames: function () {
  336. return Y.ClassNameManager.getClassName(COLUMN, this.get("key").replace(/[^\w\-]/g,""));
  337. },
  338. ////////////////////////////////////////////////////////////////////////////
  339. //
  340. // SYNC
  341. //
  342. ////////////////////////////////////////////////////////////////////////////
  343. /**
  344. * Syncs UI to intial state.
  345. *
  346. * @method syncUI
  347. * @private
  348. */
  349. syncUI: function() {
  350. this._uiSetAbbr(this.get("abbr"));
  351. },
  352. /**
  353. * Updates abbr.
  354. *
  355. * @method _uiSetAbbr
  356. * @param val {String} New abbr.
  357. * @protected
  358. */
  359. _uiSetAbbr: function(val) {
  360. this.thNode.set("abbr", val);
  361. }
  362. });
  363. Y.Column = Column;
  364. /**
  365. * The Columnset class defines and manages a collection of Columns.
  366. *
  367. * @class Columnset
  368. * @extends Base
  369. * @constructor
  370. */
  371. function Columnset(config) {
  372. Columnset.superclass.constructor.apply(this, arguments);
  373. }
  374. /////////////////////////////////////////////////////////////////////////////
  375. //
  376. // STATIC PROPERTIES
  377. //
  378. /////////////////////////////////////////////////////////////////////////////
  379. Y.mix(Columnset, {
  380. /**
  381. * Class name.
  382. *
  383. * @property NAME
  384. * @type String
  385. * @static
  386. * @final
  387. * @value "columnset"
  388. */
  389. NAME: "columnset",
  390. /////////////////////////////////////////////////////////////////////////////
  391. //
  392. // ATTRIBUTES
  393. //
  394. /////////////////////////////////////////////////////////////////////////////
  395. ATTRS: {
  396. /**
  397. * @attribute definitions
  398. * @description Array of column definitions that will populate this Columnset.
  399. * @type Array
  400. */
  401. definitions: {
  402. setter: "_setDefinitions"
  403. }
  404. }
  405. });
  406. /////////////////////////////////////////////////////////////////////////////
  407. //
  408. // PROTOTYPE
  409. //
  410. /////////////////////////////////////////////////////////////////////////////
  411. Y.extend(Columnset, Y.Base, {
  412. /////////////////////////////////////////////////////////////////////////////
  413. //
  414. // ATTRIBUTE HELPERS
  415. //
  416. /////////////////////////////////////////////////////////////////////////////
  417. /**
  418. * @method _setDefinitions
  419. * @description Clones definitions before setting.
  420. * @param definitions {Array} Array of column definitions.
  421. * @return Array
  422. * @private
  423. */
  424. _setDefinitions: function(definitions) {
  425. return Y.clone(definitions);
  426. },
  427. /////////////////////////////////////////////////////////////////////////////
  428. //
  429. // PROPERTIES
  430. //
  431. /////////////////////////////////////////////////////////////////////////////
  432. /**
  433. * Top-down tree representation of Column hierarchy. Used to create DOM
  434. * elements.
  435. *
  436. * @property tree
  437. * @type {Column[]}
  438. */
  439. tree: null,
  440. /**
  441. * Hash of all Columns by ID.
  442. *
  443. * @property idHash
  444. * @type Object
  445. */
  446. idHash: null,
  447. /**
  448. * Hash of all Columns by key.
  449. *
  450. * @property keyHash
  451. * @type Object
  452. */
  453. keyHash: null,
  454. /**
  455. * Array of only Columns that are meant to be displayed in DOM.
  456. *
  457. * @property keys
  458. * @type {Column[]}
  459. */
  460. keys: null,
  461. /////////////////////////////////////////////////////////////////////////////
  462. //
  463. // METHODS
  464. //
  465. /////////////////////////////////////////////////////////////////////////////
  466. /**
  467. * Initializer. Generates all internal representations of the collection of
  468. * Columns.
  469. *
  470. * @method initializer
  471. * @param config {Object} Config object.
  472. * @private
  473. */
  474. initializer: function() {
  475. // DOM tree representation of all Columns
  476. var tree = [],
  477. // Hash of all Columns by ID
  478. idHash = {},
  479. // Hash of all Columns by key
  480. keyHash = {},
  481. // Flat representation of only Columns that are meant to display data
  482. keys = [],
  483. // Original definitions
  484. definitions = this.get("definitions"),
  485. self = this;
  486. // Internal recursive function to define Column instances
  487. function parseColumns(depth, currentDefinitions, parent) {
  488. var i=0,
  489. len = currentDefinitions.length,
  490. currentDefinition,
  491. column,
  492. currentChildren;
  493. // One level down
  494. depth++;
  495. // Create corresponding dom node if not already there for this depth
  496. if(!tree[depth]) {
  497. tree[depth] = [];
  498. }
  499. // Parse each node at this depth for attributes and any children
  500. for(; i<len; ++i) {
  501. currentDefinition = currentDefinitions[i];
  502. currentDefinition = YLang.isString(currentDefinition) ? {key:currentDefinition} : currentDefinition;
  503. // Instantiate a new Column for each node
  504. column = new Y.Column(currentDefinition);
  505. // Cross-reference Column ID back to the original object literal definition
  506. currentDefinition.yuiColumnId = column.get("id");
  507. // Add the new Column to the hash
  508. idHash[column.get("id")] = column;
  509. keyHash[column.get("key")] = column;
  510. // Assign its parent as an attribute, if applicable
  511. if(parent) {
  512. column.parent = parent;
  513. }
  514. // The Column has descendants
  515. if(YLang.isArray(currentDefinition.children)) {
  516. currentChildren = currentDefinition.children;
  517. column._set("children", currentChildren);
  518. self._setColSpans(column, currentDefinition);
  519. self._cascadePropertiesToChildren(column, currentChildren);
  520. // The children themselves must also be parsed for Column instances
  521. if(!tree[depth+1]) {
  522. tree[depth+1] = [];
  523. }
  524. parseColumns(depth, currentChildren, column);
  525. }
  526. // This Column does not have any children
  527. else {
  528. column.keyIndex = keys.length;
  529. // Default is already 1
  530. //column.colSpan = 1;
  531. keys.push(column);
  532. }
  533. // Add the Column to the top-down dom tree
  534. tree[depth].push(column);
  535. }
  536. depth--;
  537. }
  538. // Parse out Column instances from the array of object literals
  539. parseColumns(-1, definitions);
  540. // Save to the Columnset instance
  541. this.tree = tree;
  542. this.idHash = idHash;
  543. this.keyHash = keyHash;
  544. this.keys = keys;
  545. this._setRowSpans();
  546. this._setHeaders();
  547. },
  548. /**
  549. * Destructor.
  550. *
  551. * @method destructor
  552. * @private
  553. */
  554. destructor: function() {
  555. },
  556. /////////////////////////////////////////////////////////////////////////////
  557. //
  558. // COLUMN HELPERS
  559. //
  560. /////////////////////////////////////////////////////////////////////////////
  561. /**
  562. * Cascade certain properties to children if not defined on their own.
  563. *
  564. * @method _cascadePropertiesToChildren
  565. * @private
  566. */
  567. _cascadePropertiesToChildren: function(column, currentChildren) {
  568. //TODO: this is all a giant todo
  569. var i = 0,
  570. len = currentChildren.length,
  571. child;
  572. // Cascade certain properties to children if not defined on their own
  573. for(; i<len; ++i) {
  574. child = currentChildren[i];
  575. if(column.get("className") && (child.className === undefined)) {
  576. child.className = column.get("className");
  577. }
  578. if(column.get("editor") && (child.editor === undefined)) {
  579. child.editor = column.get("editor");
  580. }
  581. if(column.get("formatter") && (child.formatter === undefined)) {
  582. child.formatter = column.get("formatter");
  583. }
  584. if(column.get("resizeable") && (child.resizeable === undefined)) {
  585. child.resizeable = column.get("resizeable");
  586. }
  587. if(column.get("sortable") && (child.sortable === undefined)) {
  588. child.sortable = column.get("sortable");
  589. }
  590. if(column.get("hidden")) {
  591. child.hidden = true;
  592. }
  593. if(column.get("width") && (child.width === undefined)) {
  594. child.width = column.get("width");
  595. }
  596. if(column.get("minWidth") && (child.minWidth === undefined)) {
  597. child.minWidth = column.get("minWidth");
  598. }
  599. if(column.get("maxAutoWidth") && (child.maxAutoWidth === undefined)) {
  600. child.maxAutoWidth = column.get("maxAutoWidth");
  601. }
  602. }
  603. },
  604. /**
  605. * @method _setColSpans
  606. * @description Calculates and sets colSpan attribute on given Column.
  607. * @param column {Array} Column instance.
  608. * @param definition {Object} Column definition.
  609. * @private
  610. */
  611. _setColSpans: function(column, definition) {
  612. // Determine COLSPAN value for this Column
  613. var terminalChildNodes = 0;
  614. function countTerminalChildNodes(ancestor) {
  615. var descendants = ancestor.children,
  616. i = 0,
  617. len = descendants.length;
  618. // Drill down each branch and count terminal nodes
  619. for(; i<len; ++i) {
  620. // Keep drilling down
  621. if(YLang.isArray(descendants[i].children)) {
  622. countTerminalChildNodes(descendants[i]);
  623. }
  624. // Reached branch terminus
  625. else {
  626. terminalChildNodes++;
  627. }
  628. }
  629. }
  630. countTerminalChildNodes(definition);
  631. column.colSpan = terminalChildNodes;
  632. },
  633. /**
  634. * @method _setRowSpans
  635. * @description Calculates and sets rowSpan attribute on all Columns.
  636. * @private
  637. */
  638. _setRowSpans: function() {
  639. // Determine ROWSPAN value for each Column in the DOM tree
  640. function parseDomTreeForRowSpan(tree) {
  641. var maxRowDepth = 1,
  642. currentRow,
  643. currentColumn,
  644. m,p;
  645. // Calculate the max depth of descendants for this row
  646. function countMaxRowDepth(row, tmpRowDepth) {
  647. tmpRowDepth = tmpRowDepth || 1;
  648. var i = 0,
  649. len = row.length,
  650. col;
  651. for(; i<len; ++i) {
  652. col = row[i];
  653. // Column has children, so keep counting
  654. if(YLang.isArray(col.children)) {
  655. tmpRowDepth++;
  656. countMaxRowDepth(col.children, tmpRowDepth);
  657. tmpRowDepth--;
  658. }
  659. // Column has children, so keep counting
  660. else if(col.get && YLang.isArray(col.get("children"))) {
  661. tmpRowDepth++;
  662. countMaxRowDepth(col.get("children"), tmpRowDepth);
  663. tmpRowDepth--;
  664. }
  665. // No children, is it the max depth?
  666. else {
  667. if(tmpRowDepth > maxRowDepth) {
  668. maxRowDepth = tmpRowDepth;
  669. }
  670. }
  671. }
  672. }
  673. // Count max row depth for each row
  674. for(m=0; m<tree.length; m++) {
  675. currentRow = tree[m];
  676. countMaxRowDepth(currentRow);
  677. // Assign the right ROWSPAN values to each Column in the row
  678. for(p=0; p<currentRow.length; p++) {
  679. currentColumn = currentRow[p];
  680. if(!YLang.isArray(currentColumn.get("children"))) {
  681. currentColumn.rowSpan = maxRowDepth;
  682. }
  683. // Default is already 1
  684. // else currentColumn.rowSpan =1;
  685. }
  686. // Reset counter for next row
  687. maxRowDepth = 1;
  688. }
  689. }
  690. parseDomTreeForRowSpan(this.tree);
  691. },
  692. /**
  693. * @method _setHeaders
  694. * @description Calculates and sets headers attribute on all Columns.
  695. * @private
  696. */
  697. _setHeaders: function() {
  698. var headers, column,
  699. allKeys = this.keys,
  700. i=0, len = allKeys.length;
  701. function recurseAncestorsForHeaders(headers, column) {
  702. headers.push(column.get("id"));
  703. if(column.parent) {
  704. recurseAncestorsForHeaders(headers, column.parent);
  705. }
  706. }
  707. for(; i<len; ++i) {
  708. headers = [];
  709. column = allKeys[i];
  710. recurseAncestorsForHeaders(headers, column);
  711. column.headers = headers.reverse().join(" ");
  712. }
  713. },
  714. //TODO
  715. getColumn: function() {
  716. }
  717. });
  718. Y.Columnset = Columnset;
  719. /**
  720. * The DataTable widget provides a progressively enhanced DHTML control for
  721. * displaying tabular data across A-grade browsers.
  722. *
  723. * @module datatable
  724. * @main datatable
  725. */
  726. /**
  727. * Provides the base DataTable implementation, which can be extended to add
  728. * additional functionality, such as sorting or scrolling.
  729. *
  730. * @module datatable
  731. * @submodule datatable-base
  732. */
  733. /**
  734. * Base class for the DataTable widget.
  735. * @class DataTable.Base
  736. * @extends Widget
  737. * @constructor
  738. */
  739. function DTBase(config) {
  740. DTBase.superclass.constructor.apply(this, arguments);
  741. }
  742. /////////////////////////////////////////////////////////////////////////////
  743. //
  744. // STATIC PROPERTIES
  745. //
  746. /////////////////////////////////////////////////////////////////////////////
  747. Y.mix(DTBase, {
  748. /**
  749. * Class name.
  750. *
  751. * @property NAME
  752. * @type String
  753. * @static
  754. * @final
  755. * @value "dataTable"
  756. */
  757. NAME: "dataTable",
  758. /////////////////////////////////////////////////////////////////////////////
  759. //
  760. // ATTRIBUTES
  761. //
  762. /////////////////////////////////////////////////////////////////////////////
  763. ATTRS: {
  764. /**
  765. * @attribute columnset
  766. * @description Pointer to Columnset instance.
  767. * @type Array | Y.Columnset
  768. */
  769. columnset: {
  770. setter: "_setColumnset"
  771. },
  772. /**
  773. * @attribute recordset
  774. * @description Pointer to Recordset instance.
  775. * @type Array | Y.Recordset
  776. */
  777. recordset: {
  778. valueFn: '_initRecordset',
  779. setter: "_setRecordset"
  780. },
  781. /*TODO
  782. * @attribute state
  783. * @description Internal state.
  784. * @readonly
  785. * @type
  786. */
  787. /*state: {
  788. value: new Y.State(),
  789. readOnly: true
  790. },*/
  791. /**
  792. * @attribute summary
  793. * @description Summary.
  794. * @type String
  795. */
  796. summary: {
  797. },
  798. /**
  799. * @attribute caption
  800. * @description Caption
  801. * @type String
  802. */
  803. caption: {
  804. },
  805. /**
  806. * @attribute thValueTemplate
  807. * @description Tokenized markup template for TH value.
  808. * @type String
  809. * @default '{value}'
  810. */
  811. thValueTemplate: {
  812. value: TEMPLATE_VALUE
  813. },
  814. /**
  815. * @attribute tdValueTemplate
  816. * @description Tokenized markup template for TD value.
  817. * @type String
  818. * @default '{value}'
  819. */
  820. tdValueTemplate: {
  821. value: TEMPLATE_VALUE
  822. },
  823. /**
  824. * @attribute trTemplate
  825. * @description Tokenized markup template for TR node creation.
  826. * @type String
  827. * @default '<tr id="{id}"></tr>'
  828. */
  829. trTemplate: {
  830. value: TEMPLATE_TR
  831. }
  832. },
  833. /////////////////////////////////////////////////////////////////////////////
  834. //
  835. // TODO: HTML_PARSER
  836. //
  837. /////////////////////////////////////////////////////////////////////////////
  838. HTML_PARSER: {
  839. /*caption: function (srcNode) {
  840. }*/
  841. }
  842. });
  843. /////////////////////////////////////////////////////////////////////////////
  844. //
  845. // PROTOTYPE
  846. //
  847. /////////////////////////////////////////////////////////////////////////////
  848. Y.extend(DTBase, Y.Widget, {
  849. /**
  850. * @property thTemplate
  851. * @description Tokenized markup template for TH node creation.
  852. * @type String
  853. * @default '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}" abbr="{abbr}"><div class="'+CLASS_LINER+'">{value}</div></th>'
  854. */
  855. thTemplate: TEMPLATE_TH,
  856. /**
  857. * @property tdTemplate
  858. * @description Tokenized markup template for TD node creation.
  859. * @type String
  860. * @default '<td headers="{headers}" class="{classnames}"><div class="yui3-datatable-liner">{value}</div></td>'
  861. */
  862. tdTemplate: TEMPLATE_TD,
  863. /**
  864. * @property _theadNode
  865. * @description Pointer to THEAD node.
  866. * @type {Node}
  867. * @private
  868. */
  869. _theadNode: null,
  870. /**
  871. * @property _tbodyNode
  872. * @description Pointer to TBODY node.
  873. * @type {Node}
  874. * @private
  875. */
  876. _tbodyNode: null,
  877. /**
  878. * @property _msgNode
  879. * @description Pointer to message display node.
  880. * @type {Node}
  881. * @private
  882. */
  883. _msgNode: null,
  884. /////////////////////////////////////////////////////////////////////////////
  885. //
  886. // ATTRIBUTE HELPERS
  887. //
  888. /////////////////////////////////////////////////////////////////////////////
  889. /**
  890. * @method _setColumnset
  891. * @description Converts Array to Y.Columnset.
  892. * @param columns {Array | Y.Columnset}
  893. * @return {Columnset}
  894. * @private
  895. */
  896. _setColumnset: function(columns) {
  897. return YLang.isArray(columns) ? new Y.Columnset({definitions:columns}) : columns;
  898. },
  899. /**
  900. * Updates the UI if Columnset is changed.
  901. *
  902. * @method _afterColumnsetChange
  903. * @param e {Event} Custom event for the attribute change.
  904. * @protected
  905. */
  906. _afterColumnsetChange: function (e) {
  907. this._uiSetColumnset(e.newVal);
  908. },
  909. /**
  910. * @method _setRecordset
  911. * @description Converts Array to Y.Recordset.
  912. * @param records {Array | Recordset}
  913. * @return {Recordset}
  914. * @private
  915. */
  916. _setRecordset: function(rs) {
  917. if(YLang.isArray(rs)) {
  918. rs = new Y.Recordset({records:rs});
  919. }
  920. rs.addTarget(this);
  921. return rs;
  922. },
  923. /**
  924. * Updates the UI if Recordset is changed.
  925. *
  926. * @method _afterRecordsetChange
  927. * @param e {Event} Custom event for the attribute change.
  928. * @protected
  929. */
  930. _afterRecordsetChange: function (e) {
  931. this._uiSetRecordset(e.newVal);
  932. },
  933. /**
  934. * Updates the UI if Recordset records are changed.
  935. *
  936. * @method _afterRecordsChange
  937. * @param e {Event} Custom event for the attribute change.
  938. * @protected
  939. */
  940. _afterRecordsChange: function (e) {
  941. this._uiSetRecordset(this.get('recordset'));
  942. },
  943. /**
  944. * Updates the UI if summary is changed.
  945. *
  946. * @method _afterSummaryChange
  947. * @param e {Event} Custom event for the attribute change.
  948. * @protected
  949. */
  950. _afterSummaryChange: function (e) {
  951. this._uiSetSummary(e.newVal);
  952. },
  953. /**
  954. * Updates the UI if caption is changed.
  955. *
  956. * @method _afterCaptionChange
  957. * @param e {Event} Custom event for the attribute change.
  958. * @protected
  959. */
  960. _afterCaptionChange: function (e) {
  961. this._uiSetCaption(e.newVal);
  962. },
  963. ////////////////////////////////////////////////////////////////////////////
  964. //
  965. // METHODS
  966. //
  967. ////////////////////////////////////////////////////////////////////////////
  968. /**
  969. * Destructor.
  970. *
  971. * @method destructor
  972. * @private
  973. */
  974. destructor: function() {
  975. this.get("recordset").removeTarget(this);
  976. },
  977. ////////////////////////////////////////////////////////////////////////////
  978. //
  979. // RENDER
  980. //
  981. ////////////////////////////////////////////////////////////////////////////
  982. /**
  983. * Renders UI.
  984. *
  985. * @method renderUI
  986. * @private
  987. */
  988. renderUI: function() {
  989. // TABLE
  990. this._addTableNode(this.get("contentBox"));
  991. // COLGROUP
  992. this._addColgroupNode(this._tableNode);
  993. // THEAD
  994. this._addTheadNode(this._tableNode);
  995. // Primary TBODY
  996. this._addTbodyNode(this._tableNode);
  997. // Message TBODY
  998. this._addMessageNode(this._tableNode);
  999. // CAPTION
  1000. this._addCaptionNode(this._tableNode);
  1001. },
  1002. /**
  1003. * Creates and attaches TABLE element to given container.
  1004. *
  1005. * @method _addTableNode
  1006. * @param containerNode {Node} Parent node.
  1007. * @protected
  1008. * @return {Node}
  1009. */
  1010. _addTableNode: function(containerNode) {
  1011. if (!this._tableNode) {
  1012. this._tableNode = containerNode.appendChild(Ycreate(TEMPLATE_TABLE));
  1013. }
  1014. return this._tableNode;
  1015. },
  1016. /**
  1017. * Creates and attaches COLGROUP element to given TABLE.
  1018. *
  1019. * @method _addColgroupNode
  1020. * @param tableNode {Node} Parent node.
  1021. * @protected
  1022. * @return {Node}
  1023. */
  1024. _addColgroupNode: function(tableNode) {
  1025. // Add COLs To DoCUMENT FRAGMENT
  1026. var len = this.get("columnset").keys.length,
  1027. i = 0,
  1028. allCols = ["<colgroup>"];
  1029. for(; i<len; ++i) {
  1030. allCols.push(TEMPLATE_COL);
  1031. }
  1032. allCols.push("</colgroup>");
  1033. // Create COLGROUP
  1034. this._colgroupNode = tableNode.insertBefore(Ycreate(allCols.join("")), tableNode.get("firstChild"));
  1035. return this._colgroupNode;
  1036. },
  1037. /**
  1038. * Creates and attaches THEAD element to given container.
  1039. *
  1040. * @method _addTheadNode
  1041. * @param tableNode {Node} Parent node.
  1042. * @protected
  1043. * @return {Node}
  1044. */
  1045. _addTheadNode: function(tableNode) {
  1046. if(tableNode) {
  1047. this._theadNode = tableNode.insertBefore(Ycreate(TEMPLATE_THEAD), this._colgroupNode.next());
  1048. return this._theadNode;
  1049. }
  1050. },
  1051. /**
  1052. * Creates and attaches TBODY element to given container.
  1053. *
  1054. * @method _addTbodyNode
  1055. * @param tableNode {Node} Parent node.
  1056. * @protected
  1057. * @return {Node}
  1058. */
  1059. _addTbodyNode: function(tableNode) {
  1060. this._tbodyNode = tableNode.appendChild(Ycreate(TEMPLATE_TBODY));
  1061. return this._tbodyNode;
  1062. },
  1063. /**
  1064. * Creates and attaches message display element to given container.
  1065. *
  1066. * @method _addMessageNode
  1067. * @param tableNode {Node} Parent node.
  1068. * @protected
  1069. * @return {Node}
  1070. */
  1071. _addMessageNode: function(tableNode) {
  1072. this._msgNode = tableNode.insertBefore(Ycreate(TEMPLATE_MSG), this._tbodyNode);
  1073. return this._msgNode;
  1074. },
  1075. /**
  1076. * Creates and attaches CAPTION element to given container.
  1077. *
  1078. * @method _addCaptionNode
  1079. * @param tableNode {Node} Parent node.
  1080. * @protected
  1081. * @return {Node}
  1082. */
  1083. _addCaptionNode: function(tableNode) {
  1084. this._captionNode = Y.Node.create('<caption></caption>');
  1085. },
  1086. ////////////////////////////////////////////////////////////////////////////
  1087. //
  1088. // BIND
  1089. //
  1090. ////////////////////////////////////////////////////////////////////////////
  1091. /**
  1092. * Binds events.
  1093. *
  1094. * @method bindUI
  1095. * @private
  1096. */
  1097. bindUI: function() {
  1098. this.after({
  1099. columnsetChange: this._afterColumnsetChange,
  1100. summaryChange : this._afterSummaryChange,
  1101. captionChange : this._afterCaptionChange,
  1102. recordsetChange: this._afterRecordsChange,
  1103. "recordset:tableChange": this._afterRecordsChange
  1104. });
  1105. },
  1106. delegate: function(type) {
  1107. //TODO: is this necessary?
  1108. if(type==="dblclick") {
  1109. this.get("boundingBox").delegate.apply(this.get("boundingBox"), arguments);
  1110. }
  1111. else {
  1112. this.get("contentBox").delegate.apply(this.get("contentBox"), arguments);
  1113. }
  1114. },
  1115. ////////////////////////////////////////////////////////////////////////////
  1116. //
  1117. // SYNC
  1118. //
  1119. ////////////////////////////////////////////////////////////////////////////
  1120. /**
  1121. * Syncs UI to intial state.
  1122. *
  1123. * @method syncUI
  1124. * @private
  1125. */
  1126. syncUI: function() {
  1127. // THEAD ROWS
  1128. this._uiSetColumnset(this.get("columnset"));
  1129. // DATA ROWS
  1130. this._uiSetRecordset(this.get("recordset"));
  1131. // SUMMARY
  1132. this._uiSetSummary(this.get("summary"));
  1133. // CAPTION
  1134. this._uiSetCaption(this.get("caption"));
  1135. },
  1136. /**
  1137. * Updates summary.
  1138. *
  1139. * @method _uiSetSummary
  1140. * @param val {String} New summary.
  1141. * @protected
  1142. */
  1143. _uiSetSummary: function(val) {
  1144. val = YisValue(val) ? val : "";
  1145. this._tableNode.set("summary", val);
  1146. },
  1147. /**
  1148. * Updates caption.
  1149. *
  1150. * @method _uiSetCaption
  1151. * @param val {String} New caption.
  1152. * @protected
  1153. */
  1154. _uiSetCaption: function(val) {
  1155. var caption = this._captionNode,
  1156. inDoc = caption.inDoc(),
  1157. method = val ? (!inDoc && 'prepend') : (inDoc && 'removeChild');
  1158. caption.setContent(val || '');
  1159. if (method) {
  1160. // prepend of remove necessary
  1161. this._tableNode[method](caption);
  1162. }
  1163. },
  1164. ////////////////////////////////////////////////////////////////////////////
  1165. //
  1166. // THEAD/COLUMNSET FUNCTIONALITY
  1167. //
  1168. ////////////////////////////////////////////////////////////////////////////
  1169. /**
  1170. * Updates THEAD.
  1171. *
  1172. * @method _uiSetColumnset
  1173. * @param cs {Columnset} New Columnset.
  1174. * @protected
  1175. */
  1176. _uiSetColumnset: function(cs) {
  1177. var tree = cs.tree,
  1178. thead = this._theadNode,
  1179. i = 0,
  1180. len = tree.length,
  1181. parent = thead.get("parentNode"),
  1182. nextSibling = thead.next();
  1183. // Move THEAD off DOM
  1184. thead.remove();
  1185. thead.get("children").remove(true);
  1186. // Iterate tree of columns to add THEAD rows
  1187. for(; i<len; ++i) {
  1188. this._addTheadTrNode({
  1189. thead: thead,
  1190. columns: tree[i],
  1191. id : '' // to avoid {id} leftovers from the trTemplate
  1192. }, (i === 0), (i === len - 1));
  1193. }
  1194. // Column helpers needs _theadNode to exist
  1195. //this._createColumnHelpers();
  1196. // Re-attach THEAD To DoM
  1197. parent.insert(thead, nextSibling);
  1198. },
  1199. /**
  1200. * Creates and attaches header row element.
  1201. *
  1202. * @method _addTheadTrNode
  1203. * @param o {Object} {thead, columns}.
  1204. * @param isFirst {Boolean} Is first row.
  1205. * @param isFirst {Boolean} Is last row.
  1206. * @protected
  1207. */
  1208. _addTheadTrNode: function(o, isFirst, isLast) {
  1209. o.tr = this._createTheadTrNode(o, isFirst, isLast);
  1210. this._attachTheadTrNode(o);
  1211. },
  1212. /**
  1213. * Creates header row element.
  1214. *
  1215. * @method _createTheadTrNode
  1216. * @param o {Object} {thead, columns}.
  1217. * @param isFirst {Boolean} Is first row.
  1218. * @param isLast {Boolean} Is last row.
  1219. * @protected
  1220. * @return {Node}
  1221. */
  1222. _createTheadTrNode: function(o, isFirst, isLast) {
  1223. //TODO: custom classnames
  1224. var tr = Ycreate(fromTemplate(this.get("trTemplate"), o)),
  1225. i = 0,
  1226. columns = o.columns,
  1227. len = columns.length,
  1228. column;
  1229. // Set FIRST/LAST class
  1230. if(isFirst) {
  1231. tr.addClass(CLASS_FIRST);
  1232. }
  1233. if(isLast) {
  1234. tr.addClass(CLASS_LAST);
  1235. }
  1236. for(; i<len; ++i) {
  1237. column = columns[i];
  1238. this._addTheadThNode({value:column.get("label"), column: column, tr:tr});
  1239. }
  1240. return tr;
  1241. },
  1242. /**
  1243. * Attaches header row element.
  1244. *
  1245. * @method _attachTheadTrNode
  1246. * @param o {Object} {thead, columns, tr}.
  1247. * @protected
  1248. */
  1249. _attachTheadTrNode: function(o) {
  1250. o.thead.appendChild(o.tr);
  1251. },
  1252. /**
  1253. * Creates and attaches header cell element.
  1254. *
  1255. * @method _addTheadThNode
  1256. * @param o {Object} {value, column, tr}.
  1257. * @protected
  1258. */
  1259. _addTheadThNode: function(o) {
  1260. o.th = this._createTheadThNode(o);
  1261. this._attachTheadThNode(o);
  1262. //TODO: assign all node pointers: thNode, thLinerNode, thLabelNode
  1263. o.column.thNode = o.th;
  1264. },
  1265. /**
  1266. * Creates header cell element.
  1267. *
  1268. * @method _createTheadThNode
  1269. * @param o {Object} {value, column, tr}.
  1270. * @protected
  1271. * @return {Node}
  1272. */
  1273. _createTheadThNode: function(o) {
  1274. var column = o.column;
  1275. // Populate template object
  1276. o.id = column.get("id");//TODO: validate 1 column ID per document
  1277. o.colspan = column.colSpan;
  1278. o.rowspan = column.rowSpan;
  1279. o.abbr = column.get("abbr");
  1280. o.classnames = column.get("classnames");
  1281. o.value = fromTemplate(this.get("thValueTemplate"), o);
  1282. /*TODO
  1283. // Clear minWidth on hidden Columns
  1284. if(column.get("hidden")) {
  1285. //this._clearMinWidth(column);
  1286. }
  1287. */
  1288. return Ycreate(fromTemplate(this.thTemplate, o));
  1289. },
  1290. /**
  1291. * Attaches header cell element.
  1292. *
  1293. * @method _attachTheadThNode
  1294. * @param o {Object} {value, column, tr}.
  1295. * @protected
  1296. */
  1297. _attachTheadThNode: function(o) {
  1298. o.tr.appendChild(o.th);
  1299. },
  1300. ////////////////////////////////////////////////////////////////////////////
  1301. //
  1302. // TBODY/RECORDSET FUNCTIONALITY
  1303. //
  1304. ////////////////////////////////////////////////////////////////////////////
  1305. /**
  1306. * Updates TBODY.
  1307. *
  1308. * @method _uiSetRecordset
  1309. * @param rs {Recordset} New Recordset.
  1310. * @protected
  1311. */
  1312. _uiSetRecordset: function(rs) {
  1313. var self = this,
  1314. oldTbody = this._tbodyNode,
  1315. parent = oldTbody.get("parentNode"),
  1316. nextSibling = oldTbody.next(),
  1317. columns = this.get('columnset').keys,
  1318. cellValueTemplate = this.get('tdValueTemplate'),
  1319. o = {},
  1320. newTbody, i, len, column, formatter;
  1321. // Replace TBODY with a new one
  1322. //TODO: split _addTbodyNode into create/attach
  1323. oldTbody.remove();
  1324. oldTbody = null;
  1325. newTbody = this._addTbodyNode(this._tableNode);
  1326. newTbody.remove();
  1327. this._tbodyNode = newTbody;
  1328. o.tbody = newTbody;
  1329. o.rowTemplate = this.get('trTemplate');
  1330. o.columns = [];
  1331. // Build up column data to avoid passing through Attribute APIs inside
  1332. // render loops for rows and cells
  1333. for (i = columns.length - 1; i >= 0; --i) {
  1334. column = columns[i];
  1335. o.columns[i] = {
  1336. column : column,
  1337. fields : column.get('field'),
  1338. classnames : column.get('classnames'),
  1339. emptyCellValue: column.get('emptyCellValue')
  1340. }
  1341. formatter = column.get('formatter');
  1342. if (YLang.isFunction(formatter)) {
  1343. // function formatters need to run before checking if the value
  1344. // needs defaulting from column.emptyCellValue
  1345. formatter = Y.bind(this._functionFormatter, this, formatter);
  1346. } else {
  1347. if (!YLang.isString(formatter)) {
  1348. formatter = cellValueTemplate;
  1349. }
  1350. // string formatters need the value defaulted before processing
  1351. formatter = Y.bind(this._templateFormatter, this, formatter);
  1352. }
  1353. o.columns[i].formatter = formatter;
  1354. }
  1355. // Iterate Recordset to use existing TR when possible or add new TR
  1356. // TODO i = this.get("state.offsetIndex")
  1357. // TODO len =this.get("state.pageLength")
  1358. for (i = 0, len = rs.size(); i < len; ++i) {
  1359. o.record = rs.item(i);
  1360. o.data = o.record.get("data");
  1361. o.rowindex = i;
  1362. this._addTbodyTrNode(o); //TODO: sometimes rowindex != recordindex
  1363. }
  1364. // TBODY To DoM
  1365. parent.insert(this._tbodyNode, nextSibling);
  1366. },
  1367. _functionFormatter: function (formatter, o) {
  1368. var value = formatter.call(this, o);
  1369. return (value !== undefined) ? value : o.emptyCellValue;
  1370. },
  1371. _templateFormatter: function (template, o) {
  1372. if (o.value === undefined) {
  1373. o.value = o.emptyCellValue;
  1374. }
  1375. return fromTemplate(template, o);
  1376. },
  1377. /**
  1378. * Creates and attaches data row element.
  1379. *
  1380. * @method _addTbodyTrNode
  1381. * @param o {Object} {tbody, record}
  1382. * @protected
  1383. */
  1384. _addTbodyTrNode: function(o) {
  1385. var row = o.tbody.one("#" + o.record.get("id"));
  1386. o.tr = row || this._createTbodyTrNode(o);
  1387. this._attachTbodyTrNode(o);
  1388. },
  1389. /**
  1390. * Creates data row element.
  1391. *
  1392. * @method _createTbodyTrNode
  1393. * @param o {Object} {tbody, record}
  1394. * @protected
  1395. * @return {Node}
  1396. */
  1397. _createTbodyTrNode: function(o) {
  1398. var columns = o.columns,
  1399. i, len, columnInfo;
  1400. o.tr = Ycreate(fromTemplate(o.rowTemplate, { id: o.record.get('id') }));
  1401. for (i = 0, len = columns.length; i < len; ++i) {
  1402. columnInfo = columns[i];
  1403. o.column = columnInfo.column;
  1404. o.field = columnInfo.fields;
  1405. o.classnames = columnInfo.classnames;
  1406. o.formatter = columnInfo.formatter;
  1407. o.emptyCellValue= columnInfo.emptyCellValue;
  1408. this._addTbodyTdNode(o);
  1409. }
  1410. return o.tr;
  1411. },
  1412. /**
  1413. * Attaches data row element.
  1414. *
  1415. * @method _attachTbodyTrNode
  1416. * @param o {Object} {tbody, record, tr}.
  1417. * @protected
  1418. */
  1419. _attachTbodyTrNode: function(o) {
  1420. var tbody = o.tbody,
  1421. tr = o.tr,
  1422. index = o.rowindex,
  1423. nextSibling = tbody.get("children").item(index) || null,
  1424. isOdd = (index % 2);
  1425. if(isOdd) {
  1426. tr.replaceClass(CLASS_EVEN, CLASS_ODD);
  1427. } else {
  1428. tr.replaceClass(CLASS_ODD, CLASS_EVEN);
  1429. }
  1430. tbody.insertBefore(tr, nextSibling);
  1431. },
  1432. /**
  1433. * Creates and attaches data cell element.
  1434. *
  1435. * @method _addTbodyTdNode
  1436. * @param o {Object} {record, column, tr}.
  1437. * @protected
  1438. */
  1439. _addTbodyTdNode: function(o) {
  1440. o.td = this._createTbodyTdNode(o);
  1441. this._attachTbodyTdNode(o);
  1442. delete o.td;
  1443. },
  1444. /**
  1445. Creates a TD Node from the tdTemplate property using the input object as
  1446. template {placeholder} values. The created Node is also assigned to the
  1447. `td` property on the input object.
  1448. If the input object already has a `td` property, it is returned an no new
  1449. Node is created.
  1450. @method createCell
  1451. @param {Object} data Template values
  1452. @return {Node}
  1453. **/
  1454. createCell: function (data) {
  1455. return data && (data.td ||
  1456. (data.td = Ycreate(fromTemplate(this.tdTemplate, data))));
  1457. },
  1458. /**
  1459. * Creates data cell element.
  1460. *
  1461. * @method _createTbodyTdNode
  1462. * @param o {Object} {record, column, tr}.
  1463. * @protected
  1464. * @return {Node}
  1465. */
  1466. _createTbodyTdNode: function(o) {
  1467. o.headers = o.column.headers;
  1468. o.value = this.formatDataCell(o);
  1469. return o.td || this.createCell(o);
  1470. },
  1471. /**
  1472. * Attaches data cell element.
  1473. *
  1474. * @method _attachTbodyTdNode
  1475. * @param o {Object} {record, column, tr, headers, classnames, value}.
  1476. * @protected
  1477. */
  1478. _attachTbodyTdNode: function(o) {
  1479. o.tr.appendChild(o.td);
  1480. },
  1481. /**
  1482. * Returns markup to insert into data cell element.
  1483. *
  1484. * @method formatDataCell
  1485. * @param @param o {Object} {record, column, tr, headers, classnames}.
  1486. */
  1487. formatDataCell: function (o) {
  1488. o.value = o.data[o.field];
  1489. return o.formatter.call(this, o);
  1490. },
  1491. _initRecordset: function () {
  1492. return new Y.Recordset({ records: [] });
  1493. }
  1494. });
  1495. Y.namespace("DataTable").Base = DTBase;
  1496. }, '3.4.0' ,{requires:['recordset-base','widget','substitute','event-mouseenter']});