1211 lines
37 KiB
JavaScript
1211 lines
37 KiB
JavaScript
|
//>>built
|
||
|
// wrapped by build app
|
||
|
define("dojox/widget/RollingList", ["dijit","dojo","dojox","dojo/i18n!dijit/nls/common","dojo/require!dojo/window,dijit/layout/ContentPane,dijit/_Templated,dijit/_Contained,dijit/layout/_LayoutWidget,dijit/Menu,dijit/form/Button,dijit/focus,dijit/_base/focus,dojox/html/metrics,dojo/i18n"], function(dijit,dojo,dojox){
|
||
|
dojo.provide("dojox.widget.RollingList");
|
||
|
dojo.experimental("dojox.widget.RollingList");
|
||
|
|
||
|
dojo.require("dojo.window");
|
||
|
|
||
|
dojo.require("dijit.layout.ContentPane");
|
||
|
dojo.require("dijit._Templated");
|
||
|
dojo.require("dijit._Contained");
|
||
|
dojo.require("dijit.layout._LayoutWidget");
|
||
|
dojo.require("dijit.Menu");
|
||
|
dojo.require("dijit.form.Button");
|
||
|
dojo.require("dijit.focus"); // dijit.focus()
|
||
|
dojo.require("dijit._base.focus"); // dijit.getFocus()
|
||
|
|
||
|
dojo.require("dojox.html.metrics");
|
||
|
|
||
|
dojo.require("dojo.i18n");
|
||
|
dojo.requireLocalization("dijit", "common");
|
||
|
|
||
|
dojo.declare("dojox.widget._RollingListPane",
|
||
|
[dijit.layout.ContentPane, dijit._Templated, dijit._Contained], {
|
||
|
// summary: a core pane that can be attached to a RollingList. All panes
|
||
|
// should extend this one
|
||
|
|
||
|
// templateString: string
|
||
|
// our template
|
||
|
templateString: '<div class="dojoxRollingListPane"><table><tbody><tr><td dojoAttachPoint="containerNode"></td></tr></tbody></div>',
|
||
|
|
||
|
// parentWidget: dojox.widget.RollingList
|
||
|
// Our rolling list widget
|
||
|
parentWidget: null,
|
||
|
|
||
|
// parentPane: dojox.widget._RollingListPane
|
||
|
// The pane that immediately precedes ours
|
||
|
parentPane: null,
|
||
|
|
||
|
// store: store
|
||
|
// the store we must use
|
||
|
store: null,
|
||
|
|
||
|
// items: item[]
|
||
|
// an array of (possibly not-yet-loaded) items to display in this.
|
||
|
// If this array is null, then the query and query options are used to
|
||
|
// get the top-level items to use. This array is also used to watch and
|
||
|
// see if the pane needs to be reloaded (store notifications are handled)
|
||
|
// by the pane
|
||
|
items: null,
|
||
|
|
||
|
// query: object
|
||
|
// a query to pass to the datastore. This is only used if items are null
|
||
|
query: null,
|
||
|
|
||
|
// queryOptions: object
|
||
|
// query options to be passed to the datastore
|
||
|
queryOptions: null,
|
||
|
|
||
|
// focusByNode: boolean
|
||
|
// set to false if the subclass will handle its own node focusing
|
||
|
_focusByNode: true,
|
||
|
|
||
|
// minWidth: integer
|
||
|
// the width (in px) for this pane
|
||
|
minWidth: 0,
|
||
|
|
||
|
_setContentAndScroll: function(/*String|DomNode|Nodelist*/cont, /*Boolean?*/isFakeContent){
|
||
|
// summary: sets the value of the content and scrolls it into view
|
||
|
this._setContent(cont, isFakeContent);
|
||
|
this.parentWidget.scrollIntoView(this);
|
||
|
},
|
||
|
|
||
|
_updateNodeWidth: function(n, min){
|
||
|
// summary: updates the min width of the pane to be minPaneWidth
|
||
|
n.style.width = "";
|
||
|
var nWidth = dojo.marginBox(n).w;
|
||
|
if(nWidth < min){
|
||
|
dojo.marginBox(n, {w: min});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onMinWidthChange: function(v){
|
||
|
// Called when the min width of a pane has changed
|
||
|
this._updateNodeWidth(this.domNode, v);
|
||
|
},
|
||
|
|
||
|
_setMinWidthAttr: function(v){
|
||
|
if(v !== this.minWidth){
|
||
|
this.minWidth = v;
|
||
|
this._onMinWidthChange(v);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
startup: function(){
|
||
|
if(this._started){ return; }
|
||
|
if(this.store && this.store.getFeatures()["dojo.data.api.Notification"]){
|
||
|
window.setTimeout(dojo.hitch(this, function(){
|
||
|
// Set connections after a slight timeout to avoid getting in the
|
||
|
// condition where we are setting them while events are still
|
||
|
// being fired
|
||
|
this.connect(this.store, "onSet", "_onSetItem");
|
||
|
this.connect(this.store, "onNew", "_onNewItem");
|
||
|
this.connect(this.store, "onDelete", "_onDeleteItem");
|
||
|
}), 1);
|
||
|
}
|
||
|
this.connect(this.focusNode||this.domNode, "onkeypress", "_focusKey");
|
||
|
this.parentWidget._updateClass(this.domNode, "Pane");
|
||
|
this.inherited(arguments);
|
||
|
this._onMinWidthChange(this.minWidth);
|
||
|
},
|
||
|
|
||
|
_focusKey: function(/*Event*/e){
|
||
|
// summary: called when a keypress happens on the widget
|
||
|
if(e.charOrCode == dojo.keys.BACKSPACE){
|
||
|
dojo.stopEvent(e);
|
||
|
return;
|
||
|
}else if(e.charOrCode == dojo.keys.LEFT_ARROW && this.parentPane){
|
||
|
this.parentPane.focus();
|
||
|
this.parentWidget.scrollIntoView(this.parentPane);
|
||
|
}else if(e.charOrCode == dojo.keys.ENTER){
|
||
|
this.parentWidget._onExecute();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
focus: function(/*boolean*/force){
|
||
|
// summary: sets the focus to this current widget
|
||
|
if(this.parentWidget._focusedPane != this){
|
||
|
this.parentWidget._focusedPane = this;
|
||
|
this.parentWidget.scrollIntoView(this);
|
||
|
if(this._focusByNode && (!this.parentWidget._savedFocus || force)){
|
||
|
try{(this.focusNode||this.domNode).focus();}catch(e){}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onShow: function(){
|
||
|
// summary: checks that the store is loaded
|
||
|
if((this.store || this.items) && ((this.refreshOnShow && this.domNode) || (!this.isLoaded && this.domNode))){
|
||
|
this.refresh();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_load: function(){
|
||
|
// summary: sets the "loading" message and then kicks off a query asyncronously
|
||
|
this.isLoaded = false;
|
||
|
if(this.items){
|
||
|
this._setContentAndScroll(this.onLoadStart(), true);
|
||
|
window.setTimeout(dojo.hitch(this, "_doQuery"), 1);
|
||
|
}else{
|
||
|
this._doQuery();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_doLoadItems: function(/*item[]*/items, /*function*/callback){
|
||
|
// summary: loads the given items, and then calls the callback when they
|
||
|
// are finished.
|
||
|
var _waitCount = 0, store = this.store;
|
||
|
dojo.forEach(items, function(item){
|
||
|
if(!store.isItemLoaded(item)){ _waitCount++; }
|
||
|
});
|
||
|
if(_waitCount === 0){
|
||
|
callback();
|
||
|
}else{
|
||
|
var onItem = function(item){
|
||
|
_waitCount--;
|
||
|
if((_waitCount) === 0){
|
||
|
callback();
|
||
|
}
|
||
|
};
|
||
|
dojo.forEach(items, function(item){
|
||
|
if(!store.isItemLoaded(item)){
|
||
|
store.loadItem({item: item, onItem: onItem});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_doQuery: function(){
|
||
|
// summary: either runs the query or loads potentially not-yet-loaded items.
|
||
|
if(!this.domNode){return;}
|
||
|
var preload = this.parentWidget.preloadItems;
|
||
|
preload = (preload === true || (this.items && this.items.length <= Number(preload)));
|
||
|
if(this.items && preload){
|
||
|
this._doLoadItems(this.items, dojo.hitch(this, "onItems"));
|
||
|
}else if(this.items){
|
||
|
this.onItems();
|
||
|
}else{
|
||
|
this._setContentAndScroll(this.onFetchStart(), true);
|
||
|
this.store.fetch({query: this.query,
|
||
|
onComplete: function(items){
|
||
|
this.items = items;
|
||
|
this.onItems();
|
||
|
},
|
||
|
onError: function(e){
|
||
|
this._onError("Fetch", e);
|
||
|
},
|
||
|
scope: this});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_hasItem: function(/* item */ item){
|
||
|
// summary: returns whether or not the given item is handled by this
|
||
|
// pane
|
||
|
var items = this.items || [];
|
||
|
for(var i = 0, myItem; (myItem = items[i]); i++){
|
||
|
if(this.parentWidget._itemsMatch(myItem, item)){
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
_onSetItem: function(/* item */ item,
|
||
|
/* attribute-name-string */ attribute,
|
||
|
/* object | array */ oldValue,
|
||
|
/* object | array */ newValue){
|
||
|
// Summary: called when an item in the store has changed
|
||
|
if(this._hasItem(item)){
|
||
|
this.refresh();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onNewItem: function(/* item */ newItem, /*object?*/ parentInfo){
|
||
|
// Summary: called when an item is added to the store
|
||
|
var sel;
|
||
|
if((!parentInfo && !this.parentPane) ||
|
||
|
(parentInfo && this.parentPane && this.parentPane._hasItem(parentInfo.item) &&
|
||
|
(sel = this.parentPane._getSelected()) && this.parentWidget._itemsMatch(sel.item, parentInfo.item))){
|
||
|
this.items.push(newItem);
|
||
|
this.refresh();
|
||
|
}else if(parentInfo && this.parentPane && this._hasItem(parentInfo.item)){
|
||
|
this.refresh();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onDeleteItem: function(/* item */ deletedItem){
|
||
|
// Summary: called when an item is removed from the store
|
||
|
if(this._hasItem(deletedItem)){
|
||
|
this.items = dojo.filter(this.items, function(i){
|
||
|
return (i != deletedItem);
|
||
|
});
|
||
|
this.refresh();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
onFetchStart: function(){
|
||
|
// summary:
|
||
|
// called before a fetch starts
|
||
|
return this.loadingMessage;
|
||
|
},
|
||
|
|
||
|
onFetchError: function(/*Error*/ error){
|
||
|
// summary:
|
||
|
// called when a fetch error occurs.
|
||
|
return this.errorMessage;
|
||
|
},
|
||
|
|
||
|
onLoadStart: function(){
|
||
|
// summary:
|
||
|
// called before a load starts
|
||
|
return this.loadingMessage;
|
||
|
},
|
||
|
|
||
|
onLoadError: function(/*Error*/ error){
|
||
|
// summary:
|
||
|
// called when a load error occurs.
|
||
|
return this.errorMessage;
|
||
|
},
|
||
|
|
||
|
onItems: function(){
|
||
|
// summary:
|
||
|
// called after a fetch or load - at this point, this.items should be
|
||
|
// set and loaded. Override this function to "do your stuff"
|
||
|
if(!this.onLoadDeferred){
|
||
|
this.cancel();
|
||
|
this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
|
||
|
}
|
||
|
this._onLoadHandler();
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
dojo.declare("dojox.widget._RollingListGroupPane",
|
||
|
[dojox.widget._RollingListPane], {
|
||
|
// summary: a pane that will handle groups (treats them as menu items)
|
||
|
|
||
|
// templateString: string
|
||
|
// our template
|
||
|
templateString: '<div><div dojoAttachPoint="containerNode"></div>' +
|
||
|
'<div dojoAttachPoint="menuContainer">' +
|
||
|
'<div dojoAttachPoint="menuNode"></div>' +
|
||
|
'</div></div>',
|
||
|
|
||
|
// _menu: dijit.Menu
|
||
|
// The menu that we will call addChild() on for adding items
|
||
|
_menu: null,
|
||
|
|
||
|
_setContent: function(/*String|DomNode|Nodelist*/cont){
|
||
|
if(!this._menu){
|
||
|
// Only set the content if we don't already have a menu
|
||
|
this.inherited(arguments);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onMinWidthChange: function(v){
|
||
|
// override and resize the menu instead
|
||
|
if(!this._menu){ return; }
|
||
|
var dWidth = dojo.marginBox(this.domNode).w;
|
||
|
var mWidth = dojo.marginBox(this._menu.domNode).w;
|
||
|
this._updateNodeWidth(this._menu.domNode, v - (dWidth - mWidth));
|
||
|
},
|
||
|
|
||
|
onItems: function(){
|
||
|
// summary:
|
||
|
// called after a fetch or load
|
||
|
var selectItem, hadChildren = false;
|
||
|
if(this._menu){
|
||
|
selectItem = this._getSelected();
|
||
|
this._menu.destroyRecursive();
|
||
|
}
|
||
|
this._menu = this._getMenu();
|
||
|
var child, selectMenuItem;
|
||
|
if(this.items.length){
|
||
|
dojo.forEach(this.items, function(item){
|
||
|
child = this.parentWidget._getMenuItemForItem(item, this);
|
||
|
if(child){
|
||
|
if(selectItem && this.parentWidget._itemsMatch(child.item, selectItem.item)){
|
||
|
selectMenuItem = child;
|
||
|
}
|
||
|
this._menu.addChild(child);
|
||
|
}
|
||
|
}, this);
|
||
|
}else{
|
||
|
child = this.parentWidget._getMenuItemForItem(null, this);
|
||
|
if(child){
|
||
|
this._menu.addChild(child);
|
||
|
}
|
||
|
}
|
||
|
if(selectMenuItem){
|
||
|
this._setSelected(selectMenuItem);
|
||
|
if((selectItem && !selectItem.children && selectMenuItem.children) ||
|
||
|
(selectItem && selectItem.children && !selectMenuItem.children)){
|
||
|
var itemPane = this.parentWidget._getPaneForItem(selectMenuItem.item, this, selectMenuItem.children);
|
||
|
if(itemPane){
|
||
|
this.parentWidget.addChild(itemPane, this.getIndexInParent() + 1);
|
||
|
}else{
|
||
|
this.parentWidget._removeAfter(this);
|
||
|
this.parentWidget._onItemClick(null, this, selectMenuItem.item, selectMenuItem.children);
|
||
|
}
|
||
|
}
|
||
|
}else if(selectItem){
|
||
|
this.parentWidget._removeAfter(this);
|
||
|
}
|
||
|
this.containerNode.innerHTML = "";
|
||
|
this.containerNode.appendChild(this._menu.domNode);
|
||
|
this.parentWidget.scrollIntoView(this);
|
||
|
this._checkScrollConnection(true);
|
||
|
this.inherited(arguments);
|
||
|
this._onMinWidthChange(this.minWidth);
|
||
|
},
|
||
|
|
||
|
_checkScrollConnection: function(doLoad){
|
||
|
// summary: checks whether or not we need to connect to our onscroll
|
||
|
// function
|
||
|
var store = this.store
|
||
|
if(this._scrollConn){
|
||
|
this.disconnect(this._scrollConn);
|
||
|
}
|
||
|
delete this._scrollConn;
|
||
|
if(!dojo.every(this.items, function(i){return store.isItemLoaded(i);})){
|
||
|
if(doLoad){
|
||
|
this._loadVisibleItems();
|
||
|
}
|
||
|
this._scrollConn = this.connect(this.domNode, "onscroll", "_onScrollPane");
|
||
|
}
|
||
|
},
|
||
|
|
||
|
startup: function(){
|
||
|
this.inherited(arguments);
|
||
|
this.parentWidget._updateClass(this.domNode, "GroupPane");
|
||
|
},
|
||
|
|
||
|
focus: function(/*boolean*/force){
|
||
|
// summary: sets the focus to this current widget
|
||
|
if(this._menu){
|
||
|
if(this._pendingFocus){
|
||
|
this.disconnect(this._pendingFocus);
|
||
|
}
|
||
|
delete this._pendingFocus;
|
||
|
|
||
|
// We focus the right widget - either the focusedChild, the
|
||
|
// selected node, the first menu item, or the menu itself
|
||
|
var focusWidget = this._menu.focusedChild;
|
||
|
if(!focusWidget){
|
||
|
var focusNode = dojo.query(".dojoxRollingListItemSelected", this.domNode)[0];
|
||
|
if(focusNode){
|
||
|
focusWidget = dijit.byNode(focusNode);
|
||
|
}
|
||
|
}
|
||
|
if(!focusWidget){
|
||
|
focusWidget = this._menu.getChildren()[0] || this._menu;
|
||
|
}
|
||
|
this._focusByNode = false;
|
||
|
if(focusWidget.focusNode){
|
||
|
if(!this.parentWidget._savedFocus || force){
|
||
|
try{focusWidget.focusNode.focus();}catch(e){}
|
||
|
}
|
||
|
window.setTimeout(function(){
|
||
|
try{
|
||
|
dojo.window.scrollIntoView(focusWidget.focusNode);
|
||
|
}catch(e){}
|
||
|
}, 1);
|
||
|
}else if(focusWidget.focus){
|
||
|
if(!this.parentWidget._savedFocus || force){
|
||
|
focusWidget.focus();
|
||
|
}
|
||
|
}else{
|
||
|
this._focusByNode = true;
|
||
|
}
|
||
|
this.inherited(arguments);
|
||
|
}else if(!this._pendingFocus){
|
||
|
this._pendingFocus = this.connect(this, "onItems", "focus");
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getMenu: function(){
|
||
|
// summary: returns a widget to be used for the container widget.
|
||
|
var self = this;
|
||
|
var menu = new dijit.Menu({
|
||
|
parentMenu: this.parentPane ? this.parentPane._menu : null,
|
||
|
onCancel: function(/*Boolean*/ closeAll){
|
||
|
if(self.parentPane){
|
||
|
self.parentPane.focus(true);
|
||
|
}
|
||
|
},
|
||
|
_moveToPopup: function(/*Event*/ evt){
|
||
|
if(this.focusedChild && !this.focusedChild.disabled){
|
||
|
this.focusedChild._onClick(evt);
|
||
|
}
|
||
|
}
|
||
|
}, this.menuNode);
|
||
|
this.connect(menu, "onItemClick", function(/*dijit.MenuItem*/ item, /*Event*/ evt){
|
||
|
if(item.disabled){ return; }
|
||
|
evt.alreadySelected = dojo.hasClass(item.domNode, "dojoxRollingListItemSelected");
|
||
|
if(evt.alreadySelected &&
|
||
|
((evt.type == "keypress" && evt.charOrCode != dojo.keys.ENTER) ||
|
||
|
(evt.type == "internal"))){
|
||
|
var p = this.parentWidget.getChildren()[this.getIndexInParent() + 1];
|
||
|
if(p){
|
||
|
p.focus(true);
|
||
|
this.parentWidget.scrollIntoView(p);
|
||
|
}
|
||
|
}else{
|
||
|
this._setSelected(item, menu);
|
||
|
this.parentWidget._onItemClick(evt, this, item.item, item.children);
|
||
|
if(evt.type == "keypress" && evt.charOrCode == dojo.keys.ENTER){
|
||
|
this.parentWidget._onExecute();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
if(!menu._started){
|
||
|
menu.startup();
|
||
|
}
|
||
|
return menu;
|
||
|
},
|
||
|
|
||
|
_onScrollPane: function(){
|
||
|
// summary: called when the pane has been scrolled - it sets a timeout
|
||
|
// so that we don't try and load our visible items too often during
|
||
|
// a scroll
|
||
|
if(this._visibleLoadPending){
|
||
|
window.clearTimeout(this._visibleLoadPending);
|
||
|
}
|
||
|
this._visibleLoadPending = window.setTimeout(dojo.hitch(this, "_loadVisibleItems"), 500);
|
||
|
},
|
||
|
|
||
|
_loadVisibleItems: function(){
|
||
|
// summary: loads the items that are currently visible in the pane
|
||
|
delete this._visibleLoadPending
|
||
|
var menu = this._menu;
|
||
|
if(!menu){ return; }
|
||
|
var children = menu.getChildren();
|
||
|
if(!children || !children.length){ return; }
|
||
|
var gpbme = function(n, m, pb){
|
||
|
var s = dojo.getComputedStyle(n);
|
||
|
var r = 0;
|
||
|
if(m){ r += dojo._getMarginExtents(n, s).t; }
|
||
|
if(pb){ r += dojo._getPadBorderExtents(n, s).t; }
|
||
|
return r;
|
||
|
};
|
||
|
var topOffset = gpbme(this.domNode, false, true) +
|
||
|
gpbme(this.containerNode, true, true) +
|
||
|
gpbme(menu.domNode, true, true) +
|
||
|
gpbme(children[0].domNode, true, false);
|
||
|
var h = dojo.contentBox(this.domNode).h;
|
||
|
var minOffset = this.domNode.scrollTop - topOffset - (h/2);
|
||
|
var maxOffset = minOffset + (3*h/2);
|
||
|
var menuItemsToLoad = dojo.filter(children, function(c){
|
||
|
var cnt = c.domNode.offsetTop;
|
||
|
var s = c.store;
|
||
|
var i = c.item;
|
||
|
return (cnt >= minOffset && cnt <= maxOffset && !s.isItemLoaded(i));
|
||
|
})
|
||
|
var itemsToLoad = dojo.map(menuItemsToLoad, function(c){
|
||
|
return c.item;
|
||
|
});
|
||
|
var onItems = dojo.hitch(this, function(){
|
||
|
var selectItem = this._getSelected();
|
||
|
var selectMenuItem;
|
||
|
dojo.forEach(itemsToLoad, function(item, idx){
|
||
|
var newItem = this.parentWidget._getMenuItemForItem(item, this);
|
||
|
var oItem = menuItemsToLoad[idx];
|
||
|
var oIdx = oItem.getIndexInParent();
|
||
|
menu.removeChild(oItem);
|
||
|
if(newItem){
|
||
|
if(selectItem && this.parentWidget._itemsMatch(newItem.item, selectItem.item)){
|
||
|
selectMenuItem = newItem;
|
||
|
}
|
||
|
menu.addChild(newItem, oIdx);
|
||
|
if(menu.focusedChild == oItem){
|
||
|
menu.focusChild(newItem);
|
||
|
}
|
||
|
}
|
||
|
oItem.destroy();
|
||
|
}, this);
|
||
|
this._checkScrollConnection(false);
|
||
|
});
|
||
|
this._doLoadItems(itemsToLoad, onItems);
|
||
|
},
|
||
|
|
||
|
_getSelected: function(/*dijit.Menu?*/ menu){
|
||
|
// summary:
|
||
|
// returns the selected menu item - or null if none are selected
|
||
|
if(!menu){ menu = this._menu; }
|
||
|
if(menu){
|
||
|
var children = this._menu.getChildren();
|
||
|
for(var i = 0, item; (item = children[i]); i++){
|
||
|
if(dojo.hasClass(item.domNode, "dojoxRollingListItemSelected")){
|
||
|
return item;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
},
|
||
|
|
||
|
_setSelected: function(/*dijit.MenuItem?*/ item, /*dijit.Menu?*/ menu){
|
||
|
// summary:
|
||
|
// selectes the given item in the given menu (defaults to pane's menu)
|
||
|
if(!menu){ menu = this._menu;}
|
||
|
if(menu){
|
||
|
dojo.forEach(menu.getChildren(), function(i){
|
||
|
this.parentWidget._updateClass(i.domNode, "Item", {"Selected": (item && (i == item && !i.disabled))});
|
||
|
}, this);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
dojo.declare("dojox.widget.RollingList",
|
||
|
[dijit._Widget, dijit._Templated, dijit._Container], {
|
||
|
// summary: a rolling list that can be tied to a data store with children
|
||
|
|
||
|
// templateString: String
|
||
|
// The template to be used to construct the widget.
|
||
|
templateString: dojo.cache("dojox.widget", "RollingList/RollingList.html", "<div class=\"dojoxRollingList ${className}\"\n\t><div class=\"dojoxRollingListContainer\" dojoAttachPoint=\"containerNode\" dojoAttachEvent=\"onkeypress:_onKey\"\n\t></div\n\t><div class=\"dojoxRollingListButtons\" dojoAttachPoint=\"buttonsNode\"\n ><button dojoType=\"dijit.form.Button\" dojoAttachPoint=\"okButton\"\n\t\t\t\tdojoAttachEvent=\"onClick:_onExecute\">${okButtonLabel}</button\n ><button dojoType=\"dijit.form.Button\" dojoAttachPoint=\"cancelButton\"\n\t\t\t\tdojoAttachEvent=\"onClick:_onCancel\">${cancelButtonLabel}</button\n\t></div\n></div>\n"),
|
||
|
widgetsInTemplate: true,
|
||
|
|
||
|
// className: string
|
||
|
// an additional class (or space-separated classes) to add for our widget
|
||
|
className: "",
|
||
|
|
||
|
// store: store
|
||
|
// the store we must use
|
||
|
store: null,
|
||
|
|
||
|
// query: object
|
||
|
// a query to pass to the datastore. This is only used if items are null
|
||
|
query: null,
|
||
|
|
||
|
// queryOptions: object
|
||
|
// query options to be passed to the datastore
|
||
|
queryOptions: null,
|
||
|
|
||
|
// childrenAttrs: String[]
|
||
|
// one ore more attributes that holds children of a node
|
||
|
childrenAttrs: ["children"],
|
||
|
|
||
|
// parentAttr: string
|
||
|
// the attribute to read for finding our parent item (if any)
|
||
|
parentAttr: "",
|
||
|
|
||
|
// value: item
|
||
|
// The value that has been selected
|
||
|
value: null,
|
||
|
|
||
|
// executeOnDblClick: boolean
|
||
|
// Set to true if you want to call onExecute when an item is
|
||
|
// double-clicked, false if you want to call onExecute yourself. (mainly
|
||
|
// used for popups to control how they want to be handled)
|
||
|
executeOnDblClick: true,
|
||
|
|
||
|
// preloadItems: boolean or int
|
||
|
// if set to true, then onItems will be called only *after* all items have
|
||
|
// been loaded (ie store.isLoaded will return true for all of them). If
|
||
|
// false, then no preloading will occur. If set to an integer, preloading
|
||
|
// will occur if the number of items is less than or equal to the value
|
||
|
// of the integer. The onItems function will need to be aware of handling
|
||
|
// items that may not be loaded
|
||
|
preloadItems: false,
|
||
|
|
||
|
// showButtons: boolean
|
||
|
// if set to true, then buttons for "OK" and "Cancel" will be provided
|
||
|
showButtons: false,
|
||
|
|
||
|
// okButtonLabel: string
|
||
|
// The string to use for the OK button - will use dijit's common "OK" string
|
||
|
// if not set
|
||
|
okButtonLabel: "",
|
||
|
|
||
|
// cancelButtonLabel: string
|
||
|
// The string to use for the Cancel button - will use dijit's common
|
||
|
// "Cancel" string if not set
|
||
|
cancelButtonLabel: "",
|
||
|
|
||
|
// minPaneWidth: integer
|
||
|
// the minimum pane width (in px) for all child panes. If they are narrower,
|
||
|
// the width will be increased to this value.
|
||
|
minPaneWidth: 0,
|
||
|
|
||
|
postMixInProperties: function(){
|
||
|
// summary: Mix in our labels, if they are not set
|
||
|
this.inherited(arguments);
|
||
|
var loc = dojo.i18n.getLocalization("dijit", "common");
|
||
|
this.okButtonLabel = this.okButtonLabel || loc.buttonOk;
|
||
|
this.cancelButtonLabel = this.cancelButtonLabel || loc.buttonCancel;
|
||
|
},
|
||
|
|
||
|
_setShowButtonsAttr: function(doShow){
|
||
|
// summary: Sets the visibility of the buttons for the widget
|
||
|
var needsLayout = false;
|
||
|
if((this.showButtons != doShow && this._started) ||
|
||
|
(this.showButtons == doShow && !this.started)){
|
||
|
needsLayout = true;
|
||
|
}
|
||
|
dojo.toggleClass(this.domNode, "dojoxRollingListButtonsHidden", !doShow);
|
||
|
this.showButtons = doShow;
|
||
|
if(needsLayout){
|
||
|
if(this._started){
|
||
|
this.layout();
|
||
|
}else{
|
||
|
window.setTimeout(dojo.hitch(this, "layout"), 0);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_itemsMatch: function(/*item*/ item1, /*item*/ item2){
|
||
|
// Summary: returns whether or not the two items match - checks ID if
|
||
|
// they aren't the exact same object
|
||
|
if(!item1 && !item2){
|
||
|
return true;
|
||
|
}else if(!item1 || !item2){
|
||
|
return false;
|
||
|
}
|
||
|
return (item1 == item2 ||
|
||
|
(this._isIdentity && this.store.getIdentity(item1) == this.store.getIdentity(item2)));
|
||
|
},
|
||
|
|
||
|
_removeAfter: function(/*Widget or int*/ idx){
|
||
|
// summary: removes all widgets after the given widget (or index)
|
||
|
if(typeof idx != "number"){
|
||
|
idx = this.getIndexOfChild(idx);
|
||
|
}
|
||
|
if(idx >= 0){
|
||
|
dojo.forEach(this.getChildren(), function(c, i){
|
||
|
if(i > idx){
|
||
|
this.removeChild(c);
|
||
|
c.destroyRecursive();
|
||
|
}
|
||
|
}, this);
|
||
|
}
|
||
|
var children = this.getChildren(), child = children[children.length - 1];
|
||
|
var selItem = null;
|
||
|
while(child && !selItem){
|
||
|
var val = child._getSelected ? child._getSelected() : null;
|
||
|
if(val){
|
||
|
selItem = val.item;
|
||
|
}
|
||
|
child = child.parentPane;
|
||
|
}
|
||
|
if(!this._setInProgress){
|
||
|
this._setValue(selItem);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
|
||
|
// summary: adds a child to this rolling list - if passed an insertIndex,
|
||
|
// then all children from that index on will be removed and destroyed
|
||
|
// before adding the child.
|
||
|
if(insertIndex > 0){
|
||
|
this._removeAfter(insertIndex - 1);
|
||
|
}
|
||
|
this.inherited(arguments);
|
||
|
if(!widget._started){
|
||
|
widget.startup();
|
||
|
}
|
||
|
widget.attr("minWidth", this.minPaneWidth);
|
||
|
this.layout();
|
||
|
if(!this._savedFocus){
|
||
|
widget.focus();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_setMinPaneWidthAttr: function(value){
|
||
|
// summary:
|
||
|
// Sets the min pane width of all children
|
||
|
if(value !== this.minPaneWidth){
|
||
|
this.minPaneWidth = value;
|
||
|
dojo.forEach(this.getChildren(), function(c){
|
||
|
c.attr("minWidth", value);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_updateClass: function(/* Node */ node, /* String */ type, /* Object? */ options){
|
||
|
// summary:
|
||
|
// sets the state of the given node with the given type and options
|
||
|
// options:
|
||
|
// an object with key-value-pairs. The values are boolean, if true,
|
||
|
// the key is added as a class, if false, it is removed.
|
||
|
if(!this._declaredClasses){
|
||
|
this._declaredClasses = ("dojoxRollingList " + this.className).split(" ");
|
||
|
}
|
||
|
dojo.forEach(this._declaredClasses, function(c){
|
||
|
if(c){
|
||
|
dojo.addClass(node, c + type);
|
||
|
for(var k in options||{}){
|
||
|
dojo.toggleClass(node, c + type + k, options[k]);
|
||
|
}
|
||
|
dojo.toggleClass(node, c + type + "FocusSelected",
|
||
|
(dojo.hasClass(node, c + type + "Focus") && dojo.hasClass(node, c + type + "Selected")));
|
||
|
dojo.toggleClass(node, c + type + "HoverSelected",
|
||
|
(dojo.hasClass(node, c + type + "Hover") && dojo.hasClass(node, c + type + "Selected")));
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
scrollIntoView: function(/*dijit._Widget*/ childWidget){
|
||
|
// summary: scrolls the given widget into view
|
||
|
if(this._scrollingTimeout){
|
||
|
window.clearTimeout(this._scrollingTimeout);
|
||
|
}
|
||
|
delete this._scrollingTimeout;
|
||
|
this._scrollingTimeout = window.setTimeout(dojo.hitch(this, function(){
|
||
|
if(childWidget.domNode){
|
||
|
dojo.window.scrollIntoView(childWidget.domNode);
|
||
|
}
|
||
|
delete this._scrollingTimeout;
|
||
|
return;
|
||
|
}), 1);
|
||
|
},
|
||
|
|
||
|
resize: function(args){
|
||
|
dijit.layout._LayoutWidget.prototype.resize.call(this, args);
|
||
|
},
|
||
|
|
||
|
layout: function(){
|
||
|
var children = this.getChildren();
|
||
|
if(this._contentBox){
|
||
|
var bn = this.buttonsNode;
|
||
|
var height = this._contentBox.h - dojo.marginBox(bn).h -
|
||
|
dojox.html.metrics.getScrollbar().h;
|
||
|
dojo.forEach(children, function(c){
|
||
|
dojo.marginBox(c.domNode, {h: height});
|
||
|
});
|
||
|
}
|
||
|
if(this._focusedPane){
|
||
|
var foc = this._focusedPane;
|
||
|
delete this._focusedPane;
|
||
|
if(!this._savedFocus){
|
||
|
foc.focus();
|
||
|
}
|
||
|
}else if(children && children.length){
|
||
|
if(!this._savedFocus){
|
||
|
children[0].focus();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onChange: function(/*item*/ value){
|
||
|
this.onChange(value);
|
||
|
},
|
||
|
|
||
|
_setValue: function(/* item */ value){
|
||
|
// summary: internally sets the value and fires onchange
|
||
|
delete this._setInProgress;
|
||
|
if(!this._itemsMatch(this.value, value)){
|
||
|
this.value = value;
|
||
|
this._onChange(value);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_setValueAttr: function(/* item */ value){
|
||
|
// summary: sets the value of this widget to the given store item
|
||
|
if(this._itemsMatch(this.value, value) && !value){ return; }
|
||
|
if(this._setInProgress && this._setInProgress === value){ return; }
|
||
|
this._setInProgress = value;
|
||
|
if(!value || !this.store.isItem(value)){
|
||
|
var pane = this.getChildren()[0];
|
||
|
pane._setSelected(null);
|
||
|
this._onItemClick(null, pane, null, null);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var fetchParentItems = dojo.hitch(this, function(/*item*/ item, /*function*/callback){
|
||
|
// Summary: Fetchs the parent items for the given item
|
||
|
var store = this.store, id;
|
||
|
if(this.parentAttr && store.getFeatures()["dojo.data.api.Identity"] &&
|
||
|
((id = this.store.getValue(item, this.parentAttr)) || id === "")){
|
||
|
// Fetch by parent attribute
|
||
|
var cb = function(i){
|
||
|
if(store.getIdentity(i) == store.getIdentity(item)){
|
||
|
callback(null);
|
||
|
}else{
|
||
|
callback([i]);
|
||
|
}
|
||
|
};
|
||
|
if(id === ""){
|
||
|
callback(null);
|
||
|
}else if(typeof id == "string"){
|
||
|
store.fetchItemByIdentity({identity: id, onItem: cb});
|
||
|
}else if(store.isItem(id)){
|
||
|
cb(id);
|
||
|
}
|
||
|
}else{
|
||
|
// Fetch by finding children
|
||
|
var numCheck = this.childrenAttrs.length;
|
||
|
var parents = [];
|
||
|
dojo.forEach(this.childrenAttrs, function(attr){
|
||
|
var q = {};
|
||
|
q[attr] = item;
|
||
|
store.fetch({query: q, scope: this,
|
||
|
onComplete: function(items){
|
||
|
if(this._setInProgress !== value){
|
||
|
return;
|
||
|
}
|
||
|
parents = parents.concat(items);
|
||
|
numCheck--;
|
||
|
if(numCheck === 0){
|
||
|
callback(parents);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}, this);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var setFromChain = dojo.hitch(this, function(/*item[]*/itemChain, /*integer*/idx){
|
||
|
// Summary: Sets the value of the widget at the given index in the chain - onchanges are not
|
||
|
// fired here
|
||
|
var set = itemChain[idx];
|
||
|
var child = this.getChildren()[idx];
|
||
|
var conn;
|
||
|
if(set && child){
|
||
|
var fx = dojo.hitch(this, function(){
|
||
|
if(conn){
|
||
|
this.disconnect(conn);
|
||
|
}
|
||
|
delete conn;
|
||
|
if(this._setInProgress !== value){
|
||
|
return;
|
||
|
}
|
||
|
var selOpt = dojo.filter(child._menu.getChildren(), function(i){
|
||
|
return this._itemsMatch(i.item, set);
|
||
|
}, this)[0];
|
||
|
if(selOpt){
|
||
|
idx++;
|
||
|
child._menu.onItemClick(selOpt, {type: "internal",
|
||
|
stopPropagation: function(){},
|
||
|
preventDefault: function(){}});
|
||
|
if(itemChain[idx]){
|
||
|
setFromChain(itemChain, idx);
|
||
|
}else{
|
||
|
this._setValue(set);
|
||
|
this.onItemClick(set, child, this.getChildItems(set));
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
if(!child.isLoaded){
|
||
|
conn = this.connect(child, "onLoad", fx);
|
||
|
}else{
|
||
|
fx();
|
||
|
}
|
||
|
}else if(idx === 0){
|
||
|
this.set("value", null);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var parentChain = [];
|
||
|
var onParents = dojo.hitch(this, function(/*item[]*/ parents){
|
||
|
// Summary: recursively grabs the parents - only the first one is followed
|
||
|
if(parents && parents.length){
|
||
|
parentChain.push(parents[0]);
|
||
|
fetchParentItems(parents[0], onParents);
|
||
|
}else{
|
||
|
if(!parents){
|
||
|
parentChain.pop();
|
||
|
}
|
||
|
parentChain.reverse();
|
||
|
setFromChain(parentChain, 0);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Only set the value in display if we are shown - if we are in a dropdown,
|
||
|
// and are hidden, don't actually do the scrolling in the display (it can
|
||
|
// mess up layouts)
|
||
|
var ns = this.domNode.style;
|
||
|
if(ns.display == "none" || ns.visibility == "hidden"){
|
||
|
this._setValue(value);
|
||
|
}else if(!this._itemsMatch(value, this._visibleItem)){
|
||
|
onParents([value]);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onItemClick: function(/* Event */ evt, /* dijit._Contained */ pane, /* item */ item, /* item[]? */ children){
|
||
|
// summary: internally called when a widget should pop up its child
|
||
|
|
||
|
if(evt){
|
||
|
var itemPane = this._getPaneForItem(item, pane, children);
|
||
|
var alreadySelected = (evt.type == "click" && evt.alreadySelected);
|
||
|
|
||
|
if(alreadySelected && itemPane){
|
||
|
this._removeAfter(pane.getIndexInParent() + 1);
|
||
|
var next = pane.getNextSibling();
|
||
|
if(next && next._setSelected){
|
||
|
next._setSelected(null);
|
||
|
}
|
||
|
this.scrollIntoView(next);
|
||
|
}else if(itemPane){
|
||
|
this.addChild(itemPane, pane.getIndexInParent() + 1);
|
||
|
if(this._savedFocus){
|
||
|
itemPane.focus(true);
|
||
|
}
|
||
|
}else{
|
||
|
this._removeAfter(pane);
|
||
|
this.scrollIntoView(pane);
|
||
|
}
|
||
|
}else if(pane){
|
||
|
this._removeAfter(pane);
|
||
|
this.scrollIntoView(pane);
|
||
|
}
|
||
|
if(!evt || evt.type != "internal"){
|
||
|
this._setValue(item);
|
||
|
this.onItemClick(item, pane, children);
|
||
|
}
|
||
|
this._visibleItem = item;
|
||
|
},
|
||
|
|
||
|
_getPaneForItem: function(/* item? */ item, /* dijit._Contained? */ parentPane, /* item[]? */ children){ // summary: gets the pane for the given item, and mixes in our needed parts
|
||
|
// Returns the pane for the given item (null if the root pane) - after mixing in
|
||
|
// its stuff.
|
||
|
var ret = this.getPaneForItem(item, parentPane, children);
|
||
|
ret.store = this.store;
|
||
|
ret.parentWidget = this;
|
||
|
ret.parentPane = parentPane||null;
|
||
|
if(!item){
|
||
|
ret.query = this.query;
|
||
|
ret.queryOptions = this.queryOptions;
|
||
|
}else if(children){
|
||
|
ret.items = children;
|
||
|
}else{
|
||
|
ret.items = [item];
|
||
|
}
|
||
|
return ret;
|
||
|
},
|
||
|
|
||
|
_getMenuItemForItem: function(/*item*/ item, /* dijit._Contained */ parentPane){
|
||
|
// summary: returns a widget for the given store item. The returned
|
||
|
// item will be added to this widget's container widget. null will
|
||
|
// be passed in for an "empty" item.
|
||
|
var store = this.store;
|
||
|
if(!item || !store || !store.isItem(item)){
|
||
|
var i = new dijit.MenuItem({
|
||
|
label: "---",
|
||
|
disabled: true,
|
||
|
iconClass: "dojoxEmpty",
|
||
|
focus: function(){
|
||
|
// Do nothing on focus of this guy...
|
||
|
}
|
||
|
});
|
||
|
this._updateClass(i.domNode, "Item");
|
||
|
return i;
|
||
|
}else{
|
||
|
var itemLoaded = store.isItemLoaded(item);
|
||
|
var childItems = itemLoaded ? this.getChildItems(item) : undefined;
|
||
|
var widgetItem;
|
||
|
if(childItems){
|
||
|
widgetItem = this.getMenuItemForItem(item, parentPane, childItems);
|
||
|
widgetItem.children = childItems;
|
||
|
this._updateClass(widgetItem.domNode, "Item", {"Expanding": true});
|
||
|
if(!widgetItem._started){
|
||
|
var c = widgetItem.connect(widgetItem, "startup", function(){
|
||
|
this.disconnect(c);
|
||
|
dojo.style(this.arrowWrapper, "display", "");
|
||
|
});
|
||
|
}else{
|
||
|
dojo.style(widgetItem.arrowWrapper, "display", "");
|
||
|
}
|
||
|
}else{
|
||
|
widgetItem = this.getMenuItemForItem(item, parentPane, null);
|
||
|
if(itemLoaded){
|
||
|
this._updateClass(widgetItem.domNode, "Item", {"Single": true});
|
||
|
}else{
|
||
|
this._updateClass(widgetItem.domNode, "Item", {"Unloaded": true});
|
||
|
widgetItem.attr("disabled", true);
|
||
|
}
|
||
|
}
|
||
|
widgetItem.store = this.store;
|
||
|
widgetItem.item = item;
|
||
|
if(!widgetItem.label){
|
||
|
widgetItem.attr("label", this.store.getLabel(item).replace(/</,"<"));
|
||
|
}
|
||
|
if(widgetItem.focusNode){
|
||
|
var self = this;
|
||
|
widgetItem.focus = function(){
|
||
|
// Don't set our class
|
||
|
if(!this.disabled){try{this.focusNode.focus();}catch(e){}}
|
||
|
};
|
||
|
widgetItem.connect(widgetItem.focusNode, "onmouseenter", function(){
|
||
|
if(!this.disabled){
|
||
|
self._updateClass(this.domNode, "Item", {"Hover": true});
|
||
|
}
|
||
|
});
|
||
|
widgetItem.connect(widgetItem.focusNode, "onmouseleave", function(){
|
||
|
if(!this.disabled){
|
||
|
self._updateClass(this.domNode, "Item", {"Hover": false});
|
||
|
}
|
||
|
});
|
||
|
widgetItem.connect(widgetItem.focusNode, "blur", function(){
|
||
|
self._updateClass(this.domNode, "Item", {"Focus": false, "Hover": false});
|
||
|
});
|
||
|
widgetItem.connect(widgetItem.focusNode, "focus", function(){
|
||
|
self._updateClass(this.domNode, "Item", {"Focus": true});
|
||
|
self._focusedPane = parentPane;
|
||
|
});
|
||
|
if(this.executeOnDblClick){
|
||
|
widgetItem.connect(widgetItem.focusNode, "ondblclick", function(){
|
||
|
self._onExecute();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
return widgetItem;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_setStore: function(/* dojo.data.api.Read */ store){
|
||
|
// summary: sets the store for this widget */
|
||
|
if(store === this.store && this._started){ return; }
|
||
|
this.store = store;
|
||
|
this._isIdentity = store.getFeatures()["dojo.data.api.Identity"];
|
||
|
var rootPane = this._getPaneForItem();
|
||
|
this.addChild(rootPane, 0);
|
||
|
},
|
||
|
|
||
|
_onKey: function(/*Event*/ e){
|
||
|
// summary: called when a keypress event happens on this widget
|
||
|
if(e.charOrCode == dojo.keys.BACKSPACE){
|
||
|
dojo.stopEvent(e);
|
||
|
return;
|
||
|
}else if(e.charOrCode == dojo.keys.ESCAPE && this._savedFocus){
|
||
|
try{dijit.focus(this._savedFocus);}catch(e){}
|
||
|
dojo.stopEvent(e);
|
||
|
return;
|
||
|
}else if(e.charOrCode == dojo.keys.LEFT_ARROW ||
|
||
|
e.charOrCode == dojo.keys.RIGHT_ARROW){
|
||
|
dojo.stopEvent(e);
|
||
|
return;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_resetValue: function(){
|
||
|
// Summary: function called when the value is reset.
|
||
|
this.set("value", this._lastExecutedValue);
|
||
|
},
|
||
|
|
||
|
_onCancel: function(){
|
||
|
// Summary: function called when the cancel button is clicked. It
|
||
|
// resets its value to whatever was last executed and then cancels
|
||
|
this._resetValue();
|
||
|
this.onCancel();
|
||
|
},
|
||
|
|
||
|
_onExecute: function(){
|
||
|
// Summary: function called when the OK button is clicked or when an
|
||
|
// item is selected (double-clicked or "enter" pressed on it)
|
||
|
this._lastExecutedValue = this.get("value");
|
||
|
this.onExecute();
|
||
|
},
|
||
|
|
||
|
focus: function(){
|
||
|
// summary: sets the focus state of this widget
|
||
|
var wasSaved = this._savedFocus;
|
||
|
this._savedFocus = dijit.getFocus(this);
|
||
|
if(!this._savedFocus.node){
|
||
|
delete this._savedFocus;
|
||
|
}
|
||
|
if(!this._focusedPane){
|
||
|
var child = this.getChildren()[0];
|
||
|
if(child && !wasSaved){
|
||
|
child.focus(true);
|
||
|
}
|
||
|
}else{
|
||
|
this._savedFocus = dijit.getFocus(this);
|
||
|
var foc = this._focusedPane;
|
||
|
delete this._focusedPane;
|
||
|
if(!wasSaved){
|
||
|
foc.focus(true);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
handleKey:function(/*Event*/e){
|
||
|
// summary: handle the key for the given event - called by dropdown
|
||
|
// widgets
|
||
|
if(e.charOrCode == dojo.keys.DOWN_ARROW){
|
||
|
delete this._savedFocus;
|
||
|
this.focus();
|
||
|
return false;
|
||
|
}else if(e.charOrCode == dojo.keys.ESCAPE){
|
||
|
this._onCancel();
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
_updateChildClasses: function(){
|
||
|
// summary: Called when a child is added or removed - so that we can
|
||
|
// update the classes for styling the "current" one differently than
|
||
|
// the others
|
||
|
var children = this.getChildren();
|
||
|
var length = children.length;
|
||
|
dojo.forEach(children, function(c, idx){
|
||
|
dojo.toggleClass(c.domNode, "dojoxRollingListPaneCurrentChild", (idx == (length - 1)));
|
||
|
dojo.toggleClass(c.domNode, "dojoxRollingListPaneCurrentSelected", (idx == (length - 2)));
|
||
|
});
|
||
|
},
|
||
|
|
||
|
startup: function(){
|
||
|
if(this._started){ return; }
|
||
|
if(!this.getParent || !this.getParent()){
|
||
|
this.resize();
|
||
|
this.connect(dojo.global, "onresize", "resize");
|
||
|
}
|
||
|
this.connect(this, "addChild", "_updateChildClasses");
|
||
|
this.connect(this, "removeChild", "_updateChildClasses");
|
||
|
this._setStore(this.store);
|
||
|
this.set("showButtons", this.showButtons);
|
||
|
this.inherited(arguments);
|
||
|
this._lastExecutedValue = this.get("value");
|
||
|
},
|
||
|
|
||
|
getChildItems: function(/*item*/ item){
|
||
|
// summary: Returns the child items for the given store item
|
||
|
var childItems, store = this.store;
|
||
|
dojo.forEach(this.childrenAttrs, function(attr){
|
||
|
var vals = store.getValues(item, attr);
|
||
|
if(vals && vals.length){
|
||
|
childItems = (childItems||[]).concat(vals);
|
||
|
}
|
||
|
});
|
||
|
return childItems;
|
||
|
},
|
||
|
|
||
|
getMenuItemForItem: function(/*item*/ item, /* dijit._Contained */ parentPane, /* item[]? */ children){
|
||
|
// summary: user overridable function to return a widget for the given item
|
||
|
// and its children.
|
||
|
return new dijit.MenuItem({});
|
||
|
},
|
||
|
|
||
|
getPaneForItem: function(/* item? */ item, /* dijit._Contained? */ parentPane, /* item[]? */ children){
|
||
|
// summary: user-overridable function to return a pane that corresponds
|
||
|
// to the given item in the store. It can return null to not add a new pane
|
||
|
// (ie, you are planning on doing something else with it in onItemClick)
|
||
|
//
|
||
|
// Item is undefined for the root pane, children is undefined for non-group panes
|
||
|
if(!item || children){
|
||
|
return new dojox.widget._RollingListGroupPane({});
|
||
|
}else{
|
||
|
return null;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
onItemClick: function(/* item */ item, /* dijit._Contained */ pane, /* item[]? */ children){
|
||
|
// summary: called when an item is clicked - it receives the store item
|
||
|
},
|
||
|
|
||
|
onExecute: function(){
|
||
|
// summary: exists so that popups don't disappear too soon
|
||
|
},
|
||
|
|
||
|
onCancel: function(){
|
||
|
// summary: exists so that we can close ourselves if we wish
|
||
|
},
|
||
|
|
||
|
onChange: function(/* item */ value){
|
||
|
// summary: called when the value of this widget has changed
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
});
|