_base.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. define("dojox/grid/cells/_base", [
  2. "dojo/_base/kernel",
  3. "dojo/_base/declare",
  4. "dojo/_base/lang",
  5. "dojo/_base/event",
  6. "dojo/_base/connect",
  7. "dojo/_base/array",
  8. "dojo/_base/sniff",
  9. "dojo/dom",
  10. "dojo/dom-attr",
  11. "dojo/dom-construct",
  12. "dijit/_Widget",
  13. "../util"
  14. ], function(dojo, declare, lang, event, connect, array, has, dom, domAttr, domConstruct, _Widget, util){
  15. var _DeferredTextWidget = declare("dojox.grid._DeferredTextWidget", _Widget, {
  16. deferred: null,
  17. _destroyOnRemove: true,
  18. postCreate: function(){
  19. if(this.deferred){
  20. this.deferred.addBoth(lang.hitch(this, function(text){
  21. if(this.domNode){
  22. this.domNode.innerHTML = text;
  23. }
  24. }));
  25. }
  26. }
  27. });
  28. var focusSelectNode = function(inNode){
  29. try{
  30. util.fire(inNode, "focus");
  31. util.fire(inNode, "select");
  32. }catch(e){// IE sux bad
  33. }
  34. };
  35. var whenIdle = function(/*inContext, inMethod, args ...*/){
  36. setTimeout(lang.hitch.apply(dojo, arguments), 0);
  37. };
  38. var BaseCell = declare("dojox.grid.cells._Base", null, {
  39. // summary:
  40. // Respresents a grid cell and contains information about column options and methods
  41. // for retrieving cell related information.
  42. // Each column in a grid layout has a cell object and most events and many methods
  43. // provide access to these objects.
  44. styles: '',
  45. classes: '',
  46. editable: false,
  47. alwaysEditing: false,
  48. formatter: null,
  49. defaultValue: '...',
  50. value: null,
  51. hidden: false,
  52. noresize: false,
  53. draggable: true,
  54. //private
  55. _valueProp: "value",
  56. _formatPending: false,
  57. constructor: function(inProps){
  58. this._props = inProps || {};
  59. lang.mixin(this, inProps);
  60. if(this.draggable === undefined){
  61. this.draggable = true;
  62. }
  63. },
  64. _defaultFormat: function(inValue, callArgs){
  65. var s = this.grid.formatterScope || this;
  66. var f = this.formatter;
  67. if(f && s && typeof f == "string"){
  68. f = this.formatter = s[f];
  69. }
  70. var v = (inValue != this.defaultValue && f) ? f.apply(s, callArgs) : inValue;
  71. if(typeof v == "undefined"){
  72. return this.defaultValue;
  73. }
  74. if(v && v.addBoth){
  75. // Check if it's a deferred
  76. v = new _DeferredTextWidget({deferred: v},
  77. domConstruct.create("span", {innerHTML: this.defaultValue}));
  78. }
  79. if(v && v.declaredClass && v.startup){
  80. return "<div class='dojoxGridStubNode' linkWidget='" +
  81. v.id +
  82. "' cellIdx='" +
  83. this.index +
  84. "'>" +
  85. this.defaultValue +
  86. "</div>";
  87. }
  88. return v;
  89. },
  90. // data source
  91. format: function(inRowIndex, inItem){
  92. // summary:
  93. // provides the html for a given grid cell.
  94. // inRowIndex: int
  95. // grid row index
  96. // returns: html for a given grid cell
  97. var i = this.grid.edit.info;
  98. var d = this.get ? this.get(inRowIndex, inItem) : (this.value || this.defaultValue);
  99. if (d && d.replace && this.grid.escapeHTMLInData) {
  100. d = d.replace(/&/g, '&amp;').replace(/</g, '&lt;');
  101. }
  102. if (this.editable && (this.alwaysEditing || (i.rowIndex==inRowIndex && i.cell==this))) {
  103. return this.formatEditing(i.value ? i.value : d, inRowIndex);
  104. } else {
  105. return this._defaultFormat(d, [d, inRowIndex, this]);
  106. }
  107. },
  108. formatEditing: function(inDatum, inRowIndex){
  109. // summary:
  110. // formats the cell for editing
  111. // inDatum: anything
  112. // cell data to edit
  113. // inRowIndex: int
  114. // grid row index
  115. // returns: string of html to place in grid cell
  116. },
  117. // utility
  118. getNode: function(inRowIndex){
  119. // summary:
  120. // gets the dom node for a given grid cell.
  121. // inRowIndex: int
  122. // grid row index
  123. // returns: dom node for a given grid cell
  124. return this.view.getCellNode(inRowIndex, this.index);
  125. },
  126. getHeaderNode: function(){
  127. return this.view.getHeaderCellNode(this.index);
  128. },
  129. getEditNode: function(inRowIndex){
  130. return (this.getNode(inRowIndex) || 0).firstChild || 0;
  131. },
  132. canResize: function(){
  133. var uw = this.unitWidth;
  134. return uw && (uw!=='auto');
  135. },
  136. isFlex: function(){
  137. var uw = this.unitWidth;
  138. return uw && lang.isString(uw) && (uw=='auto' || uw.slice(-1)=='%');
  139. },
  140. // edit support
  141. applyEdit: function(inValue, inRowIndex){
  142. if(this.getNode(inRowIndex)){
  143. this.grid.edit.applyCellEdit(inValue, this, inRowIndex);
  144. }
  145. },
  146. cancelEdit: function(inRowIndex){
  147. this.grid.doCancelEdit(inRowIndex);
  148. },
  149. _onEditBlur: function(inRowIndex){
  150. if(this.grid.edit.isEditCell(inRowIndex, this.index)){
  151. //console.log('editor onblur', e);
  152. this.grid.edit.apply();
  153. }
  154. },
  155. registerOnBlur: function(inNode, inRowIndex){
  156. if(this.commitOnBlur){
  157. connect.connect(inNode, "onblur", function(e){
  158. // hack: if editor still thinks this editor is current some ms after it blurs, assume we've focused away from grid
  159. setTimeout(lang.hitch(this, "_onEditBlur", inRowIndex), 250);
  160. });
  161. }
  162. },
  163. //protected
  164. needFormatNode: function(inDatum, inRowIndex){
  165. this._formatPending = true;
  166. whenIdle(this, "_formatNode", inDatum, inRowIndex);
  167. },
  168. cancelFormatNode: function(){
  169. this._formatPending = false;
  170. },
  171. //private
  172. _formatNode: function(inDatum, inRowIndex){
  173. if(this._formatPending){
  174. this._formatPending = false;
  175. // make cell selectable
  176. if(!has("ie")){
  177. dom.setSelectable(this.grid.domNode, true);
  178. }
  179. this.formatNode(this.getEditNode(inRowIndex), inDatum, inRowIndex);
  180. }
  181. },
  182. //protected
  183. formatNode: function(inNode, inDatum, inRowIndex){
  184. // summary:
  185. // format the editing dom node. Use when editor is a widget.
  186. // inNode: dom node
  187. // dom node for the editor
  188. // inDatum: anything
  189. // cell data to edit
  190. // inRowIndex: int
  191. // grid row index
  192. if(has("ie")){
  193. // IE sux bad
  194. whenIdle(this, "focus", inRowIndex, inNode);
  195. }else{
  196. this.focus(inRowIndex, inNode);
  197. }
  198. },
  199. dispatchEvent: function(m, e){
  200. if(m in this){
  201. return this[m](e);
  202. }
  203. },
  204. //public
  205. getValue: function(inRowIndex){
  206. // summary:
  207. // returns value entered into editor
  208. // inRowIndex: int
  209. // grid row index
  210. // returns:
  211. // value of editor
  212. return this.getEditNode(inRowIndex)[this._valueProp];
  213. },
  214. setValue: function(inRowIndex, inValue){
  215. // summary:
  216. // set the value of the grid editor
  217. // inRowIndex: int
  218. // grid row index
  219. // inValue: anything
  220. // value of editor
  221. var n = this.getEditNode(inRowIndex);
  222. if(n){
  223. n[this._valueProp] = inValue;
  224. }
  225. },
  226. focus: function(inRowIndex, inNode){
  227. // summary:
  228. // focus the grid editor
  229. // inRowIndex: int
  230. // grid row index
  231. // inNode: dom node
  232. // editor node
  233. focusSelectNode(inNode || this.getEditNode(inRowIndex));
  234. },
  235. save: function(inRowIndex){
  236. // summary:
  237. // save editor state
  238. // inRowIndex: int
  239. // grid row index
  240. this.value = this.value || this.getValue(inRowIndex);
  241. //console.log("save", this.value, inCell.index, inRowIndex);
  242. },
  243. restore: function(inRowIndex){
  244. // summary:
  245. // restore editor state
  246. // inRowIndex: int
  247. // grid row index
  248. this.setValue(inRowIndex, this.value);
  249. //console.log("restore", this.value, inCell.index, inRowIndex);
  250. },
  251. //protected
  252. _finish: function(inRowIndex){
  253. // summary:
  254. // called when editing is completed to clean up editor
  255. // inRowIndex: int
  256. // grid row index
  257. dom.setSelectable(this.grid.domNode, false);
  258. this.cancelFormatNode();
  259. },
  260. //public
  261. apply: function(inRowIndex){
  262. // summary:
  263. // apply edit from cell editor
  264. // inRowIndex: int
  265. // grid row index
  266. this.applyEdit(this.getValue(inRowIndex), inRowIndex);
  267. this._finish(inRowIndex);
  268. },
  269. cancel: function(inRowIndex){
  270. // summary:
  271. // cancel cell edit
  272. // inRowIndex: int
  273. // grid row index
  274. this.cancelEdit(inRowIndex);
  275. this._finish(inRowIndex);
  276. }
  277. });
  278. BaseCell.markupFactory = function(node, cellDef){
  279. var formatter = lang.trim(domAttr.get(node, "formatter")||"");
  280. if(formatter){
  281. cellDef.formatter = lang.getObject(formatter)||formatter;
  282. }
  283. var get = lang.trim(domAttr.get(node, "get")||"");
  284. if(get){
  285. cellDef.get = lang.getObject(get);
  286. }
  287. var getBoolAttr = function(attr, cell, cellAttr){
  288. var value = lang.trim(domAttr.get(node, attr)||"");
  289. if(value){ cell[cellAttr||attr] = !(value.toLowerCase()=="false"); }
  290. };
  291. getBoolAttr("sortDesc", cellDef);
  292. getBoolAttr("editable", cellDef);
  293. getBoolAttr("alwaysEditing", cellDef);
  294. getBoolAttr("noresize", cellDef);
  295. getBoolAttr("draggable", cellDef);
  296. var value = lang.trim(domAttr.get(node, "loadingText")||domAttr.get(node, "defaultValue")||"");
  297. if(value){
  298. cellDef.defaultValue = value;
  299. }
  300. var getStrAttr = function(attr, cell, cellAttr){
  301. var value = lang.trim(domAttr.get(node, attr)||"")||undefined;
  302. if(value){ cell[cellAttr||attr] = value; }
  303. };
  304. getStrAttr("styles", cellDef);
  305. getStrAttr("headerStyles", cellDef);
  306. getStrAttr("cellStyles", cellDef);
  307. getStrAttr("classes", cellDef);
  308. getStrAttr("headerClasses", cellDef);
  309. getStrAttr("cellClasses", cellDef);
  310. };
  311. var Cell = declare("dojox.grid.cells.Cell", BaseCell, {
  312. // summary
  313. // grid cell that provides a standard text input box upon editing
  314. constructor: function(){
  315. this.keyFilter = this.keyFilter;
  316. },
  317. // keyFilter: RegExp
  318. // optional regex for disallowing keypresses
  319. keyFilter: null,
  320. formatEditing: function(inDatum, inRowIndex){
  321. this.needFormatNode(inDatum, inRowIndex);
  322. if (inDatum && inDatum.replace) {
  323. // escape quotes to avoid XSS
  324. inDatum = inDatum.replace(/"/g, '&quot;')
  325. }
  326. return '<input class="dojoxGridInput" type="text" value="' + inDatum + '">';
  327. },
  328. formatNode: function(inNode, inDatum, inRowIndex){
  329. this.inherited(arguments);
  330. // FIXME: feels too specific for this interface
  331. this.registerOnBlur(inNode, inRowIndex);
  332. },
  333. doKey: function(e){
  334. if(this.keyFilter){
  335. var key = String.fromCharCode(e.charCode);
  336. if(key.search(this.keyFilter) == -1){
  337. event.stop(e);
  338. }
  339. }
  340. },
  341. _finish: function(inRowIndex){
  342. this.inherited(arguments);
  343. var n = this.getEditNode(inRowIndex);
  344. try{
  345. util.fire(n, "blur");
  346. }catch(e){}
  347. }
  348. });
  349. Cell.markupFactory = function(node, cellDef){
  350. BaseCell.markupFactory(node, cellDef);
  351. var keyFilter = lang.trim(domAttr.get(node, "keyFilter")||"");
  352. if(keyFilter){
  353. cellDef.keyFilter = new RegExp(keyFilter);
  354. }
  355. };
  356. var RowIndex = declare("dojox.grid.cells.RowIndex", Cell, {
  357. name: 'Row',
  358. postscript: function(){
  359. this.editable = false;
  360. },
  361. get: function(inRowIndex){
  362. return inRowIndex + 1;
  363. }
  364. });
  365. RowIndex.markupFactory = function(node, cellDef){
  366. Cell.markupFactory(node, cellDef);
  367. };
  368. var Select = declare("dojox.grid.cells.Select", Cell, {
  369. // summary:
  370. // grid cell that provides a standard select for editing
  371. // options: Array
  372. // text of each item
  373. options: null,
  374. // values: Array
  375. // value for each item
  376. values: null,
  377. // returnIndex: Integer
  378. // editor returns only the index of the selected option and not the value
  379. returnIndex: -1,
  380. constructor: function(inCell){
  381. this.values = this.values || this.options;
  382. },
  383. formatEditing: function(inDatum, inRowIndex){
  384. this.needFormatNode(inDatum, inRowIndex);
  385. var h = [ '<select class="dojoxGridSelect">' ];
  386. for (var i=0, o, v; ((o=this.options[i]) !== undefined)&&((v=this.values[i]) !== undefined); i++){
  387. v = v.replace ? v.replace(/&/g, '&amp;').replace(/</g, '&lt;') : v;
  388. o = o.replace ? o.replace(/&/g, '&amp;').replace(/</g, '&lt;') : o;
  389. h.push("<option", (inDatum==v ? ' selected' : ''), ' value="' + v + '"', ">", o, "</option>");
  390. }
  391. h.push('</select>');
  392. return h.join('');
  393. },
  394. _defaultFormat: function(inValue, callArgs){
  395. var v = this.inherited(arguments);
  396. // when 'values' and 'options' both provided and there is no cutomized formatter,
  397. // then we use 'options' as label in order to be consistent
  398. if(!this.formatter && this.values && this.options){
  399. var i = array.indexOf(this.values, v);
  400. if(i >= 0){
  401. v = this.options[i];
  402. }
  403. }
  404. return v;
  405. },
  406. getValue: function(inRowIndex){
  407. var n = this.getEditNode(inRowIndex);
  408. if(n){
  409. var i = n.selectedIndex, o = n.options[i];
  410. return this.returnIndex > -1 ? i : o.value || o.innerHTML;
  411. }
  412. }
  413. });
  414. Select.markupFactory = function(node, cell){
  415. Cell.markupFactory(node, cell);
  416. var options = lang.trim(domAttr.get(node, "options")||"");
  417. if(options){
  418. var o = options.split(',');
  419. if(o[0] != options){
  420. cell.options = o;
  421. }
  422. }
  423. var values = lang.trim(domAttr.get(node, "values")||"");
  424. if(values){
  425. var v = values.split(',');
  426. if(v[0] != values){
  427. cell.values = v;
  428. }
  429. }
  430. };
  431. var AlwaysEdit = declare("dojox.grid.cells.AlwaysEdit", Cell, {
  432. // summary:
  433. // grid cell that is always in an editable state, regardless of grid editing state
  434. alwaysEditing: true,
  435. _formatNode: function(inDatum, inRowIndex){
  436. this.formatNode(this.getEditNode(inRowIndex), inDatum, inRowIndex);
  437. },
  438. applyStaticValue: function(inRowIndex){
  439. var e = this.grid.edit;
  440. e.applyCellEdit(this.getValue(inRowIndex), this, inRowIndex);
  441. e.start(this, inRowIndex, true);
  442. }
  443. });
  444. AlwaysEdit.markupFactory = function(node, cell){
  445. Cell.markupFactory(node, cell);
  446. };
  447. var Bool = declare("dojox.grid.cells.Bool", AlwaysEdit, {
  448. // summary:
  449. // grid cell that provides a standard checkbox that is always on for editing
  450. _valueProp: "checked",
  451. formatEditing: function(inDatum, inRowIndex){
  452. return '<input class="dojoxGridInput" type="checkbox"' + (inDatum ? ' checked="checked"' : '') + ' style="width: auto" />';
  453. },
  454. doclick: function(e){
  455. if(e.target.tagName == 'INPUT'){
  456. this.applyStaticValue(e.rowIndex);
  457. }
  458. }
  459. });
  460. Bool.markupFactory = function(node, cell){
  461. AlwaysEdit.markupFactory(node, cell);
  462. };
  463. return BaseCell;
  464. });