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