diff options
Diffstat (limited to 'js/dojo-1.6/dojox/widget/RollingList.xd.js')
| -rw-r--r-- | js/dojo-1.6/dojox/widget/RollingList.xd.js | 1228 |
1 files changed, 1228 insertions, 0 deletions
diff --git a/js/dojo-1.6/dojox/widget/RollingList.xd.js b/js/dojo-1.6/dojox/widget/RollingList.xd.js new file mode 100644 index 0000000..c6f6bdd --- /dev/null +++ b/js/dojo-1.6/dojox/widget/RollingList.xd.js @@ -0,0 +1,1228 @@ +/*
+ Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
+ Available via Academic Free License >= 2.1 OR the modified BSD license.
+ see: http://dojotoolkit.org/license for details
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.widget.RollingList"],
+["require", "dojo.window"],
+["require", "dijit.layout.ContentPane"],
+["require", "dijit._Templated"],
+["require", "dijit._Contained"],
+["require", "dijit.layout._LayoutWidget"],
+["require", "dijit.Menu"],
+["require", "dijit.form.Button"],
+["require", "dojox.html.metrics"],
+["require", "dojo.i18n"],
+["requireLocalization", "dijit", "common", null, "ROOT,ar,ca,cs,da,de,el,es,fi,fr,he,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw", "ROOT,ar,ca,cs,da,de,el,es,fi,fr,he,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.widget.RollingList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.widget.RollingList"] = true;
+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("dojox.html.metrics");
+
+dojo.require("dojo.i18n");
+;
+
+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}\"\r\n\t><div class=\"dojoxRollingListContainer\" dojoAttachPoint=\"containerNode\" dojoAttachEvent=\"onkeypress:_onKey\"\r\n\t></div\r\n\t><div class=\"dojoxRollingListButtons\" dojoAttachPoint=\"buttonsNode\"\r\n ><button dojoType=\"dijit.form.Button\" dojoAttachPoint=\"okButton\"\r\n\t\t\t\tdojoAttachEvent=\"onClick:_onExecute\">${okButtonLabel}</button\r\n ><button dojoType=\"dijit.form.Button\" dojoAttachPoint=\"cancelButton\"\r\n\t\t\t\tdojoAttachEvent=\"onClick:_onCancel\">${cancelButtonLabel}</button\r\n\t></div\r\n></div>\r\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
+ }
+
+});
+
+}
+
+}};});
|
