Subversion Repositories Applications.papyrus

Rev

Blame | Last modification | View Log | RSS feed

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.data.ItemFileReadStore");
dojo.require("dijit.form.ValidationTextBox");
dojo.requireLocalization("dijit.form", "ComboBox", null, "ko,zh,ja,zh-tw,ru,it,hu,ROOT,fr,pt,pl,es,de,cs");

dojo.declare(
        "dijit.form.ComboBoxMixin",
        null,
        {
                // summary:
                //              Auto-completing text box, and base class for FilteringSelect widget.
                //
                //              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.
                //
                //              Some of the options to the ComboBox are actually arguments to the data
                //              provider.
                //
                //              You can assume that all the form widgets (and thus anything that mixes
                //              in ComboBoxMixin) will inherit from _FormWidget and thus the "this"
                //              reference will also "be a" _FormWidget.

                // item: Object
                //              This is the item returned by the dojo.data.store implementation that
                //              provides the data for this cobobox, 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: Object
                //              Reference to data provider object used by this ComboBox
                store: null,

                // 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.
                query: {},

                // autoComplete: Boolean
                //              If you type 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,

                // searchDelay: Integer
                //              Delay in milliseconds between when user types something and we start
                //              searching based on that value
                searchDelay: 100,

                // searchAttr: String
                //              Searches pattern match against this field
                searchAttr: "name",

                // ignoreCase: Boolean
                //              Set true if the ComboBox should ignore case when matching possible items
                ignoreCase: true,

                // hasDownArrow: Boolean
                //              Set this textbox to have a down arrow button.
                //              Defaults to true.
                hasDownArrow:true,

                // _hasFocus: Boolean
                //              Represents focus state of the textbox
                // TODO: get rid of this; it's unnecessary (but currently referenced in FilteringSelect)
                _hasFocus:false,

                templateString:"<table class=\"dijit dijitReset dijitInlineTable dijitLeft\" cellspacing=\"0\" cellpadding=\"0\"\n\tid=\"widget_${id}\" name=\"${name}\" dojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse\" waiRole=\"presentation\"\n\t><tr class=\"dijitReset\"\n\t\t><td class='dijitReset dijitStretch dijitInputField' width=\"100%\"\n\t\t\t><input type=\"text\" autocomplete=\"off\" name=\"${name}\"\n\t\t\tdojoAttachEvent=\"onkeypress, onkeyup, onfocus, compositionend\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" waiRole=\"combobox\"\n\t\t/></td\n\t\t><td class=\"dijitReset dijitValidationIconField\" width=\"0%\"\n\t\t\t><div dojoAttachPoint='iconNode' class='dijitValidationIcon'></div\n\t\t\t><div class='dijitValidationIconText'>&Chi;</div\n\t\t></td\n\t\t><td class='dijitReset dijitRight dijitButtonNode dijitDownArrowButton' width=\"0%\"\n\t\t\tdojoAttachPoint=\"downArrowNode\"\n\t\t\tdojoAttachEvent=\"onmousedown:_onArrowMouseDown,onmouseup:_onMouse,onmouseenter:_onMouse,onmouseleave:_onMouse\"\n\t\t\t><div class=\"dijitDownArrowButtonInner\" waiRole=\"presentation\"\n\t\t\t\t><div class=\"dijitDownArrowButtonChar\">&#9660;</div\n\t\t\t></div\n\t\t></td\t\n\t></tr\n></table>\n",

                baseClass:"dijitComboBox",

                _lastDisplayedValue: "",

                getValue:function(){
                        // don't get the textbox value but rather the previously set hidden value
                        return dijit.form.TextBox.superclass.getValue.apply(this, arguments);
                },

                setDisplayedValue:function(/*String*/ value){
                        this._lastDisplayedValue = value;
                        this.setValue(value, true);
                },

                _getCaretPos: function(/*DomNode*/ element){
                        // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
                        if(typeof(element.selectionStart)=="number"){
                                // FIXME: this is totally borked on Moz < 1.3. Any recourse?
                                return element.selectionStart;
                        }else if(dojo.isIE){
                                // in the case of a mouse click in a popup being handled,
                                // then the document.selection is not the textarea, but the popup
                                // var r = document.selection.createRange();
                                // hack to get IE 6 to play nice. What a POS browser.
                                var tr = document.selection.createRange().duplicate();
                                var ntr = element.createTextRange();
                                tr.move("character",0);
                                ntr.move("character",0);
                                try{
                                        // If control doesnt 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);
                                        return String(ntr.text).replace(/\r/g,"").length;
                                }catch(e){
                                        return 0; // If focus has shifted, 0 is fine for caret pos.
                                }
                        }
                },

                _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
                        location = parseInt(location);
                        this._setSelectedRange(element, location, location);
                },

                _setSelectedRange: function(/*DomNode*/ element, /*Number*/ start, /*Number*/ end){
                        if(!end){
                                end = element.value.length;
                        }  // NOTE: Strange - should be able to put caret at start of text?
                        // Mozilla
                        // parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130
                        if(element.setSelectionRange){
                                dijit.focus(element);
                                element.setSelectionRange(start, end);
                        }else if(element.createTextRange){ // IE
                                var range = element.createTextRange();
                                with(range){
                                        collapse(true);
                                        moveEnd('character', end);
                                        moveStart('character', start);
                                        select();
                                }
                        }else{ //otherwise try the event-creation hack (our own invention)
                                // do we need these?
                                element.value = element.value;
                                element.blur();
                                dijit.focus(element);
                                // figure out how far back to go
                                var dist = parseInt(element.value.length)-end;
                                var tchar = String.fromCharCode(37);
                                var tcc = tchar.charCodeAt(0);
                                for(var x = 0; x < dist; x++){
                                        var te = document.createEvent("KeyEvents");
                                        te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc);
                                        element.dispatchEvent(te);
                                }
                        }
                },

                onkeypress: function(/*Event*/ evt){
                        // summary: handles keyboard events

                        //except for pasting case - ctrl + v(118)
                        if(evt.altKey || (evt.ctrlKey && evt.charCode != 118)){
                                return;
                        }
                        var doSearch = false;
                        this.item = null; // #4872
                        if(this._isShowingNow){this._popupWidget.handleKey(evt);}
                        switch(evt.keyCode){
                                case dojo.keys.PAGE_DOWN:
                                case dojo.keys.DOWN_ARROW:
                                        if(!this._isShowingNow||this._prev_key_esc){
                                                this._arrowPressed();
                                                doSearch=true;
                                        }else{
                                                this._announceOption(this._popupWidget.getHighlightedOption());
                                        }
                                        dojo.stopEvent(evt);
                                        this._prev_key_backspace = false;
                                        this._prev_key_esc = false;
                                        break;

                                case dojo.keys.PAGE_UP:
                                case dojo.keys.UP_ARROW:
                                        if(this._isShowingNow){
                                                this._announceOption(this._popupWidget.getHighlightedOption());
                                        }
                                        dojo.stopEvent(evt);
                                        this._prev_key_backspace = false;
                                        this._prev_key_esc = false;
                                        break;

                                case dojo.keys.ENTER:
                                        // prevent submitting form if user presses enter
                                        // also prevent accepting the value if either Next or Previous are selected
                                        var highlighted;
                                        if(this._isShowingNow&&(highlighted=this._popupWidget.getHighlightedOption())){
                                                // only stop event on prev/next
                                                if(highlighted==this._popupWidget.nextButton){
                                                        this._nextSearch(1);
                                                        dojo.stopEvent(evt);
                                                        break;
                                                }
                                                else if(highlighted==this._popupWidget.previousButton){
                                                        this._nextSearch(-1);
                                                        dojo.stopEvent(evt);
                                                        break;
                                                }
                                        }else{
                                                this.setDisplayedValue(this.getDisplayedValue());
                                        }
                                        // default case:
                                        // prevent submit, but allow event to bubble
                                        evt.preventDefault();
                                        // fall through

                                case dojo.keys.TAB:
                                        var newvalue=this.getDisplayedValue();
                                        // #4617: if the user had More Choices selected fall into the _onBlur handler
                                        if(this._popupWidget &&
                                                (newvalue == this._popupWidget._messages["previousMessage"] ||
                                                        newvalue == this._popupWidget._messages["nextMessage"])){
                                                break;
                                        }
                                        if(this._isShowingNow){
                                                this._prev_key_backspace = false;
                                                this._prev_key_esc = false;
                                                if(this._popupWidget.getHighlightedOption()){
                                                        this._popupWidget.setValue({target:this._popupWidget.getHighlightedOption()}, true);
                                                }
                                                this._hideResultList();
                                        }
                                        break;

                                case dojo.keys.SPACE:
                                        this._prev_key_backspace = false;
                                        this._prev_key_esc = false;
                                        if(this._isShowingNow && this._popupWidget.getHighlightedOption()){
                                                dojo.stopEvent(evt);
                                                this._selectOption();
                                                this._hideResultList();
                                        }else{
                                                doSearch = true;
                                        }
                                        break;

                                case dojo.keys.ESCAPE:
                                        this._prev_key_backspace = false;
                                        this._prev_key_esc = true;
                                        this._hideResultList();
                                        if(this._lastDisplayedValue != this.getDisplayedValue()){
                                                this.setDisplayedValue(this._lastDisplayedValue);
                                                dojo.stopEvent(evt);
                                        }else{
                                                this.setValue(this.getValue(), false);
                                        }
                                        break;

                                case dojo.keys.DELETE:
                                case dojo.keys.BACKSPACE:
                                        this._prev_key_esc = false;
                                        this._prev_key_backspace = true;
                                        doSearch = true;
                                        break;

                                case dojo.keys.RIGHT_ARROW: // fall through

                                case dojo.keys.LEFT_ARROW: // fall through
                                        this._prev_key_backspace = false;
                                        this._prev_key_esc = false;
                                        break;

                                default:// non char keys (F1-F12 etc..)  shouldn't open list
                                        this._prev_key_backspace = false;
                                        this._prev_key_esc = false;
                                        if(dojo.isIE || evt.charCode != 0){
                                                doSearch=true;
                                        }
                        }
                        if(this.searchTimer){
                                clearTimeout(this.searchTimer);
                        }
                        if(doSearch){
                                // need to wait a tad before start search so that the event bubbles through DOM and we have value visible
                                this.searchTimer = setTimeout(dojo.hitch(this, this._startSearchFromInput), this.searchDelay);
                        }
                },

                _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.

                        // IE7: clear selection so next highlight works all the time
                        this._setSelectedRange(this.focusNode, this.focusNode.value.length, this.focusNode.value.length);
                        // does text autoComplete the value in the textbox?
                        // #3744: escape regexp so the user's input isn't treated as a regular expression.
                        // Example: If the user typed "(" then the regexp would throw "unterminated parenthetical."
                        // Also see #2558 for the autocompletion bug this regular expression fixes.
                        if(new RegExp("^"+escape(this.focusNode.value), this.ignoreCase ? "i" : "").test(escape(text))){
                                var cpos = this._getCaretPos(this.focusNode);
                                // only try to extend if we added the last character at the end of the input
                                if((cpos+1) > this.focusNode.value.length){
                                        // only add to input node as we would overwrite Capitalisation of chars
                                        // actually, that is ok
                                        this.focusNode.value = text;//.substr(cpos);
                                        // visually highlight the autocompleted characters
                                        this._setSelectedRange(this.focusNode, cpos, this.focusNode.value.length);
                                        dijit.setWaiState(this.focusNode, "valuenow", text);
                                }
                        }else{
                                // text does not autoComplete; replace the whole value and highlight
                                this.focusNode.value = text;
                                this._setSelectedRange(this.focusNode, 0, this.focusNode.value.length);
                                dijit.setWaiState(this.focusNode, "valuenow", text);
                        }
                },

                _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
                        if(this.disabled || dataObject.query[this.searchAttr] != this._lastQuery){
                                return;
                        }
                        this._popupWidget.clearResultList();
                        if(!results.length){
                                this._hideResultList();
                                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.

                        var zerothvalue=new String(this.store.getValue(results[0], this.searchAttr));
                        if(zerothvalue && 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.
                                (dataObject.query[this.searchAttr] != "*")){
                                this._autoCompleteText(zerothvalue);
                                // announce the autocompleted value
                                dijit.setWaiState(this.focusNode || this.domNode, "valuenow", zerothvalue);
                        }
                        this._popupWidget.createOptions(results, dataObject, dojo.hitch(this, 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(dataObject.direction==1){
                                        this._popupWidget.highlightFirstOption();
                                }else if(dataObject.direction==-1){
                                        this._popupWidget.highlightLastOption();
                                }
                                this._announceOption(this._popupWidget.getHighlightedOption());
                        }
                },

                _showResultList: function(){
                        this._hideResultList();
                        var items = this._popupWidget.getItems(),
                                visibleCount = Math.min(items.length,this.maxListLength);
                        this._arrowPressed();
                        // hide the tooltip
                        this._displayMessage("");
                        
                        // Position the list and if it's too big to fit on the screen then
                        // size it to the maximum possible height
                        // Our dear friend IE doesnt take max-height so we need to calculate that on our own every time
                        // TODO: want to redo this, see http://trac.dojotoolkit.org/ticket/3272, http://trac.dojotoolkit.org/ticket/4108
                        with(this._popupWidget.domNode.style){
                                // natural size of the list has changed, so erase old width/height settings,
                                // which were hardcoded in a previous call to this function (via dojo.marginBox() call) 
                                width="";
                                height="";
                        }
                        var best=this.open();
                        // #3212: only set auto scroll bars if necessary
                        // prevents issues with scroll bars appearing when they shouldn't when node is made wider (fractional pixels cause this)
                        var popupbox=dojo.marginBox(this._popupWidget.domNode);
                        this._popupWidget.domNode.style.overflow=((best.h==popupbox.h)&&(best.w==popupbox.w))?"hidden":"auto";
                        // #4134: borrow TextArea scrollbar test so content isn't covered by scrollbar and horizontal scrollbar doesn't appear
                        var newwidth=best.w;
                        if(best.h<this._popupWidget.domNode.scrollHeight){newwidth+=16;}
                        dojo.marginBox(this._popupWidget.domNode, {h:best.h,w:Math.max(newwidth,this.domNode.offsetWidth)});
                },

                _hideResultList: function(){
                        if(this._isShowingNow){
                                dijit.popup.close(this._popupWidget);
                                this._arrowIdle();
                                this._isShowingNow=false;
                        }
                },

                _onBlur: function(){
                        // summary: called magically when focus has shifted away from this widget and it's dropdown
                        this._hasFocus=false;
                        this._hasBeenBlurred = true;
                        this._hideResultList();
                        this._arrowIdle();
                        // 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.getDisplayedValue();
                        if(this._popupWidget&&(newvalue==this._popupWidget._messages["previousMessage"]||newvalue==this._popupWidget._messages["nextMessage"])){
                                this.setValue(this._lastValueReported, true);
                        }else{
                                this.setDisplayedValue(newvalue);
                        }
                },

                onfocus:function(/*Event*/ evt){
                        this._hasFocus=true;
                        
                        // update styling to reflect that we are focused
                        this._onMouse(evt);
                },

                _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==null){return;}
                        // pull the text value from the item attached to the DOM node
                        var newValue;
                        if(node==this._popupWidget.nextButton||node==this._popupWidget.previousButton){
                                newValue=node.innerHTML;
                        }else{
                                newValue=this.store.getValue(node.item, this.searchAttr);
                        }
                        // get the text that the user manually entered (cut off autocompleted text)
                        this.focusNode.value=this.focusNode.value.substring(0, this._getCaretPos(this.focusNode));
                        // autocomplete the rest of the option to announce change
                        this._autoCompleteText(newValue);
                },

                _selectOption: function(/*Event*/ evt){
                        var tgt = null;
                        if(!evt){
                                evt ={ target: this._popupWidget.getHighlightedOption()};
                        }
                                // what if nothing is highlighted yet?
                        if(!evt.target){
                                // handle autocompletion where the the user has hit ENTER or TAB
                                this.setDisplayedValue(this.getDisplayedValue());
                                return;
                        // otherwise the user has accepted the autocompleted value
                        }else{
                                tgt = evt.target;
                        }
                        if(!evt.noHide){
                                this._hideResultList();
                                this._setCaretPos(this.focusNode, this.store.getValue(tgt.item, this.searchAttr).length);
                        }
                        this._doSelect(tgt);
                },

                _doSelect: function(tgt){
                        this.item = tgt.item;
                        this.setValue(this.store.getValue(tgt.item, this.searchAttr), true);
                },

                _onArrowMouseDown: function(evt){
                        // summary: callback when arrow is clicked
                        if(this.disabled){
                                return;
                        }
                        dojo.stopEvent(evt);
                        this.focus();
                        if(this._isShowingNow){
                                this._hideResultList();
                        }else{
                                // forces full population of results, if they click
                                // on the arrow it means they want to see more options
                                this._startSearch("");
                        }
                },

                _startSearchFromInput: function(){
                        this._startSearch(this.focusNode.value);
                },

                _startSearch: function(/*String*/ key){
                        if(!this._popupWidget){
                                this._popupWidget = new dijit.form._ComboBoxMenu({
                                        onChange: dojo.hitch(this, this._selectOption)
                                });
                        }
                        // create a new query to prevent accidentally querying for a hidden value from FilteringSelect's keyField
                        var query=this.query;
                        this._lastQuery=query[this.searchAttr]=key+"*";
                        var dataObject=this.store.fetch({queryOptions:{ignoreCase:this.ignoreCase, deep:true}, query: query, onComplete:dojo.hitch(this, "_openResultList"), start:0, count:this.pageSize});
                        function nextSearch(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;
                                dataObject.store.fetch(dataObject);
                        }
                        this._nextSearch=this._popupWidget.onPage=dojo.hitch(this, nextSearch, dataObject);
                },

                _getValueField:function(){
                        return this.searchAttr;
                },

                /////////////// Event handlers /////////////////////

                _arrowPressed: function(){
                        if(!this.disabled&&this.hasDownArrow){
                                dojo.addClass(this.downArrowNode, "dijitArrowButtonActive");
                        }
                },

                _arrowIdle: function(){
                        if(!this.disabled&&this.hasDownArrow){
                                dojo.removeClass(this.downArrowNode, "dojoArrowButtonPushed");
                        }
                },

                compositionend: function(/*Event*/ evt){
                        // summary: When inputting characters using an input method, such as Asian
                        // languages, it will generate this event instead of onKeyDown event
                        // Note: this event is only triggered in FF (not in IE)
                        this.onkeypress({charCode:-1});
                },

                //////////// INITIALIZATION METHODS ///////////////////////////////////////
                constructor: function(){
                        this.query={};
                },

                postMixInProperties: function(){
                        if(!this.hasDownArrow){
                                this.baseClass = "dijitTextBox";
                        }
                        if(!this.store){
                                // if user didn't specify store, then assume there are option tags
                                var items = this.srcNodeRef ? dojo.query("> option", this.srcNodeRef).map(function(node){
                                        node.style.display="none";
                                        return { value: node.getAttribute("value"), name: String(node.innerHTML) };
                                }) : {};
                                this.store = new dojo.data.ItemFileReadStore({data: {identifier:this._getValueField(), items:items}});

                                // 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
                                if(items && items.length && !this.value){
                                        // For <select>, IE does not let you set the value attribute of the srcNodeRef (and thus dojo.mixin does not copy it).
                                        // IE does understand selectedIndex though, which is automatically set by the selected attribute of an option tag
                                        this.value = items[this.srcNodeRef.selectedIndex != -1 ? this.srcNodeRef.selectedIndex : 0]
                                                [this._getValueField()];
                                }
                        }
                },

                uninitialize:function(){
                        if(this._popupWidget){
                                this._hideResultList();
                                this._popupWidget.destroy()
                        };
                },

                _getMenuLabelFromItem:function(/*Item*/ item){
                        return {html:false, label:this.store.getValue(item, this.searchAttr)};
                },

                open:function(){
                        this._isShowingNow=true;
                        return dijit.popup.open({
                                popup: this._popupWidget,
                                around: this.domNode,
                                parent: this
                        });
                }
        }
);

