12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214 |
- /*
- Copyright (c) 2004-2012, 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
- */
- 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.requireLocalization("dijit", "common", null, "ROOT,ar,az,bg,ca,cs,da,de,el,es,fi,fr,he,hr,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
- 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
- }
-
- });
- }
|