1 | dojo.provide("dojox.widget.RollingList"); |
---|
2 | dojo.experimental("dojox.widget.RollingList"); |
---|
3 | |
---|
4 | dojo.require("dojo.window"); |
---|
5 | |
---|
6 | dojo.require("dijit.layout.ContentPane"); |
---|
7 | dojo.require("dijit._Templated"); |
---|
8 | dojo.require("dijit._Contained"); |
---|
9 | dojo.require("dijit.layout._LayoutWidget"); |
---|
10 | dojo.require("dijit.Menu"); |
---|
11 | dojo.require("dijit.form.Button"); |
---|
12 | dojo.require("dijit.focus"); // dijit.focus() |
---|
13 | dojo.require("dijit._base.focus"); // dijit.getFocus() |
---|
14 | |
---|
15 | dojo.require("dojox.html.metrics"); |
---|
16 | |
---|
17 | dojo.require("dojo.i18n"); |
---|
18 | dojo.requireLocalization("dijit", "common"); |
---|
19 | |
---|
20 | dojo.declare("dojox.widget._RollingListPane", |
---|
21 | [dijit.layout.ContentPane, dijit._Templated, dijit._Contained], { |
---|
22 | // summary: |
---|
23 | // a core pane that can be attached to a RollingList. All panes |
---|
24 | // should extend this one |
---|
25 | |
---|
26 | // templateString: string |
---|
27 | // our template |
---|
28 | templateString: '<div class="dojoxRollingListPane"><table><tbody><tr><td dojoAttachPoint="containerNode"></td></tr></tbody></div>', |
---|
29 | |
---|
30 | // parentWidget: dojox.widget.RollingList |
---|
31 | // Our rolling list widget |
---|
32 | parentWidget: null, |
---|
33 | |
---|
34 | // parentPane: dojox.widget._RollingListPane |
---|
35 | // The pane that immediately precedes ours |
---|
36 | parentPane: null, |
---|
37 | |
---|
38 | // store: store |
---|
39 | // the store we must use |
---|
40 | store: null, |
---|
41 | |
---|
42 | // items: item[] |
---|
43 | // an array of (possibly not-yet-loaded) items to display in this. |
---|
44 | // If this array is null, then the query and query options are used to |
---|
45 | // get the top-level items to use. This array is also used to watch and |
---|
46 | // see if the pane needs to be reloaded (store notifications are handled) |
---|
47 | // by the pane |
---|
48 | items: null, |
---|
49 | |
---|
50 | // query: object |
---|
51 | // a query to pass to the datastore. This is only used if items are null |
---|
52 | query: null, |
---|
53 | |
---|
54 | // queryOptions: object |
---|
55 | // query options to be passed to the datastore |
---|
56 | queryOptions: null, |
---|
57 | |
---|
58 | // focusByNode: boolean |
---|
59 | // set to false if the subclass will handle its own node focusing |
---|
60 | _focusByNode: true, |
---|
61 | |
---|
62 | // minWidth: integer |
---|
63 | // the width (in px) for this pane |
---|
64 | minWidth: 0, |
---|
65 | |
---|
66 | _setContentAndScroll: function(/*String|DomNode|Nodelist*/cont, /*Boolean?*/isFakeContent){ |
---|
67 | // summary: |
---|
68 | // sets the value of the content and scrolls it into view |
---|
69 | this._setContent(cont, isFakeContent); |
---|
70 | this.parentWidget.scrollIntoView(this); |
---|
71 | }, |
---|
72 | |
---|
73 | _updateNodeWidth: function(n, min){ |
---|
74 | // summary: |
---|
75 | // updates the min width of the pane to be minPaneWidth |
---|
76 | n.style.width = ""; |
---|
77 | var nWidth = dojo.marginBox(n).w; |
---|
78 | if(nWidth < min){ |
---|
79 | dojo.marginBox(n, {w: min}); |
---|
80 | } |
---|
81 | }, |
---|
82 | |
---|
83 | _onMinWidthChange: function(v){ |
---|
84 | // Called when the min width of a pane has changed |
---|
85 | this._updateNodeWidth(this.domNode, v); |
---|
86 | }, |
---|
87 | |
---|
88 | _setMinWidthAttr: function(v){ |
---|
89 | if(v !== this.minWidth){ |
---|
90 | this.minWidth = v; |
---|
91 | this._onMinWidthChange(v); |
---|
92 | } |
---|
93 | }, |
---|
94 | |
---|
95 | startup: function(){ |
---|
96 | if(this._started){ return; } |
---|
97 | if(this.store && this.store.getFeatures()["dojo.data.api.Notification"]){ |
---|
98 | window.setTimeout(dojo.hitch(this, function(){ |
---|
99 | // Set connections after a slight timeout to avoid getting in the |
---|
100 | // condition where we are setting them while events are still |
---|
101 | // being fired |
---|
102 | this.connect(this.store, "onSet", "_onSetItem"); |
---|
103 | this.connect(this.store, "onNew", "_onNewItem"); |
---|
104 | this.connect(this.store, "onDelete", "_onDeleteItem"); |
---|
105 | }), 1); |
---|
106 | } |
---|
107 | this.connect(this.focusNode||this.domNode, "onkeypress", "_focusKey"); |
---|
108 | this.parentWidget._updateClass(this.domNode, "Pane"); |
---|
109 | this.inherited(arguments); |
---|
110 | this._onMinWidthChange(this.minWidth); |
---|
111 | }, |
---|
112 | |
---|
113 | _focusKey: function(/*Event*/e){ |
---|
114 | // summary: |
---|
115 | // called when a keypress happens on the widget |
---|
116 | if(e.charOrCode == dojo.keys.BACKSPACE){ |
---|
117 | dojo.stopEvent(e); |
---|
118 | return; |
---|
119 | }else if(e.charOrCode == dojo.keys.LEFT_ARROW && this.parentPane){ |
---|
120 | this.parentPane.focus(); |
---|
121 | this.parentWidget.scrollIntoView(this.parentPane); |
---|
122 | }else if(e.charOrCode == dojo.keys.ENTER){ |
---|
123 | this.parentWidget._onExecute(); |
---|
124 | } |
---|
125 | }, |
---|
126 | |
---|
127 | focus: function(/*boolean*/force){ |
---|
128 | // summary: |
---|
129 | // sets the focus to this current widget |
---|
130 | if(this.parentWidget._focusedPane != this){ |
---|
131 | this.parentWidget._focusedPane = this; |
---|
132 | this.parentWidget.scrollIntoView(this); |
---|
133 | if(this._focusByNode && (!this.parentWidget._savedFocus || force)){ |
---|
134 | try{(this.focusNode||this.domNode).focus();}catch(e){} |
---|
135 | } |
---|
136 | } |
---|
137 | }, |
---|
138 | |
---|
139 | _onShow: function(){ |
---|
140 | // summary: |
---|
141 | // checks that the store is loaded |
---|
142 | this.inherited(arguments); |
---|
143 | if((this.store || this.items) && ((this.refreshOnShow && this.domNode) || (!this.isLoaded && this.domNode))){ |
---|
144 | this.refresh(); |
---|
145 | } |
---|
146 | }, |
---|
147 | |
---|
148 | _load: function(){ |
---|
149 | // summary: |
---|
150 | // sets the "loading" message and then kicks off a query asyncronously |
---|
151 | this.isLoaded = false; |
---|
152 | if(this.items){ |
---|
153 | this._setContentAndScroll(this.onLoadStart(), true); |
---|
154 | window.setTimeout(dojo.hitch(this, "_doQuery"), 1); |
---|
155 | }else{ |
---|
156 | this._doQuery(); |
---|
157 | } |
---|
158 | }, |
---|
159 | |
---|
160 | _doLoadItems: function(/*item[]*/items, /*function*/callback){ |
---|
161 | // summary: |
---|
162 | // loads the given items, and then calls the callback when they |
---|
163 | // are finished. |
---|
164 | var _waitCount = 0, store = this.store; |
---|
165 | dojo.forEach(items, function(item){ |
---|
166 | if(!store.isItemLoaded(item)){ _waitCount++; } |
---|
167 | }); |
---|
168 | if(_waitCount === 0){ |
---|
169 | callback(); |
---|
170 | }else{ |
---|
171 | var onItem = function(item){ |
---|
172 | _waitCount--; |
---|
173 | if((_waitCount) === 0){ |
---|
174 | callback(); |
---|
175 | } |
---|
176 | }; |
---|
177 | dojo.forEach(items, function(item){ |
---|
178 | if(!store.isItemLoaded(item)){ |
---|
179 | store.loadItem({item: item, onItem: onItem}); |
---|
180 | } |
---|
181 | }); |
---|
182 | } |
---|
183 | }, |
---|
184 | |
---|
185 | _doQuery: function(){ |
---|
186 | // summary: |
---|
187 | // either runs the query or loads potentially not-yet-loaded items. |
---|
188 | if(!this.domNode){return;} |
---|
189 | var preload = this.parentWidget.preloadItems; |
---|
190 | preload = (preload === true || (this.items && this.items.length <= Number(preload))); |
---|
191 | if(this.items && preload){ |
---|
192 | this._doLoadItems(this.items, dojo.hitch(this, "onItems")); |
---|
193 | }else if(this.items){ |
---|
194 | this.onItems(); |
---|
195 | }else{ |
---|
196 | this._setContentAndScroll(this.onFetchStart(), true); |
---|
197 | this.store.fetch({query: this.query, |
---|
198 | onComplete: function(items){ |
---|
199 | this.items = items; |
---|
200 | this.onItems(); |
---|
201 | }, |
---|
202 | onError: function(e){ |
---|
203 | this._onError("Fetch", e); |
---|
204 | }, |
---|
205 | scope: this}); |
---|
206 | } |
---|
207 | }, |
---|
208 | |
---|
209 | _hasItem: function(/* item */ item){ |
---|
210 | // summary: |
---|
211 | // returns whether or not the given item is handled by this |
---|
212 | // pane |
---|
213 | var items = this.items || []; |
---|
214 | for(var i = 0, myItem; (myItem = items[i]); i++){ |
---|
215 | if(this.parentWidget._itemsMatch(myItem, item)){ |
---|
216 | return true; |
---|
217 | } |
---|
218 | } |
---|
219 | return false; |
---|
220 | }, |
---|
221 | |
---|
222 | _onSetItem: function(/* item */ item, |
---|
223 | /* attribute-name-string */ attribute, |
---|
224 | /* Object|Array */ oldValue, |
---|
225 | /* Object|Array */ newValue){ |
---|
226 | // summary: |
---|
227 | // called when an item in the store has changed |
---|
228 | if(this._hasItem(item)){ |
---|
229 | this.refresh(); |
---|
230 | } |
---|
231 | }, |
---|
232 | |
---|
233 | _onNewItem: function(/* item */ newItem, /*object?*/ parentInfo){ |
---|
234 | // summary: |
---|
235 | // called when an item is added to the store |
---|
236 | var sel; |
---|
237 | if((!parentInfo && !this.parentPane) || |
---|
238 | (parentInfo && this.parentPane && this.parentPane._hasItem(parentInfo.item) && |
---|
239 | (sel = this.parentPane._getSelected()) && this.parentWidget._itemsMatch(sel.item, parentInfo.item))){ |
---|
240 | this.items.push(newItem); |
---|
241 | this.refresh(); |
---|
242 | }else if(parentInfo && this.parentPane && this._hasItem(parentInfo.item)){ |
---|
243 | this.refresh(); |
---|
244 | } |
---|
245 | }, |
---|
246 | |
---|
247 | _onDeleteItem: function(/* item */ deletedItem){ |
---|
248 | // summary: |
---|
249 | // called when an item is removed from the store |
---|
250 | if(this._hasItem(deletedItem)){ |
---|
251 | this.items = dojo.filter(this.items, function(i){ |
---|
252 | return (i != deletedItem); |
---|
253 | }); |
---|
254 | this.refresh(); |
---|
255 | } |
---|
256 | }, |
---|
257 | |
---|
258 | onFetchStart: function(){ |
---|
259 | // summary: |
---|
260 | // called before a fetch starts |
---|
261 | return this.loadingMessage; |
---|
262 | }, |
---|
263 | |
---|
264 | onFetchError: function(/*Error*/ error){ |
---|
265 | // summary: |
---|
266 | // called when a fetch error occurs. |
---|
267 | return this.errorMessage; |
---|
268 | }, |
---|
269 | |
---|
270 | onLoadStart: function(){ |
---|
271 | // summary: |
---|
272 | // called before a load starts |
---|
273 | return this.loadingMessage; |
---|
274 | }, |
---|
275 | |
---|
276 | onLoadError: function(/*Error*/ error){ |
---|
277 | // summary: |
---|
278 | // called when a load error occurs. |
---|
279 | return this.errorMessage; |
---|
280 | }, |
---|
281 | |
---|
282 | onItems: function(){ |
---|
283 | // summary: |
---|
284 | // called after a fetch or load - at this point, this.items should be |
---|
285 | // set and loaded. Override this function to "do your stuff" |
---|
286 | if(!this.onLoadDeferred){ |
---|
287 | this.cancel(); |
---|
288 | this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel")); |
---|
289 | } |
---|
290 | this._onLoadHandler(); |
---|
291 | } |
---|
292 | |
---|
293 | }); |
---|
294 | |
---|
295 | dojo.declare("dojox.widget._RollingListGroupPane", |
---|
296 | [dojox.widget._RollingListPane], { |
---|
297 | // summary: |
---|
298 | // a pane that will handle groups (treats them as menu items) |
---|
299 | |
---|
300 | // templateString: string |
---|
301 | // our template |
---|
302 | templateString: '<div><div dojoAttachPoint="containerNode"></div>' + |
---|
303 | '<div dojoAttachPoint="menuContainer">' + |
---|
304 | '<div dojoAttachPoint="menuNode"></div>' + |
---|
305 | '</div></div>', |
---|
306 | |
---|
307 | // _menu: dijit.Menu |
---|
308 | // The menu that we will call addChild() on for adding items |
---|
309 | _menu: null, |
---|
310 | |
---|
311 | _setContent: function(/*String|DomNode|Nodelist*/cont){ |
---|
312 | if(!this._menu){ |
---|
313 | // Only set the content if we don't already have a menu |
---|
314 | this.inherited(arguments); |
---|
315 | } |
---|
316 | }, |
---|
317 | |
---|
318 | _onMinWidthChange: function(v){ |
---|
319 | // override and resize the menu instead |
---|
320 | if(!this._menu){ return; } |
---|
321 | var dWidth = dojo.marginBox(this.domNode).w; |
---|
322 | var mWidth = dojo.marginBox(this._menu.domNode).w; |
---|
323 | this._updateNodeWidth(this._menu.domNode, v - (dWidth - mWidth)); |
---|
324 | }, |
---|
325 | |
---|
326 | onItems: function(){ |
---|
327 | // summary: |
---|
328 | // called after a fetch or load |
---|
329 | var selectItem, hadChildren = false; |
---|
330 | if(this._menu){ |
---|
331 | selectItem = this._getSelected(); |
---|
332 | this._menu.destroyRecursive(); |
---|
333 | } |
---|
334 | this._menu = this._getMenu(); |
---|
335 | var child, selectMenuItem; |
---|
336 | if(this.items.length){ |
---|
337 | dojo.forEach(this.items, function(item){ |
---|
338 | child = this.parentWidget._getMenuItemForItem(item, this); |
---|
339 | if(child){ |
---|
340 | if(selectItem && this.parentWidget._itemsMatch(child.item, selectItem.item)){ |
---|
341 | selectMenuItem = child; |
---|
342 | } |
---|
343 | this._menu.addChild(child); |
---|
344 | } |
---|
345 | }, this); |
---|
346 | }else{ |
---|
347 | child = this.parentWidget._getMenuItemForItem(null, this); |
---|
348 | if(child){ |
---|
349 | this._menu.addChild(child); |
---|
350 | } |
---|
351 | } |
---|
352 | if(selectMenuItem){ |
---|
353 | this._setSelected(selectMenuItem); |
---|
354 | if((selectItem && !selectItem.children && selectMenuItem.children) || |
---|
355 | (selectItem && selectItem.children && !selectMenuItem.children)){ |
---|
356 | var itemPane = this.parentWidget._getPaneForItem(selectMenuItem.item, this, selectMenuItem.children); |
---|
357 | if(itemPane){ |
---|
358 | this.parentWidget.addChild(itemPane, this.getIndexInParent() + 1); |
---|
359 | }else{ |
---|
360 | this.parentWidget._removeAfter(this); |
---|
361 | this.parentWidget._onItemClick(null, this, selectMenuItem.item, selectMenuItem.children); |
---|
362 | } |
---|
363 | } |
---|
364 | }else if(selectItem){ |
---|
365 | this.parentWidget._removeAfter(this); |
---|
366 | } |
---|
367 | this.containerNode.innerHTML = ""; |
---|
368 | this.containerNode.appendChild(this._menu.domNode); |
---|
369 | this.parentWidget.scrollIntoView(this); |
---|
370 | this._checkScrollConnection(true); |
---|
371 | this.inherited(arguments); |
---|
372 | this._onMinWidthChange(this.minWidth); |
---|
373 | }, |
---|
374 | |
---|
375 | _checkScrollConnection: function(doLoad){ |
---|
376 | // summary: |
---|
377 | // checks whether or not we need to connect to our onscroll |
---|
378 | // function |
---|
379 | var store = this.store; |
---|
380 | if(this._scrollConn){ |
---|
381 | this.disconnect(this._scrollConn); |
---|
382 | } |
---|
383 | delete this._scrollConn; |
---|
384 | if(!dojo.every(this.items, function(i){return store.isItemLoaded(i);})){ |
---|
385 | if(doLoad){ |
---|
386 | this._loadVisibleItems(); |
---|
387 | } |
---|
388 | this._scrollConn = this.connect(this.domNode, "onscroll", "_onScrollPane"); |
---|
389 | } |
---|
390 | }, |
---|
391 | |
---|
392 | startup: function(){ |
---|
393 | this.inherited(arguments); |
---|
394 | this.parentWidget._updateClass(this.domNode, "GroupPane"); |
---|
395 | }, |
---|
396 | |
---|
397 | focus: function(/*boolean*/force){ |
---|
398 | // summary: |
---|
399 | // sets the focus to this current widget |
---|
400 | if(this._menu){ |
---|
401 | if(this._pendingFocus){ |
---|
402 | this.disconnect(this._pendingFocus); |
---|
403 | } |
---|
404 | delete this._pendingFocus; |
---|
405 | |
---|
406 | // We focus the right widget - either the focusedChild, the |
---|
407 | // selected node, the first menu item, or the menu itself |
---|
408 | var focusWidget = this._menu.focusedChild; |
---|
409 | if(!focusWidget){ |
---|
410 | var focusNode = dojo.query(".dojoxRollingListItemSelected", this.domNode)[0]; |
---|
411 | if(focusNode){ |
---|
412 | focusWidget = dijit.byNode(focusNode); |
---|
413 | } |
---|
414 | } |
---|
415 | if(!focusWidget){ |
---|
416 | focusWidget = this._menu.getChildren()[0] || this._menu; |
---|
417 | } |
---|
418 | this._focusByNode = false; |
---|
419 | if(focusWidget.focusNode){ |
---|
420 | if(!this.parentWidget._savedFocus || force){ |
---|
421 | try{focusWidget.focusNode.focus();}catch(e){} |
---|
422 | } |
---|
423 | window.setTimeout(function(){ |
---|
424 | try{ |
---|
425 | dojo.window.scrollIntoView(focusWidget.focusNode); |
---|
426 | }catch(e){} |
---|
427 | }, 1); |
---|
428 | }else if(focusWidget.focus){ |
---|
429 | if(!this.parentWidget._savedFocus || force){ |
---|
430 | focusWidget.focus(); |
---|
431 | } |
---|
432 | }else{ |
---|
433 | this._focusByNode = true; |
---|
434 | } |
---|
435 | this.inherited(arguments); |
---|
436 | }else if(!this._pendingFocus){ |
---|
437 | this._pendingFocus = this.connect(this, "onItems", "focus"); |
---|
438 | } |
---|
439 | }, |
---|
440 | |
---|
441 | _getMenu: function(){ |
---|
442 | // summary: |
---|
443 | // returns a widget to be used for the container widget. |
---|
444 | var self = this; |
---|
445 | var menu = new dijit.Menu({ |
---|
446 | parentMenu: this.parentPane ? this.parentPane._menu : null, |
---|
447 | onCancel: function(/*Boolean*/ closeAll){ |
---|
448 | if(self.parentPane){ |
---|
449 | self.parentPane.focus(true); |
---|
450 | } |
---|
451 | }, |
---|
452 | _moveToPopup: function(/*Event*/ evt){ |
---|
453 | if(this.focusedChild && !this.focusedChild.disabled){ |
---|
454 | this.onItemClick(this.focusedChild, evt); |
---|
455 | } |
---|
456 | } |
---|
457 | }, this.menuNode); |
---|
458 | this.connect(menu, "onItemClick", function(/*dijit.MenuItem*/ item, /*Event*/ evt){ |
---|
459 | if(item.disabled){ return; } |
---|
460 | evt.alreadySelected = dojo.hasClass(item.domNode, "dojoxRollingListItemSelected"); |
---|
461 | if(evt.alreadySelected && |
---|
462 | ((evt.type == "keypress" && evt.charOrCode != dojo.keys.ENTER) || |
---|
463 | (evt.type == "internal"))){ |
---|
464 | var p = this.parentWidget.getChildren()[this.getIndexInParent() + 1]; |
---|
465 | if(p){ |
---|
466 | p.focus(true); |
---|
467 | this.parentWidget.scrollIntoView(p); |
---|
468 | } |
---|
469 | }else{ |
---|
470 | this._setSelected(item, menu); |
---|
471 | this.parentWidget._onItemClick(evt, this, item.item, item.children); |
---|
472 | if(evt.type == "keypress" && evt.charOrCode == dojo.keys.ENTER){ |
---|
473 | this.parentWidget._onExecute(); |
---|
474 | } |
---|
475 | } |
---|
476 | }); |
---|
477 | if(!menu._started){ |
---|
478 | menu.startup(); |
---|
479 | } |
---|
480 | return menu; |
---|
481 | }, |
---|
482 | |
---|
483 | _onScrollPane: function(){ |
---|
484 | // summary: |
---|
485 | // called when the pane has been scrolled - it sets a timeout |
---|
486 | // so that we don't try and load our visible items too often during |
---|
487 | // a scroll |
---|
488 | if(this._visibleLoadPending){ |
---|
489 | window.clearTimeout(this._visibleLoadPending); |
---|
490 | } |
---|
491 | this._visibleLoadPending = window.setTimeout(dojo.hitch(this, "_loadVisibleItems"), 500); |
---|
492 | }, |
---|
493 | |
---|
494 | _loadVisibleItems: function(){ |
---|
495 | // summary: |
---|
496 | // loads the items that are currently visible in the pane |
---|
497 | delete this._visibleLoadPending; |
---|
498 | var menu = this._menu; |
---|
499 | if(!menu){ return; } |
---|
500 | var children = menu.getChildren(); |
---|
501 | if(!children || !children.length){ return; } |
---|
502 | var gpbme = function(n, m, pb){ |
---|
503 | var s = dojo.getComputedStyle(n); |
---|
504 | var r = 0; |
---|
505 | if(m){ r += dojo._getMarginExtents(n, s).t; } |
---|
506 | if(pb){ r += dojo._getPadBorderExtents(n, s).t; } |
---|
507 | return r; |
---|
508 | }; |
---|
509 | var topOffset = gpbme(this.domNode, false, true) + |
---|
510 | gpbme(this.containerNode, true, true) + |
---|
511 | gpbme(menu.domNode, true, true) + |
---|
512 | gpbme(children[0].domNode, true, false); |
---|
513 | var h = dojo.contentBox(this.domNode).h; |
---|
514 | var minOffset = this.domNode.scrollTop - topOffset - (h/2); |
---|
515 | var maxOffset = minOffset + (3*h/2); |
---|
516 | var menuItemsToLoad = dojo.filter(children, function(c){ |
---|
517 | var cnt = c.domNode.offsetTop; |
---|
518 | var s = c.store; |
---|
519 | var i = c.item; |
---|
520 | return (cnt >= minOffset && cnt <= maxOffset && !s.isItemLoaded(i)); |
---|
521 | }) |
---|
522 | var itemsToLoad = dojo.map(menuItemsToLoad, function(c){ |
---|
523 | return c.item; |
---|
524 | }); |
---|
525 | var onItems = dojo.hitch(this, function(){ |
---|
526 | var selectItem = this._getSelected(); |
---|
527 | var selectMenuItem; |
---|
528 | dojo.forEach(itemsToLoad, function(item, idx){ |
---|
529 | var newItem = this.parentWidget._getMenuItemForItem(item, this); |
---|
530 | var oItem = menuItemsToLoad[idx]; |
---|
531 | var oIdx = oItem.getIndexInParent(); |
---|
532 | menu.removeChild(oItem); |
---|
533 | if(newItem){ |
---|
534 | if(selectItem && this.parentWidget._itemsMatch(newItem.item, selectItem.item)){ |
---|
535 | selectMenuItem = newItem; |
---|
536 | } |
---|
537 | menu.addChild(newItem, oIdx); |
---|
538 | if(menu.focusedChild == oItem){ |
---|
539 | menu.focusChild(newItem); |
---|
540 | } |
---|
541 | } |
---|
542 | oItem.destroy(); |
---|
543 | }, this); |
---|
544 | this._checkScrollConnection(false); |
---|
545 | }); |
---|
546 | this._doLoadItems(itemsToLoad, onItems); |
---|
547 | }, |
---|
548 | |
---|
549 | _getSelected: function(/*dijit.Menu?*/ menu){ |
---|
550 | // summary: |
---|
551 | // returns the selected menu item - or null if none are selected |
---|
552 | if(!menu){ menu = this._menu; } |
---|
553 | if(menu){ |
---|
554 | var children = this._menu.getChildren(); |
---|
555 | for(var i = 0, item; (item = children[i]); i++){ |
---|
556 | if(dojo.hasClass(item.domNode, "dojoxRollingListItemSelected")){ |
---|
557 | return item; |
---|
558 | } |
---|
559 | } |
---|
560 | } |
---|
561 | return null; |
---|
562 | }, |
---|
563 | |
---|
564 | _setSelected: function(/*dijit.MenuItem?*/ item, /*dijit.Menu?*/ menu){ |
---|
565 | // summary: |
---|
566 | // selects the given item in the given menu (defaults to pane's menu) |
---|
567 | if(!menu){ menu = this._menu;} |
---|
568 | if(menu){ |
---|
569 | dojo.forEach(menu.getChildren(), function(i){ |
---|
570 | this.parentWidget._updateClass(i.domNode, "Item", {"Selected": (item && (i == item && !i.disabled))}); |
---|
571 | }, this); |
---|
572 | } |
---|
573 | } |
---|
574 | }); |
---|
575 | |
---|
576 | dojo.declare("dojox.widget.RollingList", |
---|
577 | [dijit._Widget, dijit._Templated, dijit._Container], { |
---|
578 | // summary: |
---|
579 | // a rolling list that can be tied to a data store with children |
---|
580 | |
---|
581 | // templateString: String |
---|
582 | // The template to be used to construct the widget. |
---|
583 | templateString: dojo.cache("dojox.widget", "RollingList/RollingList.html"), |
---|
584 | widgetsInTemplate: true, |
---|
585 | |
---|
586 | // className: string |
---|
587 | // an additional class (or space-separated classes) to add for our widget |
---|
588 | className: "", |
---|
589 | |
---|
590 | // store: store |
---|
591 | // the store we must use |
---|
592 | store: null, |
---|
593 | |
---|
594 | // query: object |
---|
595 | // a query to pass to the datastore. This is only used if items are null |
---|
596 | query: null, |
---|
597 | |
---|
598 | // queryOptions: object |
---|
599 | // query options to be passed to the datastore |
---|
600 | queryOptions: null, |
---|
601 | |
---|
602 | // childrenAttrs: String[] |
---|
603 | // one ore more attributes that holds children of a node |
---|
604 | childrenAttrs: ["children"], |
---|
605 | |
---|
606 | // parentAttr: string |
---|
607 | // the attribute to read for finding our parent item (if any) |
---|
608 | parentAttr: "", |
---|
609 | |
---|
610 | // value: item |
---|
611 | // The value that has been selected |
---|
612 | value: null, |
---|
613 | |
---|
614 | // executeOnDblClick: boolean |
---|
615 | // Set to true if you want to call onExecute when an item is |
---|
616 | // double-clicked, false if you want to call onExecute yourself. (mainly |
---|
617 | // used for popups to control how they want to be handled) |
---|
618 | executeOnDblClick: true, |
---|
619 | |
---|
620 | // preloadItems: boolean or int |
---|
621 | // if set to true, then onItems will be called only *after* all items have |
---|
622 | // been loaded (ie store.isLoaded will return true for all of them). If |
---|
623 | // false, then no preloading will occur. If set to an integer, preloading |
---|
624 | // will occur if the number of items is less than or equal to the value |
---|
625 | // of the integer. The onItems function will need to be aware of handling |
---|
626 | // items that may not be loaded |
---|
627 | preloadItems: false, |
---|
628 | |
---|
629 | // showButtons: boolean |
---|
630 | // if set to true, then buttons for "OK" and "Cancel" will be provided |
---|
631 | showButtons: false, |
---|
632 | |
---|
633 | // okButtonLabel: string |
---|
634 | // The string to use for the OK button - will use dijit's common "OK" string |
---|
635 | // if not set |
---|
636 | okButtonLabel: "", |
---|
637 | |
---|
638 | // cancelButtonLabel: string |
---|
639 | // The string to use for the Cancel button - will use dijit's common |
---|
640 | // "Cancel" string if not set |
---|
641 | cancelButtonLabel: "", |
---|
642 | |
---|
643 | // minPaneWidth: integer |
---|
644 | // the minimum pane width (in px) for all child panes. If they are narrower, |
---|
645 | // the width will be increased to this value. |
---|
646 | minPaneWidth: 0, |
---|
647 | |
---|
648 | postMixInProperties: function(){ |
---|
649 | // summary: |
---|
650 | // Mix in our labels, if they are not set |
---|
651 | this.inherited(arguments); |
---|
652 | var loc = dojo.i18n.getLocalization("dijit", "common"); |
---|
653 | this.okButtonLabel = this.okButtonLabel || loc.buttonOk; |
---|
654 | this.cancelButtonLabel = this.cancelButtonLabel || loc.buttonCancel; |
---|
655 | }, |
---|
656 | |
---|
657 | _setShowButtonsAttr: function(doShow){ |
---|
658 | // summary: |
---|
659 | // Sets the visibility of the buttons for the widget |
---|
660 | var needsLayout = false; |
---|
661 | if((this.showButtons != doShow && this._started) || |
---|
662 | (this.showButtons == doShow && !this.started)){ |
---|
663 | needsLayout = true; |
---|
664 | } |
---|
665 | dojo.toggleClass(this.domNode, "dojoxRollingListButtonsHidden", !doShow); |
---|
666 | this.showButtons = doShow; |
---|
667 | if(needsLayout){ |
---|
668 | if(this._started){ |
---|
669 | this.layout(); |
---|
670 | }else{ |
---|
671 | window.setTimeout(dojo.hitch(this, "layout"), 0); |
---|
672 | } |
---|
673 | } |
---|
674 | }, |
---|
675 | |
---|
676 | _itemsMatch: function(/*item*/ item1, /*item*/ item2){ |
---|
677 | // summary: |
---|
678 | // returns whether or not the two items match - checks ID if |
---|
679 | // they aren't the exact same object |
---|
680 | if(!item1 && !item2){ |
---|
681 | return true; |
---|
682 | }else if(!item1 || !item2){ |
---|
683 | return false; |
---|
684 | } |
---|
685 | return (item1 == item2 || |
---|
686 | (this._isIdentity && this.store.getIdentity(item1) == this.store.getIdentity(item2))); |
---|
687 | }, |
---|
688 | |
---|
689 | _removeAfter: function(/*Widget or int*/ idx){ |
---|
690 | // summary: |
---|
691 | // removes all widgets after the given widget (or index) |
---|
692 | if(typeof idx != "number"){ |
---|
693 | idx = this.getIndexOfChild(idx); |
---|
694 | } |
---|
695 | if(idx >= 0){ |
---|
696 | dojo.forEach(this.getChildren(), function(c, i){ |
---|
697 | if(i > idx){ |
---|
698 | this.removeChild(c); |
---|
699 | c.destroyRecursive(); |
---|
700 | } |
---|
701 | }, this); |
---|
702 | } |
---|
703 | var children = this.getChildren(), child = children[children.length - 1]; |
---|
704 | var selItem = null; |
---|
705 | while(child && !selItem){ |
---|
706 | var val = child._getSelected ? child._getSelected() : null; |
---|
707 | if(val){ |
---|
708 | selItem = val.item; |
---|
709 | } |
---|
710 | child = child.parentPane; |
---|
711 | } |
---|
712 | if(!this._setInProgress){ |
---|
713 | this._setValue(selItem); |
---|
714 | } |
---|
715 | }, |
---|
716 | |
---|
717 | addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){ |
---|
718 | // summary: |
---|
719 | // adds a child to this rolling list - if passed an insertIndex, |
---|
720 | // then all children from that index on will be removed and destroyed |
---|
721 | // before adding the child. |
---|
722 | if(insertIndex > 0){ |
---|
723 | this._removeAfter(insertIndex - 1); |
---|
724 | } |
---|
725 | this.inherited(arguments); |
---|
726 | if(!widget._started){ |
---|
727 | widget.startup(); |
---|
728 | } |
---|
729 | widget.attr("minWidth", this.minPaneWidth); |
---|
730 | this.layout(); |
---|
731 | if(!this._savedFocus){ |
---|
732 | widget.focus(); |
---|
733 | } |
---|
734 | }, |
---|
735 | |
---|
736 | _setMinPaneWidthAttr: function(value){ |
---|
737 | // summary: |
---|
738 | // Sets the min pane width of all children |
---|
739 | if(value !== this.minPaneWidth){ |
---|
740 | this.minPaneWidth = value; |
---|
741 | dojo.forEach(this.getChildren(), function(c){ |
---|
742 | c.attr("minWidth", value); |
---|
743 | }); |
---|
744 | } |
---|
745 | }, |
---|
746 | |
---|
747 | _updateClass: function(/* Node */ node, /* String */ type, /* Object? */ options){ |
---|
748 | // summary: |
---|
749 | // sets the state of the given node with the given type and options |
---|
750 | // options: |
---|
751 | // an object with key-value-pairs. The values are boolean, if true, |
---|
752 | // the key is added as a class, if false, it is removed. |
---|
753 | if(!this._declaredClasses){ |
---|
754 | this._declaredClasses = ("dojoxRollingList " + this.className).split(" "); |
---|
755 | } |
---|
756 | dojo.forEach(this._declaredClasses, function(c){ |
---|
757 | if(c){ |
---|
758 | dojo.addClass(node, c + type); |
---|
759 | for(var k in options||{}){ |
---|
760 | dojo.toggleClass(node, c + type + k, options[k]); |
---|
761 | } |
---|
762 | dojo.toggleClass(node, c + type + "FocusSelected", |
---|
763 | (dojo.hasClass(node, c + type + "Focus") && dojo.hasClass(node, c + type + "Selected"))); |
---|
764 | dojo.toggleClass(node, c + type + "HoverSelected", |
---|
765 | (dojo.hasClass(node, c + type + "Hover") && dojo.hasClass(node, c + type + "Selected"))); |
---|
766 | } |
---|
767 | }); |
---|
768 | }, |
---|
769 | |
---|
770 | scrollIntoView: function(/*dijit._Widget*/ childWidget){ |
---|
771 | // summary: |
---|
772 | // scrolls the given widget into view |
---|
773 | if(this._scrollingTimeout){ |
---|
774 | window.clearTimeout(this._scrollingTimeout); |
---|
775 | } |
---|
776 | delete this._scrollingTimeout; |
---|
777 | this._scrollingTimeout = window.setTimeout(dojo.hitch(this, function(){ |
---|
778 | if(childWidget.domNode){ |
---|
779 | dojo.window.scrollIntoView(childWidget.domNode); |
---|
780 | } |
---|
781 | delete this._scrollingTimeout; |
---|
782 | return; |
---|
783 | }), 1); |
---|
784 | }, |
---|
785 | |
---|
786 | resize: function(args){ |
---|
787 | dijit.layout._LayoutWidget.prototype.resize.call(this, args); |
---|
788 | }, |
---|
789 | |
---|
790 | layout: function(){ |
---|
791 | var children = this.getChildren(); |
---|
792 | if(this._contentBox){ |
---|
793 | var bn = this.buttonsNode; |
---|
794 | var height = this._contentBox.h - dojo.marginBox(bn).h - |
---|
795 | dojox.html.metrics.getScrollbar().h; |
---|
796 | dojo.forEach(children, function(c){ |
---|
797 | dojo.marginBox(c.domNode, {h: height}); |
---|
798 | }); |
---|
799 | } |
---|
800 | if(this._focusedPane){ |
---|
801 | var foc = this._focusedPane; |
---|
802 | delete this._focusedPane; |
---|
803 | if(!this._savedFocus){ |
---|
804 | foc.focus(); |
---|
805 | } |
---|
806 | }else if(children && children.length){ |
---|
807 | if(!this._savedFocus){ |
---|
808 | children[0].focus(); |
---|
809 | } |
---|
810 | } |
---|
811 | }, |
---|
812 | |
---|
813 | _onChange: function(/*item*/ value){ |
---|
814 | this.onChange(value); |
---|
815 | }, |
---|
816 | |
---|
817 | _setValue: function(/* item */ value){ |
---|
818 | // summary: |
---|
819 | // internally sets the value and fires onchange |
---|
820 | delete this._setInProgress; |
---|
821 | if(!this._itemsMatch(this.value, value)){ |
---|
822 | this.value = value; |
---|
823 | this._onChange(value); |
---|
824 | } |
---|
825 | }, |
---|
826 | |
---|
827 | _setValueAttr: function(/* item */ value){ |
---|
828 | // summary: |
---|
829 | // sets the value of this widget to the given store item |
---|
830 | if(this._itemsMatch(this.value, value) && !value){ return; } |
---|
831 | if(this._setInProgress && this._setInProgress === value){ return; } |
---|
832 | this._setInProgress = value; |
---|
833 | if(!value || !this.store.isItem(value)){ |
---|
834 | var pane = this.getChildren()[0]; |
---|
835 | pane._setSelected(null); |
---|
836 | this._onItemClick(null, pane, null, null); |
---|
837 | return; |
---|
838 | } |
---|
839 | |
---|
840 | var fetchParentItems = dojo.hitch(this, function(/*item*/ item, /*function*/callback){ |
---|
841 | // summary: |
---|
842 | // Fetches the parent items for the given item |
---|
843 | var store = this.store, id; |
---|
844 | if(this.parentAttr && store.getFeatures()["dojo.data.api.Identity"] && |
---|
845 | ((id = this.store.getValue(item, this.parentAttr)) || id === "")){ |
---|
846 | // Fetch by parent attribute |
---|
847 | var cb = function(i){ |
---|
848 | if(store.getIdentity(i) == store.getIdentity(item)){ |
---|
849 | callback(null); |
---|
850 | }else{ |
---|
851 | callback([i]); |
---|
852 | } |
---|
853 | }; |
---|
854 | if(id === ""){ |
---|
855 | callback(null); |
---|
856 | }else if(typeof id == "string"){ |
---|
857 | store.fetchItemByIdentity({identity: id, onItem: cb}); |
---|
858 | }else if(store.isItem(id)){ |
---|
859 | cb(id); |
---|
860 | } |
---|
861 | }else{ |
---|
862 | // Fetch by finding children |
---|
863 | var numCheck = this.childrenAttrs.length; |
---|
864 | var parents = []; |
---|
865 | dojo.forEach(this.childrenAttrs, function(attr){ |
---|
866 | var q = {}; |
---|
867 | q[attr] = item; |
---|
868 | store.fetch({query: q, scope: this, |
---|
869 | onComplete: function(items){ |
---|
870 | if(this._setInProgress !== value){ |
---|
871 | return; |
---|
872 | } |
---|
873 | parents = parents.concat(items); |
---|
874 | numCheck--; |
---|
875 | if(numCheck === 0){ |
---|
876 | callback(parents); |
---|
877 | } |
---|
878 | } |
---|
879 | }); |
---|
880 | }, this); |
---|
881 | } |
---|
882 | }); |
---|
883 | |
---|
884 | var setFromChain = dojo.hitch(this, function(/*item[]*/itemChain, /*integer*/idx){ |
---|
885 | // summary: |
---|
886 | // Sets the value of the widget at the given index in the chain - onchanges are not |
---|
887 | // fired here |
---|
888 | var set = itemChain[idx]; |
---|
889 | var child = this.getChildren()[idx]; |
---|
890 | var conn; |
---|
891 | if(set && child){ |
---|
892 | var fx = dojo.hitch(this, function(){ |
---|
893 | if(conn){ |
---|
894 | this.disconnect(conn); |
---|
895 | } |
---|
896 | delete conn; |
---|
897 | if(this._setInProgress !== value){ |
---|
898 | return; |
---|
899 | } |
---|
900 | var selOpt = dojo.filter(child._menu.getChildren(), function(i){ |
---|
901 | return this._itemsMatch(i.item, set); |
---|
902 | }, this)[0]; |
---|
903 | if(selOpt){ |
---|
904 | idx++; |
---|
905 | child._menu.onItemClick(selOpt, {type: "internal", |
---|
906 | stopPropagation: function(){}, |
---|
907 | preventDefault: function(){}}); |
---|
908 | if(itemChain[idx]){ |
---|
909 | setFromChain(itemChain, idx); |
---|
910 | }else{ |
---|
911 | this._setValue(set); |
---|
912 | this.onItemClick(set, child, this.getChildItems(set)); |
---|
913 | } |
---|
914 | } |
---|
915 | }); |
---|
916 | if(!child.isLoaded){ |
---|
917 | conn = this.connect(child, "onLoad", fx); |
---|
918 | }else{ |
---|
919 | fx(); |
---|
920 | } |
---|
921 | }else if(idx === 0){ |
---|
922 | this.set("value", null); |
---|
923 | } |
---|
924 | }); |
---|
925 | |
---|
926 | var parentChain = []; |
---|
927 | var onParents = dojo.hitch(this, function(/*item[]*/ parents){ |
---|
928 | // summary: |
---|
929 | // recursively grabs the parents - only the first one is followed |
---|
930 | if(parents && parents.length){ |
---|
931 | parentChain.push(parents[0]); |
---|
932 | fetchParentItems(parents[0], onParents); |
---|
933 | }else{ |
---|
934 | if(!parents){ |
---|
935 | parentChain.pop(); |
---|
936 | } |
---|
937 | parentChain.reverse(); |
---|
938 | setFromChain(parentChain, 0); |
---|
939 | } |
---|
940 | }); |
---|
941 | |
---|
942 | // Only set the value in display if we are shown - if we are in a dropdown, |
---|
943 | // and are hidden, don't actually do the scrolling in the display (it can |
---|
944 | // mess up layouts) |
---|
945 | var ns = this.domNode.style; |
---|
946 | if(ns.display == "none" || ns.visibility == "hidden"){ |
---|
947 | this._setValue(value); |
---|
948 | }else if(!this._itemsMatch(value, this._visibleItem)){ |
---|
949 | onParents([value]); |
---|
950 | } |
---|
951 | }, |
---|
952 | |
---|
953 | _onItemClick: function(/* Event */ evt, /* dijit._Contained */ pane, /* item */ item, /* item[]? */ children){ |
---|
954 | // summary: |
---|
955 | // internally called when a widget should pop up its child |
---|
956 | |
---|
957 | if(evt){ |
---|
958 | var itemPane = this._getPaneForItem(item, pane, children); |
---|
959 | var alreadySelected = (evt.type == "click" && evt.alreadySelected); |
---|
960 | |
---|
961 | if(alreadySelected && itemPane){ |
---|
962 | this._removeAfter(pane.getIndexInParent() + 1); |
---|
963 | var next = pane.getNextSibling(); |
---|
964 | if(next && next._setSelected){ |
---|
965 | next._setSelected(null); |
---|
966 | } |
---|
967 | this.scrollIntoView(next); |
---|
968 | }else if(itemPane){ |
---|
969 | this.addChild(itemPane, pane.getIndexInParent() + 1); |
---|
970 | if(this._savedFocus){ |
---|
971 | itemPane.focus(true); |
---|
972 | } |
---|
973 | }else{ |
---|
974 | this._removeAfter(pane); |
---|
975 | this.scrollIntoView(pane); |
---|
976 | } |
---|
977 | }else if(pane){ |
---|
978 | this._removeAfter(pane); |
---|
979 | this.scrollIntoView(pane); |
---|
980 | } |
---|
981 | if(!evt || evt.type != "internal"){ |
---|
982 | this._setValue(item); |
---|
983 | this.onItemClick(item, pane, children); |
---|
984 | } |
---|
985 | this._visibleItem = item; |
---|
986 | }, |
---|
987 | |
---|
988 | _getPaneForItem: function(/* item? */ item, /* dijit._Contained? */ parentPane, /* item[]? */ children){ |
---|
989 | // summary: |
---|
990 | // gets the pane for the given item, and mixes in our needed parts |
---|
991 | // Returns the pane for the given item (null if the root pane) - after mixing in |
---|
992 | // its stuff. |
---|
993 | var ret = this.getPaneForItem(item, parentPane, children); |
---|
994 | ret.store = this.store; |
---|
995 | ret.parentWidget = this; |
---|
996 | ret.parentPane = parentPane||null; |
---|
997 | if(!item){ |
---|
998 | ret.query = this.query; |
---|
999 | ret.queryOptions = this.queryOptions; |
---|
1000 | }else if(children){ |
---|
1001 | ret.items = children; |
---|
1002 | }else{ |
---|
1003 | ret.items = [item]; |
---|
1004 | } |
---|
1005 | return ret; |
---|
1006 | }, |
---|
1007 | |
---|
1008 | _getMenuItemForItem: function(/*item*/ item, /* dijit._Contained */ parentPane){ |
---|
1009 | // summary: |
---|
1010 | // returns a widget for the given store item. The returned |
---|
1011 | // item will be added to this widget's container widget. null will |
---|
1012 | // be passed in for an "empty" item. |
---|
1013 | var store = this.store; |
---|
1014 | if(!item || !store || !store.isItem(item)){ |
---|
1015 | var i = new dijit.MenuItem({ |
---|
1016 | label: "---", |
---|
1017 | disabled: true, |
---|
1018 | iconClass: "dojoxEmpty", |
---|
1019 | focus: function(){ |
---|
1020 | // Do nothing on focus of this guy... |
---|
1021 | } |
---|
1022 | }); |
---|
1023 | this._updateClass(i.domNode, "Item"); |
---|
1024 | return i; |
---|
1025 | }else{ |
---|
1026 | var itemLoaded = store.isItemLoaded(item); |
---|
1027 | var childItems = itemLoaded ? this.getChildItems(item) : undefined; |
---|
1028 | var widgetItem; |
---|
1029 | if(childItems){ |
---|
1030 | widgetItem = this.getMenuItemForItem(item, parentPane, childItems); |
---|
1031 | widgetItem.children = childItems; |
---|
1032 | this._updateClass(widgetItem.domNode, "Item", {"Expanding": true}); |
---|
1033 | if(!widgetItem._started){ |
---|
1034 | var c = widgetItem.connect(widgetItem, "startup", function(){ |
---|
1035 | this.disconnect(c); |
---|
1036 | dojo.style(this.arrowWrapper, "visibility", ""); |
---|
1037 | }); |
---|
1038 | }else{ |
---|
1039 | dojo.style(widgetItem.arrowWrapper, "visibility", ""); |
---|
1040 | } |
---|
1041 | }else{ |
---|
1042 | widgetItem = this.getMenuItemForItem(item, parentPane, null); |
---|
1043 | if(itemLoaded){ |
---|
1044 | this._updateClass(widgetItem.domNode, "Item", {"Single": true}); |
---|
1045 | }else{ |
---|
1046 | this._updateClass(widgetItem.domNode, "Item", {"Unloaded": true}); |
---|
1047 | widgetItem.attr("disabled", true); |
---|
1048 | } |
---|
1049 | } |
---|
1050 | widgetItem.store = this.store; |
---|
1051 | widgetItem.item = item; |
---|
1052 | if(!widgetItem.label){ |
---|
1053 | widgetItem.attr("label", this.store.getLabel(item).replace(/</,"<")); |
---|
1054 | } |
---|
1055 | if(widgetItem.focusNode){ |
---|
1056 | var self = this; |
---|
1057 | widgetItem.focus = function(){ |
---|
1058 | // Don't set our class |
---|
1059 | if(!this.disabled){try{this.focusNode.focus();}catch(e){}} |
---|
1060 | }; |
---|
1061 | widgetItem.connect(widgetItem.focusNode, "onmouseenter", function(){ |
---|
1062 | if(!this.disabled){ |
---|
1063 | self._updateClass(this.domNode, "Item", {"Hover": true}); |
---|
1064 | } |
---|
1065 | }); |
---|
1066 | widgetItem.connect(widgetItem.focusNode, "onmouseleave", function(){ |
---|
1067 | if(!this.disabled){ |
---|
1068 | self._updateClass(this.domNode, "Item", {"Hover": false}); |
---|
1069 | } |
---|
1070 | }); |
---|
1071 | widgetItem.connect(widgetItem.focusNode, "blur", function(){ |
---|
1072 | self._updateClass(this.domNode, "Item", {"Focus": false, "Hover": false}); |
---|
1073 | }); |
---|
1074 | widgetItem.connect(widgetItem.focusNode, "focus", function(){ |
---|
1075 | self._updateClass(this.domNode, "Item", {"Focus": true}); |
---|
1076 | self._focusedPane = parentPane; |
---|
1077 | }); |
---|
1078 | if(this.executeOnDblClick){ |
---|
1079 | widgetItem.connect(widgetItem.focusNode, "ondblclick", function(){ |
---|
1080 | self._onExecute(); |
---|
1081 | }); |
---|
1082 | } |
---|
1083 | } |
---|
1084 | return widgetItem; |
---|
1085 | } |
---|
1086 | }, |
---|
1087 | |
---|
1088 | _setStore: function(/* dojo/data/api/Read */ store){ |
---|
1089 | // summary: |
---|
1090 | // sets the store for this widget */ |
---|
1091 | if(store === this.store && this._started){ return; } |
---|
1092 | this.store = store; |
---|
1093 | this._isIdentity = store.getFeatures()["dojo.data.api.Identity"]; |
---|
1094 | var rootPane = this._getPaneForItem(); |
---|
1095 | this.addChild(rootPane, 0); |
---|
1096 | }, |
---|
1097 | |
---|
1098 | _onKey: function(/*Event*/ e){ |
---|
1099 | // summary: |
---|
1100 | // called when a keypress event happens on this widget |
---|
1101 | if(e.charOrCode == dojo.keys.BACKSPACE){ |
---|
1102 | dojo.stopEvent(e); |
---|
1103 | return; |
---|
1104 | }else if(e.charOrCode == dojo.keys.ESCAPE && this._savedFocus){ |
---|
1105 | try{dijit.focus(this._savedFocus);}catch(e){} |
---|
1106 | dojo.stopEvent(e); |
---|
1107 | return; |
---|
1108 | }else if(e.charOrCode == dojo.keys.LEFT_ARROW || |
---|
1109 | e.charOrCode == dojo.keys.RIGHT_ARROW){ |
---|
1110 | dojo.stopEvent(e); |
---|
1111 | return; |
---|
1112 | } |
---|
1113 | }, |
---|
1114 | |
---|
1115 | _resetValue: function(){ |
---|
1116 | // summary: |
---|
1117 | // function called when the value is reset. |
---|
1118 | this.set("value", this._lastExecutedValue); |
---|
1119 | }, |
---|
1120 | |
---|
1121 | _onCancel: function(){ |
---|
1122 | // summary: |
---|
1123 | // function called when the cancel button is clicked. It |
---|
1124 | // resets its value to whatever was last executed and then cancels |
---|
1125 | this._resetValue(); |
---|
1126 | this.onCancel(); |
---|
1127 | }, |
---|
1128 | |
---|
1129 | _onExecute: function(){ |
---|
1130 | // summary: |
---|
1131 | // function called when the OK button is clicked or when an |
---|
1132 | // item is selected (double-clicked or "enter" pressed on it) |
---|
1133 | this._lastExecutedValue = this.get("value"); |
---|
1134 | this.onExecute(); |
---|
1135 | }, |
---|
1136 | |
---|
1137 | focus: function(){ |
---|
1138 | // summary: |
---|
1139 | // sets the focus state of this widget |
---|
1140 | var wasSaved = this._savedFocus; |
---|
1141 | this._savedFocus = dijit.getFocus(this); |
---|
1142 | if(!this._savedFocus.node){ |
---|
1143 | delete this._savedFocus; |
---|
1144 | } |
---|
1145 | if(!this._focusedPane){ |
---|
1146 | var child = this.getChildren()[0]; |
---|
1147 | if(child && !wasSaved){ |
---|
1148 | child.focus(true); |
---|
1149 | } |
---|
1150 | }else{ |
---|
1151 | this._savedFocus = dijit.getFocus(this); |
---|
1152 | var foc = this._focusedPane; |
---|
1153 | delete this._focusedPane; |
---|
1154 | if(!wasSaved){ |
---|
1155 | foc.focus(true); |
---|
1156 | } |
---|
1157 | } |
---|
1158 | }, |
---|
1159 | |
---|
1160 | handleKey:function(/*Event*/e){ |
---|
1161 | // summary: |
---|
1162 | // handle the key for the given event - called by dropdown |
---|
1163 | // widgets |
---|
1164 | if(e.keyCode == dojo.keys.DOWN_ARROW){ |
---|
1165 | delete this._savedFocus; |
---|
1166 | this.focus(); |
---|
1167 | return false; |
---|
1168 | }else if(e.keyCode == dojo.keys.ESCAPE){ |
---|
1169 | this._onCancel(); |
---|
1170 | return false; |
---|
1171 | } |
---|
1172 | return true; |
---|
1173 | }, |
---|
1174 | |
---|
1175 | _updateChildClasses: function(){ |
---|
1176 | // summary: |
---|
1177 | // Called when a child is added or removed - so that we can |
---|
1178 | // update the classes for styling the "current" one differently than |
---|
1179 | // the others |
---|
1180 | var children = this.getChildren(); |
---|
1181 | var length = children.length; |
---|
1182 | dojo.forEach(children, function(c, idx){ |
---|
1183 | dojo.toggleClass(c.domNode, "dojoxRollingListPaneCurrentChild", (idx == (length - 1))); |
---|
1184 | dojo.toggleClass(c.domNode, "dojoxRollingListPaneCurrentSelected", (idx == (length - 2))); |
---|
1185 | }); |
---|
1186 | }, |
---|
1187 | |
---|
1188 | startup: function(){ |
---|
1189 | if(this._started){ return; } |
---|
1190 | if(!this.getParent || !this.getParent()){ |
---|
1191 | this.resize(); |
---|
1192 | this.connect(dojo.global, "onresize", "resize"); |
---|
1193 | } |
---|
1194 | this.connect(this, "addChild", "_updateChildClasses"); |
---|
1195 | this.connect(this, "removeChild", "_updateChildClasses"); |
---|
1196 | this._setStore(this.store); |
---|
1197 | this.set("showButtons", this.showButtons); |
---|
1198 | this.inherited(arguments); |
---|
1199 | this._lastExecutedValue = this.get("value"); |
---|
1200 | }, |
---|
1201 | |
---|
1202 | getChildItems: function(/*item*/ item){ |
---|
1203 | // summary: |
---|
1204 | // Returns the child items for the given store item |
---|
1205 | var childItems, store = this.store; |
---|
1206 | dojo.forEach(this.childrenAttrs, function(attr){ |
---|
1207 | var vals = store.getValues(item, attr); |
---|
1208 | if(vals && vals.length){ |
---|
1209 | childItems = (childItems||[]).concat(vals); |
---|
1210 | } |
---|
1211 | }); |
---|
1212 | return childItems; |
---|
1213 | }, |
---|
1214 | |
---|
1215 | getMenuItemForItem: function(/*item*/ item, /* dijit._Contained */ parentPane, /* item[]? */ children){ |
---|
1216 | // summary: |
---|
1217 | // user overridable function to return a widget for the given item |
---|
1218 | // and its children. |
---|
1219 | return new dijit.MenuItem({}); |
---|
1220 | }, |
---|
1221 | |
---|
1222 | getPaneForItem: function(/* item? */ item, /* dijit._Contained? */ parentPane, /* item[]? */ children){ |
---|
1223 | // summary: |
---|
1224 | // user-overridable function to return a pane that corresponds |
---|
1225 | // to the given item in the store. It can return null to not add a new pane |
---|
1226 | // (ie, you are planning on doing something else with it in onItemClick) |
---|
1227 | |
---|
1228 | // Item is undefined for the root pane, children is undefined for non-group panes |
---|
1229 | if(!item || children){ |
---|
1230 | return new dojox.widget._RollingListGroupPane({}); |
---|
1231 | }else{ |
---|
1232 | return null; |
---|
1233 | } |
---|
1234 | }, |
---|
1235 | |
---|
1236 | onItemClick: function(/* item */ item, /* dijit._Contained */ pane, /* item[]? */ children){ |
---|
1237 | // summary: |
---|
1238 | // called when an item is clicked - it receives the store item |
---|
1239 | }, |
---|
1240 | |
---|
1241 | onExecute: function(){ |
---|
1242 | // summary: |
---|
1243 | // exists so that popups don't disappear too soon |
---|
1244 | }, |
---|
1245 | |
---|
1246 | onCancel: function(){ |
---|
1247 | // summary: |
---|
1248 | // exists so that we can close ourselves if we wish |
---|
1249 | }, |
---|
1250 | |
---|
1251 | onChange: function(/* item */ value){ |
---|
1252 | // summary: |
---|
1253 | // called when the value of this widget has changed |
---|
1254 | } |
---|
1255 | |
---|
1256 | }); |
---|