dojo.declare(
        "dijit.form._ComboBoxMenu",
        [dijit._Widget, dijit._Templated],

        {
                // summary:
                //      Focus-less div based menu for internal use in ComboBox

                templateString:"<div class='dijitMenu' dojoAttachEvent='onmousedown,onmouseup,onmouseover,onmouseout' tabIndex='-1' style='overflow:\"auto\";'>"
                                +"<div class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton'></div>"
                                +"<div class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton'></div>"
                        +"</div>",
                _messages:null,

                postMixInProperties:function(){
                        this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
                        this.inherited("postMixInProperties", arguments);
                },

                setValue:function(/*Object*/ value){
                        this.value=value;
                        this.onChange(value);
                },

                onChange:function(/*Object*/ value){},
                onPage:function(/*Number*/ direction){},

                postCreate:function(){
                        // fill in template with i18n messages
                        this.previousButton.innerHTML=this._messages["previousMessage"];
                        this.nextButton.innerHTML=this._messages["nextMessage"];
                        this.inherited("postCreate", arguments);
                },

                onClose:function(){
                        this._blurOptionNode();
                },

                _createOption:function(/*Object*/ item, labelFunc){
                        // summary: creates an option to appear on the popup menu
                        // subclassed by FilteringSelect

                        var labelObject=labelFunc(item);
                        var menuitem = document.createElement("div");
                        if(labelObject.html){menuitem.innerHTML=labelObject.label;}
                        else{menuitem.appendChild(document.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){
                        //this._dataObject=dataObject;
                        //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
                        // display "Previous . . ." button
                        this.previousButton.style.display=dataObject.start==0?"none":"";
                        // create options using _createOption function defined by parent ComboBox (or FilteringSelect) class
                        // #2309: iterate over cache nondestructively
                        var _this=this;
                        dojo.forEach(results, function(item){
                                var menuitem=_this._createOption(item, labelFunc);
                                menuitem.className = "dijitMenuItem";
                                _this.domNode.insertBefore(menuitem, _this.nextButton);
                        });
                        // display "Next . . ." button
                        this.nextButton.style.display=dataObject.count==results.length?"":"none";
                },

                clearResultList:function(){
                        // keep the previous and next buttons of course
                        while(this.domNode.childNodes.length>2){
                                this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
                        }
                },

                // these functions are called in showResultList
                getItems:function(){
                        return this.domNode.childNodes;
                },

                getListLength:function(){
                        return this.domNode.childNodes.length-2;
                },

                onmousedown:function(/*Event*/ evt){
                        dojo.stopEvent(evt);
                },

                onmouseup:function(/*Event*/ evt){
                        if(evt.target === this.domNode){
                                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 the clicked node is inside the div
                                while(!tgt.item){
                                        // recurse to the top
                                        tgt=tgt.parentNode;
                                }
                                this.setValue({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, "dijitMenuItemHover");
                        }
                },

                _blurOptionNode:function(){
                        // summary:
                        //      removes highlight on highlighted option
                        if(this._highlighted_option){
                                dojo.removeClass(this._highlighted_option, "dijitMenuItemHover");
                                this._highlighted_option = null;
                        }
                },

                _highlightNextOption:function(){
                        // 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()){
                                this._focusOptionNode(this.domNode.firstChild.style.display=="none"?this.domNode.firstChild.nextSibling:this.domNode.firstChild);
                        }else if(this._highlighted_option.nextSibling&&this._highlighted_option.nextSibling.style.display!="none"){
                                this._focusOptionNode(this._highlighted_option.nextSibling);
                        }
                        // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
                        dijit.scrollIntoView(this._highlighted_option);
                },

                highlightFirstOption:function(){
                        // highlight the non-Previous choices option
                        this._focusOptionNode(this.domNode.firstChild.nextSibling);
                        dijit.scrollIntoView(this._highlighted_option);
                },

                highlightLastOption:function(){
                        // highlight the noon-More choices option
                        this._focusOptionNode(this.domNode.lastChild.previousSibling);
                        dijit.scrollIntoView(this._highlighted_option);
                },

                _highlightPrevOption:function(){
                        // if nothing selected, highlight last option
                        // makes sense if you select Previous and try to keep scrolling up the list
                        if(!this.getHighlightedOption()){
                                this._focusOptionNode(this.domNode.lastChild.style.display=="none"?this.domNode.lastChild.previousSibling:this.domNode.lastChild);
                        }else if(this._highlighted_option.previousSibling&&this._highlighted_option.previousSibling.style.display!="none"){
                                this._focusOptionNode(this._highlighted_option.previousSibling);
                        }
                        dijit.scrollIntoView(this._highlighted_option);
                },

                _page:function(/*Boolean*/ up){
                        var scrollamount=0;
                        var oldscroll=this.domNode.scrollTop;
                        var height=parseInt(dojo.getComputedStyle(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(){
                        this._page(true);
                },

                pageDown:function(){
                        this._page(false);
                },

                getHighlightedOption:function(){
                        // summary:
                        //      Returns the highlighted option.
                        return this._highlighted_option&&this._highlighted_option.parentNode ? this._highlighted_option : null;
                },

                handleKey:function(evt){
                        switch(evt.keyCode){
                                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],
        {
                postMixInProperties: function(){
                        dijit.form.ComboBoxMixin.prototype.postMixInProperties.apply(this, arguments);
                        dijit.form.ValidationTextBox.prototype.postMixInProperties.apply(this, arguments);
                }
        }
);

}