From 1354d17270961fff662d40f90521223f8fd0d73b Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 14 Aug 2012 18:59:10 +0400 Subject: update dojo to 1.7.3 --- lib/dijit/form/ComboBox.js | 1233 +------------------------------------------- 1 file changed, 2 insertions(+), 1231 deletions(-) (limited to 'lib/dijit/form/ComboBox.js') diff --git a/lib/dijit/form/ComboBox.js b/lib/dijit/form/ComboBox.js index aecc5c4fc..2c46468c2 100644 --- a/lib/dijit/form/ComboBox.js +++ b/lib/dijit/form/ComboBox.js @@ -1,1231 +1,2 @@ -/* - 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"]){ //_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"); -dojo.require("dojo.data.util.simpleFetch"); -dojo.require("dojo.data.util.filter"); -dojo.require("dijit._CssStateMixin"); -dojo.require("dijit.form._FormWidget"); -dojo.require("dijit.form.ValidationTextBox"); -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 `` box, - // automatically copy the first entry displayed in the drop down list to - // the `` 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", "
\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 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), - '$1' - ); // 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, "&").replace(//gm, ">").replace(/"/gm, """); - 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: "
    " - +"
  • " - +"
  • " - +"
", - - // _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 = " "; - } - 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 , 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 - // | - // | ... - // - // 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 ` - var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0]; - args.onItem(item); - }, - - fetchSelectedItem: function(){ - // summary: - // Get the option marked as selected, like `