summaryrefslogtreecommitdiff
path: root/lib/dijit/form/ComboBox.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dijit/form/ComboBox.js')
-rw-r--r--lib/dijit/form/ComboBox.js1812
1 files changed, 1214 insertions, 598 deletions
diff --git a/lib/dijit/form/ComboBox.js b/lib/dijit/form/ComboBox.js
index 694c43ed1..aecc5c4fc 100644
--- a/lib/dijit/form/ComboBox.js
+++ b/lib/dijit/form/ComboBox.js
@@ -1,12 +1,12 @@
/*
- Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
+ 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
*/
-if(!dojo._hasResource["dijit.form.ComboBox"]){
-dojo._hasResource["dijit.form.ComboBox"]=true;
+if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dijit.form.ComboBox"] = true;
dojo.provide("dijit.form.ComboBox");
dojo.require("dojo.window");
dojo.require("dojo.regexp");
@@ -15,601 +15,1217 @@ dojo.require("dojo.data.util.filter");
dojo.require("dijit._CssStateMixin");
dojo.require("dijit.form._FormWidget");
dojo.require("dijit.form.ValidationTextBox");
-dojo.requireLocalization("dijit.form","ComboBox",null,"ROOT,ar,ca,cs,da,de,el,es,fi,fr,he,hu,it,ja,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
-dojo.declare("dijit.form.ComboBoxMixin",null,{item:null,pageSize:Infinity,store:null,fetchProperties:{},query:{},autoComplete:true,highlightMatch:"first",searchDelay:100,searchAttr:"name",labelAttr:"",labelType:"text",queryExpr:"${0}*",ignoreCase:true,hasDownArrow:true,templateString:dojo.cache("dijit.form","templates/ComboBox.html","<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\tdojoAttachPoint=\"comboNode\" waiRole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"downArrowNode\" waiRole=\"presentation\"\n\t\tdojoAttachEvent=\"onmousedown:_onArrowMouseDown\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&Chi; \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdojoAttachEvent=\"onkeypress:_onKeyPress,compositionend\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" waiRole=\"textbox\" waiState=\"haspopup-true,autocomplete-list\"\n\t/></div\n></div>\n"),baseClass:"dijitTextBox dijitComboBox",cssStateNodes:{"downArrowNode":"dijitDownArrowButton"},_getCaretPos:function(_1){
-var _2=0;
-if(typeof (_1.selectionStart)=="number"){
-_2=_1.selectionStart;
-}else{
-if(dojo.isIE){
-var tr=dojo.doc.selection.createRange().duplicate();
-var _3=_1.createTextRange();
-tr.move("character",0);
-_3.move("character",0);
-try{
-_3.setEndPoint("EndToEnd",tr);
-_2=String(_3.text).replace(/\r/g,"").length;
-}
-catch(e){
-}
-}
-}
-return _2;
-},_setCaretPos:function(_4,_5){
-_5=parseInt(_5);
-dijit.selectInputText(_4,_5,_5);
-},_setDisabledAttr:function(_6){
-this.inherited(arguments);
-dijit.setWaiState(this.comboNode,"disabled",_6);
-},_abortQuery:function(){
-if(this.searchTimer){
-clearTimeout(this.searchTimer);
-this.searchTimer=null;
-}
-if(this._fetchHandle){
-if(this._fetchHandle.abort){
-this._fetchHandle.abort();
-}
-this._fetchHandle=null;
-}
-},_onInput:function(_7){
-if(!this.searchTimer&&(_7.type=="paste"||_7.type=="input")&&this._lastInput!=this.textbox.value){
-this.searchTimer=setTimeout(dojo.hitch(this,function(){
-this._onKeyPress({charOrCode:229});
-}),100);
-}
-this.inherited(arguments);
-},_onKeyPress:function(_8){
-var _9=_8.charOrCode;
-if(_8.altKey||((_8.ctrlKey||_8.metaKey)&&(_9!="x"&&_9!="v"))||_9==dojo.keys.SHIFT){
-return;
-}
-var _a=false;
-var _b="_startSearchFromInput";
-var pw=this._popupWidget;
-var dk=dojo.keys;
-var _c=null;
-this._prev_key_backspace=false;
-this._abortQuery();
-if(this._isShowingNow){
-pw.handleKey(_9);
-_c=pw.getHighlightedOption();
-}
-switch(_9){
-case dk.PAGE_DOWN:
-case dk.DOWN_ARROW:
-case dk.PAGE_UP:
-case dk.UP_ARROW:
-if(!this._isShowingNow){
-_a=true;
-_b="_startSearchAll";
-}else{
-this._announceOption(_c);
-}
-dojo.stopEvent(_8);
-break;
-case dk.ENTER:
-if(_c){
-if(_c==pw.nextButton){
-this._nextSearch(1);
-dojo.stopEvent(_8);
-break;
-}else{
-if(_c==pw.previousButton){
-this._nextSearch(-1);
-dojo.stopEvent(_8);
-break;
-}
-}
-}else{
-this._setBlurValue();
-this._setCaretPos(this.focusNode,this.focusNode.value.length);
-}
-_8.preventDefault();
-case dk.TAB:
-var _d=this.get("displayedValue");
-if(pw&&(_d==pw._messages["previousMessage"]||_d==pw._messages["nextMessage"])){
-break;
-}
-if(_c){
-this._selectOption();
-}
-if(this._isShowingNow){
-this._lastQuery=null;
-this._hideResultList();
-}
-break;
-case " ":
-if(_c){
-dojo.stopEvent(_8);
-this._selectOption();
-this._hideResultList();
-}else{
-_a=true;
-}
-break;
-case dk.ESCAPE:
-if(this._isShowingNow){
-dojo.stopEvent(_8);
-this._hideResultList();
-}
-break;
-case dk.DELETE:
-case dk.BACKSPACE:
-this._prev_key_backspace=true;
-_a=true;
-break;
-default:
-_a=typeof _9=="string"||_9==229;
-}
-if(_a){
-this.item=undefined;
-this.searchTimer=setTimeout(dojo.hitch(this,_b),1);
-}
-},_autoCompleteText:function(_e){
-var fn=this.focusNode;
-dijit.selectInputText(fn,fn.value.length);
-var _f=this.ignoreCase?"toLowerCase":"substr";
-if(_e[_f](0).indexOf(this.focusNode.value[_f](0))==0){
-var _10=this._getCaretPos(fn);
-if((_10+1)>fn.value.length){
-fn.value=_e;
-dijit.selectInputText(fn,_10);
-}
-}else{
-fn.value=_e;
-dijit.selectInputText(fn);
-}
-},_openResultList:function(_11,_12){
-this._fetchHandle=null;
-if(this.disabled||this.readOnly||(_12.query[this.searchAttr]!=this._lastQuery)){
-return;
-}
-this._popupWidget.clearResultList();
-if(!_11.length&&!this._maxOptions){
-this._hideResultList();
-return;
-}
-_12._maxOptions=this._maxOptions;
-var _13=this._popupWidget.createOptions(_11,_12,dojo.hitch(this,"_getMenuLabelFromItem"));
-this._showResultList();
-if(_12.direction){
-if(1==_12.direction){
-this._popupWidget.highlightFirstOption();
-}else{
-if(-1==_12.direction){
-this._popupWidget.highlightLastOption();
-}
-}
-this._announceOption(this._popupWidget.getHighlightedOption());
-}else{
-if(this.autoComplete&&!this._prev_key_backspace&&!/^[*]+$/.test(_12.query[this.searchAttr])){
-this._announceOption(_13[1]);
-}
-}
-},_showResultList:function(){
-this._hideResultList();
-this.displayMessage("");
-dojo.style(this._popupWidget.domNode,{width:"",height:""});
-var _14=this.open();
-var _15=dojo.marginBox(this._popupWidget.domNode);
-this._popupWidget.domNode.style.overflow=((_14.h==_15.h)&&(_14.w==_15.w))?"hidden":"auto";
-var _16=_14.w;
-if(_14.h<this._popupWidget.domNode.scrollHeight){
-_16+=16;
-}
-dojo.marginBox(this._popupWidget.domNode,{h:_14.h,w:Math.max(_16,this.domNode.offsetWidth)});
-if(_16<this.domNode.offsetWidth){
-this._popupWidget.domNode.parentNode.style.left=dojo.position(this.domNode,true).x+"px";
-}
-dijit.setWaiState(this.comboNode,"expanded","true");
-},_hideResultList:function(){
-this._abortQuery();
-if(this._isShowingNow){
-dijit.popup.close(this._popupWidget);
-this._isShowingNow=false;
-dijit.setWaiState(this.comboNode,"expanded","false");
-dijit.removeWaiState(this.focusNode,"activedescendant");
-}
-},_setBlurValue:function(){
-var _17=this.get("displayedValue");
-var pw=this._popupWidget;
-if(pw&&(_17==pw._messages["previousMessage"]||_17==pw._messages["nextMessage"])){
-this._setValueAttr(this._lastValueReported,true);
-}else{
-if(typeof this.item=="undefined"){
-this.item=null;
-this.set("displayedValue",_17);
-}else{
-if(this.value!=this._lastValueReported){
-dijit.form._FormValueWidget.prototype._setValueAttr.call(this,this.value,true);
-}
-this._refreshState();
-}
-}
-},_onBlur:function(){
-this._hideResultList();
-this.inherited(arguments);
-},_setItemAttr:function(_18,_19,_1a){
-if(!_1a){
-_1a=this.labelFunc(_18,this.store);
-}
-this.value=this._getValueField()!=this.searchAttr?this.store.getIdentity(_18):_1a;
-this.item=_18;
-dijit.form.ComboBox.superclass._setValueAttr.call(this,this.value,_19,_1a);
-},_announceOption:function(_1b){
-if(!_1b){
-return;
-}
-var _1c;
-if(_1b==this._popupWidget.nextButton||_1b==this._popupWidget.previousButton){
-_1c=_1b.innerHTML;
-this.item=undefined;
-this.value="";
-}else{
-_1c=this.labelFunc(_1b.item,this.store);
-this.set("item",_1b.item,false,_1c);
-}
-this.focusNode.value=this.focusNode.value.substring(0,this._lastInput.length);
-dijit.setWaiState(this.focusNode,"activedescendant",dojo.attr(_1b,"id"));
-this._autoCompleteText(_1c);
-},_selectOption:function(evt){
-if(evt){
-this._announceOption(evt.target);
-}
-this._hideResultList();
-this._setCaretPos(this.focusNode,this.focusNode.value.length);
-dijit.form._FormValueWidget.prototype._setValueAttr.call(this,this.value,true);
-},_onArrowMouseDown:function(evt){
-if(this.disabled||this.readOnly){
-return;
-}
-dojo.stopEvent(evt);
-this.focus();
-if(this._isShowingNow){
-this._hideResultList();
-}else{
-this._startSearchAll();
-}
-},_startSearchAll:function(){
-this._startSearch("");
-},_startSearchFromInput:function(){
-this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g,"\\$1"));
-},_getQueryString:function(_1d){
-return dojo.string.substitute(this.queryExpr,[_1d]);
-},_startSearch:function(key){
-if(!this._popupWidget){
-var _1e=this.id+"_popup";
-this._popupWidget=new dijit.form._ComboBoxMenu({onChange:dojo.hitch(this,this._selectOption),id:_1e,dir:this.dir});
-dijit.removeWaiState(this.focusNode,"activedescendant");
-dijit.setWaiState(this.textbox,"owns",_1e);
-}
-var _1f=dojo.clone(this.query);
-this._lastInput=key;
-this._lastQuery=_1f[this.searchAttr]=this._getQueryString(key);
-this.searchTimer=setTimeout(dojo.hitch(this,function(_20,_21){
-this.searchTimer=null;
-var _22={queryOptions:{ignoreCase:this.ignoreCase,deep:true},query:_20,onBegin:dojo.hitch(this,"_setMaxOptions"),onComplete:dojo.hitch(this,"_openResultList"),onError:function(_23){
-_21._fetchHandle=null;
-console.error("dijit.form.ComboBox: "+_23);
-dojo.hitch(_21,"_hideResultList")();
-},start:0,count:this.pageSize};
-dojo.mixin(_22,_21.fetchProperties);
-this._fetchHandle=_21.store.fetch(_22);
-var _24=function(_25,_26){
-_25.start+=_25.count*_26;
-_25.direction=_26;
-this._fetchHandle=this.store.fetch(_25);
-};
-this._nextSearch=this._popupWidget.onPage=dojo.hitch(this,_24,this._fetchHandle);
-},_1f,this),this.searchDelay);
-},_setMaxOptions:function(_27,_28){
-this._maxOptions=_27;
-},_getValueField:function(){
-return this.searchAttr;
-},compositionend:function(evt){
-this._onKeyPress({charOrCode:229});
-},constructor:function(){
-this.query={};
-this.fetchProperties={};
-},postMixInProperties:function(){
-if(!this.store){
-var _29=this.srcNodeRef;
-this.store=new dijit.form._ComboBoxDataStore(_29);
-if(!("value" in this.params)){
-var _2a=this.store.fetchSelectedItem();
-if(_2a){
-var _2b=this._getValueField();
-this.value=_2b!=this.searchAttr?this.store.getValue(_2a,_2b):this.labelFunc(_2a,this.store);
-}
-}
-}
-this.inherited(arguments);
-},postCreate:function(){
-if(!this.hasDownArrow){
-this.downArrowNode.style.display="none";
-}
-var _2c=dojo.query("label[for=\""+this.id+"\"]");
-if(_2c.length){
-_2c[0].id=(this.id+"_label");
-var cn=this.comboNode;
-dijit.setWaiState(cn,"labelledby",_2c[0].id);
-}
-this.inherited(arguments);
-},uninitialize:function(){
-if(this._popupWidget&&!this._popupWidget._destroyed){
-this._hideResultList();
-this._popupWidget.destroy();
-}
-this.inherited(arguments);
-},_getMenuLabelFromItem:function(_2d){
-var _2e=this.labelAttr?this.store.getValue(_2d,this.labelAttr):this.labelFunc(_2d,this.store);
-var _2f=this.labelType;
-if(this.highlightMatch!="none"&&this.labelType=="text"&&this._lastInput){
-_2e=this.doHighlight(_2e,this._escapeHtml(this._lastInput));
-_2f="html";
-}
-return {html:_2f=="html",label:_2e};
-},doHighlight:function(_30,_31){
-var _32="i"+(this.highlightMatch=="all"?"g":"");
-var _33=this._escapeHtml(_30);
-_31=dojo.regexp.escapeString(_31);
-var ret=_33.replace(new RegExp("(^|\\s)("+_31+")",_32),"$1<span class=\"dijitComboBoxHighlightMatch\">$2</span>");
-return ret;
-},_escapeHtml:function(str){
-str=String(str).replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;").replace(/"/gm,"&quot;");
-return str;
-},open:function(){
-this._isShowingNow=true;
-return dijit.popup.open({popup:this._popupWidget,around:this.domNode,parent:this});
-},reset:function(){
-this.item=null;
-this.inherited(arguments);
-},labelFunc:function(_34,_35){
-return _35.getValue(_34,this.searchAttr).toString();
-}});
-dojo.declare("dijit.form._ComboBoxMenu",[dijit._Widget,dijit._Templated,dijit._CssStateMixin],{templateString:"<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' tabIndex='-1' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"+"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' waiRole='option'></li>"+"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' waiRole='option'></li>"+"</ul>",_messages:null,baseClass:"dijitComboBoxMenu",postMixInProperties:function(){
-this._messages=dojo.i18n.getLocalization("dijit.form","ComboBox",this.lang);
-this.inherited(arguments);
-},_setValueAttr:function(_36){
-this.value=_36;
-this.onChange(_36);
-},onChange:function(_37){
-},onPage:function(_38){
-},postCreate:function(){
-this.previousButton.innerHTML=this._messages["previousMessage"];
-this.nextButton.innerHTML=this._messages["nextMessage"];
-this.inherited(arguments);
-},onClose:function(){
-this._blurOptionNode();
-},_createOption:function(_39,_3a){
-var _3b=_3a(_39);
-var _3c=dojo.doc.createElement("li");
-dijit.setWaiRole(_3c,"option");
-if(_3b.html){
-_3c.innerHTML=_3b.label;
-}else{
-_3c.appendChild(dojo.doc.createTextNode(_3b.label));
-}
-if(_3c.innerHTML==""){
-_3c.innerHTML="&nbsp;";
-}
-_3c.item=_39;
-return _3c;
-},createOptions:function(_3d,_3e,_3f){
-this.previousButton.style.display=(_3e.start==0)?"none":"";
-dojo.attr(this.previousButton,"id",this.id+"_prev");
-dojo.forEach(_3d,function(_40,i){
-var _41=this._createOption(_40,_3f);
-_41.className="dijitReset dijitMenuItem"+(this.isLeftToRight()?"":" dijitMenuItemRtl");
-dojo.attr(_41,"id",this.id+i);
-this.domNode.insertBefore(_41,this.nextButton);
-},this);
-var _42=false;
-if(_3e._maxOptions&&_3e._maxOptions!=-1){
-if((_3e.start+_3e.count)<_3e._maxOptions){
-_42=true;
-}else{
-if((_3e.start+_3e.count)>_3e._maxOptions&&_3e.count==_3d.length){
-_42=true;
-}
-}
-}else{
-if(_3e.count==_3d.length){
-_42=true;
-}
-}
-this.nextButton.style.display=_42?"":"none";
-dojo.attr(this.nextButton,"id",this.id+"_next");
-return this.domNode.childNodes;
-},clearResultList:function(){
-while(this.domNode.childNodes.length>2){
-this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
-}
-},_onMouseDown:function(evt){
-dojo.stopEvent(evt);
-},_onMouseUp:function(evt){
-if(evt.target===this.domNode||!this._highlighted_option){
-return;
-}else{
-if(evt.target==this.previousButton){
-this.onPage(-1);
-}else{
-if(evt.target==this.nextButton){
-this.onPage(1);
-}else{
-var tgt=evt.target;
-while(!tgt.item){
-tgt=tgt.parentNode;
-}
-this._setValueAttr({target:tgt},true);
-}
-}
-}
-},_onMouseOver:function(evt){
-if(evt.target===this.domNode){
-return;
-}
-var tgt=evt.target;
-if(!(tgt==this.previousButton||tgt==this.nextButton)){
-while(!tgt.item){
-tgt=tgt.parentNode;
-}
-}
-this._focusOptionNode(tgt);
-},_onMouseOut:function(evt){
-if(evt.target===this.domNode){
-return;
-}
-this._blurOptionNode();
-},_focusOptionNode:function(_43){
-if(this._highlighted_option!=_43){
-this._blurOptionNode();
-this._highlighted_option=_43;
-dojo.addClass(this._highlighted_option,"dijitMenuItemSelected");
-}
-},_blurOptionNode:function(){
-if(this._highlighted_option){
-dojo.removeClass(this._highlighted_option,"dijitMenuItemSelected");
-this._highlighted_option=null;
-}
-},_highlightNextOption:function(){
-if(!this.getHighlightedOption()){
-var fc=this.domNode.firstChild;
-this._focusOptionNode(fc.style.display=="none"?fc.nextSibling:fc);
-}else{
-var ns=this._highlighted_option.nextSibling;
-if(ns&&ns.style.display!="none"){
-this._focusOptionNode(ns);
-}else{
-this.highlightFirstOption();
-}
-}
-dojo.window.scrollIntoView(this._highlighted_option);
-},highlightFirstOption:function(){
-var _44=this.domNode.firstChild;
-var _45=_44.nextSibling;
-this._focusOptionNode(_45.style.display=="none"?_44:_45);
-dojo.window.scrollIntoView(this._highlighted_option);
-},highlightLastOption:function(){
-this._focusOptionNode(this.domNode.lastChild.previousSibling);
-dojo.window.scrollIntoView(this._highlighted_option);
-},_highlightPrevOption:function(){
-if(!this.getHighlightedOption()){
-var lc=this.domNode.lastChild;
-this._focusOptionNode(lc.style.display=="none"?lc.previousSibling:lc);
-}else{
-var ps=this._highlighted_option.previousSibling;
-if(ps&&ps.style.display!="none"){
-this._focusOptionNode(ps);
-}else{
-this.highlightLastOption();
-}
-}
-dojo.window.scrollIntoView(this._highlighted_option);
-},_page:function(up){
-var _46=0;
-var _47=this.domNode.scrollTop;
-var _48=dojo.style(this.domNode,"height");
-if(!this.getHighlightedOption()){
-this._highlightNextOption();
-}
-while(_46<_48){
-if(up){
-if(!this.getHighlightedOption().previousSibling||this._highlighted_option.previousSibling.style.display=="none"){
-break;
-}
-this._highlightPrevOption();
-}else{
-if(!this.getHighlightedOption().nextSibling||this._highlighted_option.nextSibling.style.display=="none"){
-break;
-}
-this._highlightNextOption();
-}
-var _49=this.domNode.scrollTop;
-_46+=(_49-_47)*(up?-1:1);
-_47=_49;
-}
-},pageUp:function(){
-this._page(true);
-},pageDown:function(){
-this._page(false);
-},getHighlightedOption:function(){
-var ho=this._highlighted_option;
-return (ho&&ho.parentNode)?ho:null;
-},handleKey:function(key){
-switch(key){
-case dojo.keys.DOWN_ARROW:
-this._highlightNextOption();
-break;
-case dojo.keys.PAGE_DOWN:
-this.pageDown();
-break;
-case dojo.keys.UP_ARROW:
-this._highlightPrevOption();
-break;
-case dojo.keys.PAGE_UP:
-this.pageUp();
-break;
-}
-}});
-dojo.declare("dijit.form.ComboBox",[dijit.form.ValidationTextBox,dijit.form.ComboBoxMixin],{_setValueAttr:function(_4a,_4b,_4c){
-this.item=null;
-if(!_4a){
-_4a="";
-}
-dijit.form.ValidationTextBox.prototype._setValueAttr.call(this,_4a,_4b,_4c);
-}});
-dojo.declare("dijit.form._ComboBoxDataStore",null,{constructor:function(_4d){
-this.root=_4d;
-if(_4d.tagName!="SELECT"&&_4d.firstChild){
-_4d=dojo.query("select",_4d);
-if(_4d.length>0){
-_4d=_4d[0];
-}else{
-this.root.innerHTML="<SELECT>"+this.root.innerHTML+"</SELECT>";
-_4d=this.root.firstChild;
-}
-this.root=_4d;
-}
-dojo.query("> option",_4d).forEach(function(_4e){
-_4e.innerHTML=dojo.trim(_4e.innerHTML);
-});
-},getValue:function(_4f,_50,_51){
-return (_50=="value")?_4f.value:(_4f.innerText||_4f.textContent||"");
-},isItemLoaded:function(_52){
-return true;
-},getFeatures:function(){
-return {"dojo.data.api.Read":true,"dojo.data.api.Identity":true};
-},_fetchItems:function(_53,_54,_55){
-if(!_53.query){
-_53.query={};
-}
-if(!_53.query.name){
-_53.query.name="";
-}
-if(!_53.queryOptions){
-_53.queryOptions={};
-}
-var _56=dojo.data.util.filter.patternToRegExp(_53.query.name,_53.queryOptions.ignoreCase),_57=dojo.query("> option",this.root).filter(function(_58){
-return (_58.innerText||_58.textContent||"").match(_56);
+dojo.require("dijit._HasDropDown");
+dojo.requireLocalization("dijit.form", "ComboBox", 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");
+
+
+dojo.declare(
+ "dijit.form.ComboBoxMixin",
+ dijit._HasDropDown,
+ {
+ // summary:
+ // Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
+ // description:
+ // All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`.
+ // tags:
+ // protected
+
+ // item: Object
+ // This is the item returned by the dojo.data.store implementation that
+ // provides the data for this ComboBox, it's the currently selected item.
+ item: null,
+
+ // pageSize: Integer
+ // Argument to data provider.
+ // Specifies number of search results per page (before hitting "next" button)
+ pageSize: Infinity,
+
+ // store: [const] Object
+ // Reference to data provider object used by this ComboBox
+ store: null,
+
+ // fetchProperties: Object
+ // Mixin to the dojo.data store's fetch.
+ // For example, to set the sort order of the ComboBox menu, pass:
+ // | { sort: [{attribute:"name",descending: true}] }
+ // To override the default queryOptions so that deep=false, do:
+ // | { queryOptions: {ignoreCase: true, deep: false} }
+ fetchProperties:{},
+
+ // query: Object
+ // A query that can be passed to 'store' to initially filter the items,
+ // before doing further filtering based on `searchAttr` and the key.
+ // Any reference to the `searchAttr` is ignored.
+ query: {},
+
+ // autoComplete: Boolean
+ // If user types in a partial string, and then tab out of the `<input>` box,
+ // automatically copy the first entry displayed in the drop down list to
+ // the `<input>` field
+ autoComplete: true,
+
+ // highlightMatch: String
+ // One of: "first", "all" or "none".
+ //
+ // If the ComboBox/FilteringSelect opens with the search results and the searched
+ // string can be found, it will be highlighted. If set to "all"
+ // then will probably want to change `queryExpr` parameter to '*${0}*'
+ //
+ // Highlighting is only performed when `labelType` is "text", so as to not
+ // interfere with any HTML markup an HTML label might contain.
+ highlightMatch: "first",
+
+ // searchDelay: Integer
+ // Delay in milliseconds between when user types something and we start
+ // searching based on that value
+ searchDelay: 100,
+
+ // searchAttr: String
+ // Search for items in the data store where this attribute (in the item)
+ // matches what the user typed
+ searchAttr: "name",
+
+ // labelAttr: String?
+ // The entries in the drop down list come from this attribute in the
+ // dojo.data items.
+ // If not specified, the searchAttr attribute is used instead.
+ labelAttr: "",
+
+ // labelType: String
+ // Specifies how to interpret the labelAttr in the data store items.
+ // Can be "html" or "text".
+ labelType: "text",
+
+ // queryExpr: String
+ // This specifies what query ComboBox/FilteringSelect sends to the data store,
+ // based on what the user has typed. Changing this expression will modify
+ // whether the drop down shows only exact matches, a "starting with" match,
+ // etc. Use it in conjunction with highlightMatch.
+ // dojo.data query expression pattern.
+ // `${0}` will be substituted for the user text.
+ // `*` is used for wildcards.
+ // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
+ queryExpr: "${0}*",
+
+ // ignoreCase: Boolean
+ // Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
+ ignoreCase: true,
+
+ // hasDownArrow: Boolean
+ // Set this textbox to have a down arrow button, to display the drop down list.
+ // Defaults to true.
+ hasDownArrow: true,
+
+ templateString: dojo.cache("dijit.form", "templates/DropDownBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"),
+
+ baseClass: "dijitTextBox dijitComboBox",
+
+ // dropDownClass: [protected extension] String
+ // Name of the dropdown widget class used to select a date/time.
+ // Subclasses should specify this.
+ dropDownClass: "dijit.form._ComboBoxMenu",
+
+ // Set classes like dijitDownArrowButtonHover depending on
+ // mouse action over button node
+ cssStateNodes: {
+ "_buttonNode": "dijitDownArrowButton"
+ },
+
+ // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
+ maxHeight: -1,
+
+ // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
+ _stopClickEvents: false,
+
+ _getCaretPos: function(/*DomNode*/ element){
+ // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
+ var pos = 0;
+ if(typeof(element.selectionStart) == "number"){
+ // FIXME: this is totally borked on Moz < 1.3. Any recourse?
+ pos = element.selectionStart;
+ }else if(dojo.isIE){
+ // in the case of a mouse click in a popup being handled,
+ // then the dojo.doc.selection is not the textarea, but the popup
+ // var r = dojo.doc.selection.createRange();
+ // hack to get IE 6 to play nice. What a POS browser.
+ var tr = dojo.doc.selection.createRange().duplicate();
+ var ntr = element.createTextRange();
+ tr.move("character",0);
+ ntr.move("character",0);
+ try{
+ // If control doesn't have focus, you get an exception.
+ // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
+ // There appears to be no workaround for this - googled for quite a while.
+ ntr.setEndPoint("EndToEnd", tr);
+ pos = String(ntr.text).replace(/\r/g,"").length;
+ }catch(e){
+ // If focus has shifted, 0 is fine for caret pos.
+ }
+ }
+ return pos;
+ },
+
+ _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
+ location = parseInt(location);
+ dijit.selectInputText(element, location, location);
+ },
+
+ _setDisabledAttr: function(/*Boolean*/ value){
+ // Additional code to set disabled state of ComboBox node.
+ // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
+ this.inherited(arguments);
+ dijit.setWaiState(this.domNode, "disabled", value);
+ },
+
+ _abortQuery: function(){
+ // stop in-progress query
+ if(this.searchTimer){
+ clearTimeout(this.searchTimer);
+ this.searchTimer = null;
+ }
+ if(this._fetchHandle){
+ if(this._fetchHandle.abort){ this._fetchHandle.abort(); }
+ this._fetchHandle = null;
+ }
+ },
+
+ _onInput: function(/*Event*/ evt){
+ // summary:
+ // Handles paste events
+ if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){
+ this.searchTimer = setTimeout(dojo.hitch(this, function(){
+ this._onKey({charOrCode: 229}); // fake IME key to cause a search
+ }), 100); // long delay that will probably be preempted by keyboard input
+ }
+ this.inherited(arguments);
+ },
+
+ _onKey: function(/*Event*/ evt){
+ // summary:
+ // Handles keyboard events
+
+ var key = evt.charOrCode;
+
+ // except for cutting/pasting case - ctrl + x/v
+ if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){
+ return; // throw out weird key combinations and spurious events
+ }
+
+ var doSearch = false;
+ var pw = this.dropDown;
+ var dk = dojo.keys;
+ var highlighted = null;
+ this._prev_key_backspace = false;
+ this._abortQuery();
+
+ // _HasDropDown will do some of the work:
+ // 1. when drop down is not yet shown:
+ // - if user presses the down arrow key, call loadDropDown()
+ // 2. when drop down is already displayed:
+ // - on ESC key, call closeDropDown()
+ // - otherwise, call dropDown.handleKey() to process the keystroke
+ this.inherited(arguments);
+
+ if(this._opened){
+ highlighted = pw.getHighlightedOption();
+ }
+ switch(key){
+ case dk.PAGE_DOWN:
+ case dk.DOWN_ARROW:
+ case dk.PAGE_UP:
+ case dk.UP_ARROW:
+ // Keystroke caused ComboBox_menu to move to a different item.
+ // Copy new item to <input> box.
+ if(this._opened){
+ this._announceOption(highlighted);
+ }
+ dojo.stopEvent(evt);
+ break;
+
+ case dk.ENTER:
+ // prevent submitting form if user presses enter. Also
+ // prevent accepting the value if either Next or Previous
+ // are selected
+ if(highlighted){
+ // only stop event on prev/next
+ if(highlighted == pw.nextButton){
+ this._nextSearch(1);
+ dojo.stopEvent(evt);
+ break;
+ }else if(highlighted == pw.previousButton){
+ this._nextSearch(-1);
+ dojo.stopEvent(evt);
+ break;
+ }
+ }else{
+ // Update 'value' (ex: KY) according to currently displayed text
+ this._setBlurValue(); // set value if needed
+ this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
+ }
+ // default case:
+ // if enter pressed while drop down is open, or for FilteringSelect,
+ // if we are in the middle of a query to convert a directly typed in value to an item,
+ // prevent submit, but allow event to bubble
+ if(this._opened || this._fetchHandle){
+ evt.preventDefault();
+ }
+ // fall through
+
+ case dk.TAB:
+ var newvalue = this.get('displayedValue');
+ // if the user had More Choices selected fall into the
+ // _onBlur handler
+ if(pw && (
+ newvalue == pw._messages["previousMessage"] ||
+ newvalue == pw._messages["nextMessage"])
+ ){
+ break;
+ }
+ if(highlighted){
+ this._selectOption();
+ }
+ if(this._opened){
+ this._lastQuery = null; // in case results come back later
+ this.closeDropDown();
+ }
+ break;
+
+ case ' ':
+ if(highlighted){
+ // user is effectively clicking a choice in the drop down menu
+ dojo.stopEvent(evt);
+ this._selectOption();
+ this.closeDropDown();
+ }else{
+ // user typed a space into the input box, treat as normal character
+ doSearch = true;
+ }
+ break;
+
+ case dk.DELETE:
+ case dk.BACKSPACE:
+ this._prev_key_backspace = true;
+ doSearch = true;
+ break;
+
+ default:
+ // Non char keys (F1-F12 etc..) shouldn't open list.
+ // Ascii characters and IME input (Chinese, Japanese etc.) should.
+ //IME input produces keycode == 229.
+ doSearch = typeof key == 'string' || key == 229;
+ }
+ if(doSearch){
+ // need to wait a tad before start search so that the event
+ // bubbles through DOM and we have value visible
+ this.item = undefined; // undefined means item needs to be set
+ this.searchTimer = setTimeout(dojo.hitch(this, "_startSearchFromInput"),1);
+ }
+ },
+
+ _autoCompleteText: function(/*String*/ text){
+ // summary:
+ // Fill in the textbox with the first item from the drop down
+ // list, and highlight the characters that were
+ // auto-completed. For example, if user typed "CA" and the
+ // drop down list appeared, the textbox would be changed to
+ // "California" and "ifornia" would be highlighted.
+
+ var fn = this.focusNode;
+
+ // IE7: clear selection so next highlight works all the time
+ dijit.selectInputText(fn, fn.value.length);
+ // does text autoComplete the value in the textbox?
+ var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
+ if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
+ var cpos = this._getCaretPos(fn);
+ // only try to extend if we added the last character at the end of the input
+ if((cpos+1) > fn.value.length){
+ // only add to input node as we would overwrite Capitalisation of chars
+ // actually, that is ok
+ fn.value = text;//.substr(cpos);
+ // visually highlight the autocompleted characters
+ dijit.selectInputText(fn, cpos);
+ }
+ }else{
+ // text does not autoComplete; replace the whole value and highlight
+ fn.value = text;
+ dijit.selectInputText(fn);
+ }
+ },
+
+ _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
+ // summary:
+ // Callback when a search completes.
+ // description:
+ // 1. generates drop-down list and calls _showResultList() to display it
+ // 2. if this result list is from user pressing "more choices"/"previous choices"
+ // then tell screen reader to announce new option
+ this._fetchHandle = null;
+ if( this.disabled ||
+ this.readOnly ||
+ (dataObject.query[this.searchAttr] != this._lastQuery)
+ ){
+ return;
+ }
+ var wasSelected = this.dropDown._highlighted_option && dojo.hasClass(this.dropDown._highlighted_option, "dijitMenuItemSelected");
+ this.dropDown.clearResultList();
+ if(!results.length && !this._maxOptions){ // if no results and not just the previous choices button
+ this.closeDropDown();
+ return;
+ }
+
+ // Fill in the textbox with the first item from the drop down list,
+ // and highlight the characters that were auto-completed. For
+ // example, if user typed "CA" and the drop down list appeared, the
+ // textbox would be changed to "California" and "ifornia" would be
+ // highlighted.
+
+ dataObject._maxOptions = this._maxOptions;
+ var nodes = this.dropDown.createOptions(
+ results,
+ dataObject,
+ dojo.hitch(this, "_getMenuLabelFromItem")
+ );
+
+ // show our list (only if we have content, else nothing)
+ this._showResultList();
+
+ // #4091:
+ // tell the screen reader that the paging callback finished by
+ // shouting the next choice
+ if(dataObject.direction){
+ if(1 == dataObject.direction){
+ this.dropDown.highlightFirstOption();
+ }else if(-1 == dataObject.direction){
+ this.dropDown.highlightLastOption();
+ }
+ if(wasSelected){
+ this._announceOption(this.dropDown.getHighlightedOption());
+ }
+ }else if(this.autoComplete && !this._prev_key_backspace
+ // when the user clicks the arrow button to show the full list,
+ // startSearch looks for "*".
+ // it does not make sense to autocomplete
+ // if they are just previewing the options available.
+ && !/^[*]+$/.test(dataObject.query[this.searchAttr])){
+ this._announceOption(nodes[1]); // 1st real item
+ }
+ },
+
+ _showResultList: function(){
+ // summary:
+ // Display the drop down if not already displayed, or if it is displayed, then
+ // reposition it if necessary (reposition may be necessary if drop down's height changed).
+
+ this.closeDropDown(true);
+
+ // hide the tooltip
+ this.displayMessage("");
+
+ this.openDropDown();
+
+ dijit.setWaiState(this.domNode, "expanded", "true");
+ },
+
+ loadDropDown: function(/*Function*/ callback){
+ // Overrides _HasDropDown.loadDropDown().
+ // This is called when user has pressed button icon or pressed the down arrow key
+ // to open the drop down.
+
+ this._startSearchAll();
+ },
+
+ isLoaded: function(){
+ // signal to _HasDropDown that it needs to call loadDropDown() to load the
+ // drop down asynchronously before displaying it
+ return false;
+ },
+
+ closeDropDown: function(){
+ // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
+ // This method is the callback when the user types ESC or clicking
+ // the button icon while the drop down is open. It's also called by other code.
+ this._abortQuery();
+ if(this._opened){
+ this.inherited(arguments);
+ dijit.setWaiState(this.domNode, "expanded", "false");
+ dijit.removeWaiState(this.focusNode,"activedescendant");
+ }
+ },
+
+ _setBlurValue: function(){
+ // if the user clicks away from the textbox OR tabs away, set the
+ // value to the textbox value
+ // #4617:
+ // if value is now more choices or previous choices, revert
+ // the value
+ var newvalue = this.get('displayedValue');
+ var pw = this.dropDown;
+ if(pw && (
+ newvalue == pw._messages["previousMessage"] ||
+ newvalue == pw._messages["nextMessage"]
+ )
+ ){
+ this._setValueAttr(this._lastValueReported, true);
+ }else if(typeof this.item == "undefined"){
+ // Update 'value' (ex: KY) according to currently displayed text
+ this.item = null;
+ this.set('displayedValue', newvalue);
+ }else{
+ if(this.value != this._lastValueReported){
+ dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true);
+ }
+ this._refreshState();
+ }
+ },
+
+ _onBlur: function(){
+ // summary:
+ // Called magically when focus has shifted away from this widget and it's drop down
+ this.closeDropDown();
+ this.inherited(arguments);
+ },
+
+ _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
+ // summary:
+ // Set the displayed valued in the input box, and the hidden value
+ // that gets submitted, based on a dojo.data store item.
+ // description:
+ // Users shouldn't call this function; they should be calling
+ // set('item', value)
+ // tags:
+ // private
+ if(!displayedValue){
+ displayedValue = this.store.getValue(item, this.searchAttr);
+ }
+ var value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue;
+ this._set("item", item);
+ dijit.form.ComboBox.superclass._setValueAttr.call(this, value, priorityChange, displayedValue);
+ },
+
+ _announceOption: function(/*Node*/ node){
+ // summary:
+ // a11y code that puts the highlighted option in the textbox.
+ // This way screen readers will know what is happening in the
+ // menu.
+
+ if(!node){
+ return;
+ }
+ // pull the text value from the item attached to the DOM node
+ var newValue;
+ if(node == this.dropDown.nextButton ||
+ node == this.dropDown.previousButton){
+ newValue = node.innerHTML;
+ this.item = undefined;
+ this.value = '';
+ }else{
+ newValue = this.store.getValue(node.item, this.searchAttr).toString();
+ this.set('item', node.item, false, newValue);
+ }
+ // get the text that the user manually entered (cut off autocompleted text)
+ this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
+ // set up ARIA activedescendant
+ dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
+ // autocomplete the rest of the option to announce change
+ this._autoCompleteText(newValue);
+ },
+
+ _selectOption: function(/*Event*/ evt){
+ // summary:
+ // Menu callback function, called when an item in the menu is selected.
+ if(evt){
+ this._announceOption(evt.target);
+ }
+ this.closeDropDown();
+ this._setCaretPos(this.focusNode, this.focusNode.value.length);
+ dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange
+ },
+
+ _startSearchAll: function(){
+ this._startSearch('');
+ },
+
+ _startSearchFromInput: function(){
+ this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
+ },
+
+ _getQueryString: function(/*String*/ text){
+ return dojo.string.substitute(this.queryExpr, [text]);
+ },
+
+ _startSearch: function(/*String*/ key){
+ // summary:
+ // Starts a search for elements matching key (key=="" means to return all items),
+ // and calls _openResultList() when the search completes, to display the results.
+ if(!this.dropDown){
+ var popupId = this.id + "_popup",
+ dropDownConstructor = dojo.getObject(this.dropDownClass, false);
+ this.dropDown = new dropDownConstructor({
+ onChange: dojo.hitch(this, this._selectOption),
+ id: popupId,
+ dir: this.dir
+ });
+ dijit.removeWaiState(this.focusNode,"activedescendant");
+ dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
+ }
+ // create a new query to prevent accidentally querying for a hidden
+ // value from FilteringSelect's keyField
+ var query = dojo.clone(this.query); // #5970
+ this._lastInput = key; // Store exactly what was entered by the user.
+ this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
+ // #5970: set _lastQuery, *then* start the timeout
+ // otherwise, if the user types and the last query returns before the timeout,
+ // _lastQuery won't be set and their input gets rewritten
+ this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
+ this.searchTimer = null;
+ var fetch = {
+ queryOptions: {
+ ignoreCase: this.ignoreCase,
+ deep: true
+ },
+ query: query,
+ onBegin: dojo.hitch(this, "_setMaxOptions"),
+ onComplete: dojo.hitch(this, "_openResultList"),
+ onError: function(errText){
+ _this._fetchHandle = null;
+ console.error('dijit.form.ComboBox: ' + errText);
+ _this.closeDropDown();
+ },
+ start: 0,
+ count: this.pageSize
+ };
+ dojo.mixin(fetch, _this.fetchProperties);
+ this._fetchHandle = _this.store.fetch(fetch);
+
+ var nextSearch = function(dataObject, direction){
+ dataObject.start += dataObject.count*direction;
+ // #4091:
+ // tell callback the direction of the paging so the screen
+ // reader knows which menu option to shout
+ dataObject.direction = direction;
+ this._fetchHandle = this.store.fetch(dataObject);
+ this.focus();
+ };
+ this._nextSearch = this.dropDown.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
+ }, query, this), this.searchDelay);
+ },
+
+ _setMaxOptions: function(size, request){
+ this._maxOptions = size;
+ },
+
+ _getValueField: function(){
+ // summary:
+ // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
+ // Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
+ return this.searchAttr;
+ },
+
+ //////////// INITIALIZATION METHODS ///////////////////////////////////////
+
+ constructor: function(){
+ this.query={};
+ this.fetchProperties={};
+ },
+
+ postMixInProperties: function(){
+ if(!this.store){
+ var srcNodeRef = this.srcNodeRef;
+
+ // if user didn't specify store, then assume there are option tags
+ this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
+
+ // if there is no value set and there is an option list, set
+ // the value to the first value to be consistent with native
+ // Select
+
+ // Firefox and Safari set value
+ // IE6 and Opera set selectedIndex, which is automatically set
+ // by the selected attribute of an option tag
+ // IE6 does not set value, Opera sets value = selectedIndex
+ if(!("value" in this.params)){
+ var item = (this.item = this.store.fetchSelectedItem());
+ if(item){
+ var valueField = this._getValueField();
+ this.value = this.store.getValue(item, valueField);
+ }
+ }
+ }
+
+ this.inherited(arguments);
+ },
+
+ postCreate: function(){
+ // summary:
+ // Subclasses must call this method from their postCreate() methods
+ // tags:
+ // protected
+
+ // find any associated label element and add to ComboBox node.
+ var label=dojo.query('label[for="'+this.id+'"]');
+ if(label.length){
+ label[0].id = (this.id+"_label");
+ dijit.setWaiState(this.domNode, "labelledby", label[0].id);
+
+ }
+ this.inherited(arguments);
+ },
+
+ _setHasDownArrowAttr: function(val){
+ this.hasDownArrow = val;
+ this._buttonNode.style.display = val ? "" : "none";
+ },
+
+ _getMenuLabelFromItem: function(/*Item*/ item){
+ var label = this.labelFunc(item, this.store),
+ labelType = this.labelType;
+ // If labelType is not "text" we don't want to screw any markup ot whatever.
+ if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
+ label = this.doHighlight(label, this._escapeHtml(this._lastInput));
+ labelType = "html";
+ }
+ return {html: labelType == "html", label: label};
+ },
+
+ doHighlight: function(/*String*/ label, /*String*/ find){
+ // summary:
+ // Highlights the string entered by the user in the menu. By default this
+ // highlights the first occurrence found. Override this method
+ // to implement your custom highlighting.
+ // tags:
+ // protected
+
+ var
+ // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
+ modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
+ i = this.queryExpr.indexOf("${0}");
+ find = dojo.regexp.escapeString(find); // escape regexp special chars
+ return this._escapeHtml(label).replace(
+ // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
+ new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
+ '<span class="dijitComboBoxHighlightMatch">$1</span>'
+ ); // returns String, (almost) valid HTML (entities encoded)
+ },
+
+ _escapeHtml: function(/*String*/ str){
+ // TODO Should become dojo.html.entities(), when exists use instead
+ // summary:
+ // Adds escape sequences for special characters in XML: &<>"'
+ str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
+ .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
+ return str; // string
+ },
+
+ reset: function(){
+ // Overrides the _FormWidget.reset().
+ // Additionally reset the .item (to clean up).
+ this.item = null;
+ this.inherited(arguments);
+ },
+
+ labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
+ // summary:
+ // Computes the label to display based on the dojo.data store item.
+ // returns:
+ // The label that the ComboBox should display
+ // tags:
+ // private
+
+ // Use toString() because XMLStore returns an XMLItem whereas this
+ // method is expected to return a String (#9354)
+ return store.getValue(item, this.labelAttr || this.searchAttr).toString(); // String
+ }
+ }
+);
+
+dojo.declare(
+ "dijit.form._ComboBoxMenu",
+ [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
+ {
+ // summary:
+ // Focus-less menu for internal use in `dijit.form.ComboBox`
+ // tags:
+ // private
+
+ templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
+ +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' role='option'></li>"
+ +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' role='option'></li>"
+ +"</ul>",
+
+ // _messages: Object
+ // Holds "next" and "previous" text for paging buttons on drop down
+ _messages: null,
+
+ baseClass: "dijitComboBoxMenu",
+
+ postMixInProperties: function(){
+ this.inherited(arguments);
+ this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
+ },
+
+ buildRendering: function(){
+ this.inherited(arguments);
+
+ // fill in template with i18n messages
+ this.previousButton.innerHTML = this._messages["previousMessage"];
+ this.nextButton.innerHTML = this._messages["nextMessage"];
+ },
+
+ _setValueAttr: function(/*Object*/ value){
+ this.value = value;
+ this.onChange(value);
+ },
+
+ // stubs
+ onChange: function(/*Object*/ value){
+ // summary:
+ // Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu.
+ // Probably should be called onSelect.
+ // tags:
+ // callback
+ },
+ onPage: function(/*Number*/ direction){
+ // summary:
+ // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
+ // tags:
+ // callback
+ },
+
+ onClose: function(){
+ // summary:
+ // Callback from dijit.popup code to this widget, notifying it that it closed
+ // tags:
+ // private
+ this._blurOptionNode();
+ },
+
+ _createOption: function(/*Object*/ item, labelFunc){
+ // summary:
+ // Creates an option to appear on the popup menu subclassed by
+ // `dijit.form.FilteringSelect`.
+
+ var menuitem = dojo.create("li", {
+ "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
+ role: "option"
+ });
+ var labelObject = labelFunc(item);
+ if(labelObject.html){
+ menuitem.innerHTML = labelObject.label;
+ }else{
+ menuitem.appendChild(
+ dojo.doc.createTextNode(labelObject.label)
+ );
+ }
+ // #3250: in blank options, assign a normal height
+ if(menuitem.innerHTML == ""){
+ menuitem.innerHTML = "&nbsp;";
+ }
+ menuitem.item=item;
+ return menuitem;
+ },
+
+ createOptions: function(results, dataObject, labelFunc){
+ // summary:
+ // Fills in the items in the drop down list
+ // results:
+ // Array of dojo.data items
+ // dataObject:
+ // dojo.data store
+ // labelFunc:
+ // Function to produce a label in the drop down list from a dojo.data item
+
+ //this._dataObject=dataObject;
+ //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
+ // display "Previous . . ." button
+ this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
+ dojo.attr(this.previousButton, "id", this.id + "_prev");
+ // create options using _createOption function defined by parent
+ // ComboBox (or FilteringSelect) class
+ // #2309:
+ // iterate over cache nondestructively
+ dojo.forEach(results, function(item, i){
+ var menuitem = this._createOption(item, labelFunc);
+ dojo.attr(menuitem, "id", this.id + i);
+ this.domNode.insertBefore(menuitem, this.nextButton);
+ }, this);
+ // display "Next . . ." button
+ var displayMore = false;
+ //Try to determine if we should show 'more'...
+ if(dataObject._maxOptions && dataObject._maxOptions != -1){
+ if((dataObject.start + dataObject.count) < dataObject._maxOptions){
+ displayMore = true;
+ }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){
+ //Weird return from a datastore, where a start + count > maxOptions
+ // implies maxOptions isn't really valid and we have to go into faking it.
+ //And more or less assume more if count == results.length
+ displayMore = true;
+ }
+ }else if(dataObject.count == results.length){
+ //Don't know the size, so we do the best we can based off count alone.
+ //So, if we have an exact match to count, assume more.
+ displayMore = true;
+ }
+
+ this.nextButton.style.display = displayMore ? "" : "none";
+ dojo.attr(this.nextButton,"id", this.id + "_next");
+ return this.domNode.childNodes;
+ },
+
+ clearResultList: function(){
+ // summary:
+ // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
+ while(this.domNode.childNodes.length>2){
+ this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
+ }
+ this._blurOptionNode();
+ },
+
+ _onMouseDown: function(/*Event*/ evt){
+ dojo.stopEvent(evt);
+ },
+
+ _onMouseUp: function(/*Event*/ evt){
+ if(evt.target === this.domNode || !this._highlighted_option){
+ // !this._highlighted_option check to prevent immediate selection when menu appears on top
+ // of <input>, see #9898. Note that _HasDropDown also has code to prevent this.
+ return;
+ }else if(evt.target == this.previousButton){
+ this._blurOptionNode();
+ this.onPage(-1);
+ }else if(evt.target == this.nextButton){
+ this._blurOptionNode();
+ this.onPage(1);
+ }else{
+ var tgt = evt.target;
+ // while the clicked node is inside the div
+ while(!tgt.item){
+ // recurse to the top
+ tgt = tgt.parentNode;
+ }
+ this._setValueAttr({ target: tgt }, true);
+ }
+ },
+
+ _onMouseOver: function(/*Event*/ evt){
+ if(evt.target === this.domNode){ return; }
+ var tgt = evt.target;
+ if(!(tgt == this.previousButton || tgt == this.nextButton)){
+ // while the clicked node is inside the div
+ while(!tgt.item){
+ // recurse to the top
+ tgt = tgt.parentNode;
+ }
+ }
+ this._focusOptionNode(tgt);
+ },
+
+ _onMouseOut: function(/*Event*/ evt){
+ if(evt.target === this.domNode){ return; }
+ this._blurOptionNode();
+ },
+
+ _focusOptionNode: function(/*DomNode*/ node){
+ // summary:
+ // Does the actual highlight.
+ if(this._highlighted_option != node){
+ this._blurOptionNode();
+ this._highlighted_option = node;
+ dojo.addClass(this._highlighted_option, "dijitMenuItemSelected");
+ }
+ },
+
+ _blurOptionNode: function(){
+ // summary:
+ // Removes highlight on highlighted option.
+ if(this._highlighted_option){
+ dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected");
+ this._highlighted_option = null;
+ }
+ },
+
+ _highlightNextOption: function(){
+ // summary:
+ // Highlight the item just below the current selection.
+ // If nothing selected, highlight first option.
+
+ // because each press of a button clears the menu,
+ // the highlighted option sometimes becomes detached from the menu!
+ // test to see if the option has a parent to see if this is the case.
+ if(!this.getHighlightedOption()){
+ var fc = this.domNode.firstChild;
+ this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc);
+ }else{
+ var ns = this._highlighted_option.nextSibling;
+ if(ns && ns.style.display != "none"){
+ this._focusOptionNode(ns);
+ }else{
+ this.highlightFirstOption();
+ }
+ }
+ // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
+ dojo.window.scrollIntoView(this._highlighted_option);
+ },
+
+ highlightFirstOption: function(){
+ // summary:
+ // Highlight the first real item in the list (not Previous Choices).
+ var first = this.domNode.firstChild;
+ var second = first.nextSibling;
+ this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list
+ dojo.window.scrollIntoView(this._highlighted_option);
+ },
+
+ highlightLastOption: function(){
+ // summary:
+ // Highlight the last real item in the list (not More Choices).
+ this._focusOptionNode(this.domNode.lastChild.previousSibling);
+ dojo.window.scrollIntoView(this._highlighted_option);
+ },
+
+ _highlightPrevOption: function(){
+ // summary:
+ // Highlight the item just above the current selection.
+ // If nothing selected, highlight last option (if
+ // you select Previous and try to keep scrolling up the list).
+ if(!this.getHighlightedOption()){
+ var lc = this.domNode.lastChild;
+ this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
+ }else{
+ var ps = this._highlighted_option.previousSibling;
+ if(ps && ps.style.display != "none"){
+ this._focusOptionNode(ps);
+ }else{
+ this.highlightLastOption();
+ }
+ }
+ dojo.window.scrollIntoView(this._highlighted_option);
+ },
+
+ _page: function(/*Boolean*/ up){
+ // summary:
+ // Handles page-up and page-down keypresses
+
+ var scrollamount = 0;
+ var oldscroll = this.domNode.scrollTop;
+ var height = dojo.style(this.domNode, "height");
+ // if no item is highlighted, highlight the first option
+ if(!this.getHighlightedOption()){
+ this._highlightNextOption();
+ }
+ while(scrollamount<height){
+ if(up){
+ // stop at option 1
+ if(!this.getHighlightedOption().previousSibling ||
+ this._highlighted_option.previousSibling.style.display == "none"){
+ break;
+ }
+ this._highlightPrevOption();
+ }else{
+ // stop at last option
+ if(!this.getHighlightedOption().nextSibling ||
+ this._highlighted_option.nextSibling.style.display == "none"){
+ break;
+ }
+ this._highlightNextOption();
+ }
+ // going backwards
+ var newscroll=this.domNode.scrollTop;
+ scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
+ oldscroll=newscroll;
+ }
+ },
+
+ pageUp: function(){
+ // summary:
+ // Handles pageup keypress.
+ // TODO: just call _page directly from handleKey().
+ // tags:
+ // private
+ this._page(true);
+ },
+
+ pageDown: function(){
+ // summary:
+ // Handles pagedown keypress.
+ // TODO: just call _page directly from handleKey().
+ // tags:
+ // private
+ this._page(false);
+ },
+
+ getHighlightedOption: function(){
+ // summary:
+ // Returns the highlighted option.
+ var ho = this._highlighted_option;
+ return (ho && ho.parentNode) ? ho : null;
+ },
+
+ handleKey: function(evt){
+ // summary:
+ // Handle keystroke event forwarded from ComboBox, returning false if it's
+ // a keystroke I recognize and process, true otherwise.
+ switch(evt.charOrCode){
+ case dojo.keys.DOWN_ARROW:
+ this._highlightNextOption();
+ return false;
+ case dojo.keys.PAGE_DOWN:
+ this.pageDown();
+ return false;
+ case dojo.keys.UP_ARROW:
+ this._highlightPrevOption();
+ return false;
+ case dojo.keys.PAGE_UP:
+ this.pageUp();
+ return false;
+ default:
+ return true;
+ }
+ }
+ }
+);
+
+dojo.declare(
+ "dijit.form.ComboBox",
+ [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
+ {
+ // summary:
+ // Auto-completing text box, and base class for dijit.form.FilteringSelect.
+ //
+ // description:
+ // The drop down box's values are populated from an class called
+ // a data provider, which returns a list of values based on the characters
+ // that the user has typed into the input box.
+ // If OPTION tags are used as the data provider via markup,
+ // then the OPTION tag's child text node is used as the widget value
+ // when selected. The OPTION tag's value attribute is ignored.
+ // To set the default value when using OPTION tags, specify the selected
+ // attribute on 1 of the child OPTION tags.
+ //
+ // Some of the options to the ComboBox are actually arguments to the data
+ // provider.
+
+ _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
+ // summary:
+ // Hook so set('value', value) works.
+ // description:
+ // Sets the value of the select.
+ this._set("item", null); // value not looked up in store
+ if(!value){ value = ''; } // null translates to blank
+ dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue);
+ }
+ }
+);
+
+dojo.declare("dijit.form._ComboBoxDataStore", null, {
+ // summary:
+ // Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data
+ //
+ // description:
+ // Provides a store for inlined data like:
+ //
+ // | <select>
+ // | <option value="AL">Alabama</option>
+ // | ...
+ //
+ // Actually. just implements the subset of dojo.data.Read/Notification
+ // needed for ComboBox and FilteringSelect to work.
+ //
+ // Note that an item is just a pointer to the <option> DomNode.
+
+ constructor: function( /*DomNode*/ root){
+ this.root = root;
+ if(root.tagName != "SELECT" && root.firstChild){
+ root = dojo.query("select", root);
+ if(root.length > 0){ // SELECT is a child of srcNodeRef
+ root = root[0];
+ }else{ // no select, so create 1 to parent the option tags to define selectedIndex
+ this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>";
+ root = this.root.firstChild;
+ }
+ this.root = root;
+ }
+ dojo.query("> option", root).forEach(function(node){
+ // TODO: this was added in #3858 but unclear why/if it's needed; doesn't seem to be.
+ // If it is needed then can we just hide the select itself instead?
+ //node.style.display="none";
+ node.innerHTML = dojo.trim(node.innerHTML);
+ });
+
+ },
+
+ getValue: function( /*item*/ item,
+ /*attribute-name-string*/ attribute,
+ /*value?*/ defaultValue){
+ return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
+ },
+
+ isItemLoaded: function(/*anything*/ something){
+ return true;
+ },
+
+ getFeatures: function(){
+ return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
+ },
+
+ _fetchItems: function( /*Object*/ args,
+ /*Function*/ findCallback,
+ /*Function*/ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+ if(!args.query){ args.query = {}; }
+ if(!args.query.name){ args.query.name = ""; }
+ if(!args.queryOptions){ args.queryOptions = {}; }
+ var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
+ items = dojo.query("> option", this.root).filter(function(option){
+ return (option.innerText || option.textContent || '').match(matcher);
+ } );
+ if(args.sort){
+ items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
+ }
+ findCallback(items, args);
+ },
+
+ close: function(/*dojo.data.api.Request || args || null*/ request){
+ return;
+ },
+
+ getLabel: function(/*item*/ item){
+ return item.innerHTML;
+ },
+
+ getIdentity: function(/*item*/ item){
+ return dojo.attr(item, "value");
+ },
+
+ fetchItemByIdentity: function(/*Object*/ args){
+ // summary:
+ // Given the identity of an item, this method returns the item that has
+ // that identity through the onItem callback.
+ // Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
+ //
+ // description:
+ // Given arguments like:
+ //
+ // | {identity: "CA", onItem: function(item){...}
+ //
+ // Call `onItem()` with the DOM node `<option value="CA">California</option>`
+ var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0];
+ args.onItem(item);
+ },
+
+ fetchSelectedItem: function(){
+ // summary:
+ // Get the option marked as selected, like `<option selected>`.
+ // Not part of dojo.data API.
+ var root = this.root,
+ si = root.selectedIndex;
+ return typeof si == "number"
+ ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0]
+ : null; // dojo.data.Item
+ }
});
-if(_53.sort){
-_57.sort(dojo.data.util.sorter.createSortFunction(_53.sort,this));
-}
-_54(_57,_53);
-},close:function(_59){
-return;
-},getLabel:function(_5a){
-return _5a.innerHTML;
-},getIdentity:function(_5b){
-return dojo.attr(_5b,"value");
-},fetchItemByIdentity:function(_5c){
-var _5d=dojo.query("> option[value='"+_5c.identity+"']",this.root)[0];
-_5c.onItem(_5d);
-},fetchSelectedItem:function(){
-var _5e=this.root,si=_5e.selectedIndex;
-return typeof si=="number"?dojo.query("> option:nth-child("+(si!=-1?si+1:1)+")",_5e)[0]:null;
-}});
+//Mix in the simple fetch implementation to this class.
dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch);
+
}