From 81bea17aefb26859f825b9293c7c99192874806e Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 8 Nov 2011 20:40:44 +0400 Subject: upgrade Dojo to 1.6.1 --- lib/dijit/form/Button.js | 463 ++++++-- lib/dijit/form/CheckBox.js | 262 +++-- lib/dijit/form/ComboBox.js | 1812 +++++++++++++++++++++----------- lib/dijit/form/ComboButton.js | 9 +- lib/dijit/form/CurrencyTextBox.js | 103 +- lib/dijit/form/DateTextBox.js | 34 +- lib/dijit/form/DropDownButton.js | 9 +- lib/dijit/form/FilteringSelect.js | 301 ++++-- lib/dijit/form/Form.js | 231 +++- lib/dijit/form/HorizontalRule.js | 92 +- lib/dijit/form/HorizontalRuleLabels.js | 115 +- lib/dijit/form/HorizontalSlider.js | 526 +++++---- lib/dijit/form/MappedTextBox.js | 9 +- lib/dijit/form/MultiSelect.js | 148 ++- lib/dijit/form/NumberSpinner.js | 89 +- lib/dijit/form/NumberTextBox.js | 370 +++++-- lib/dijit/form/RadioButton.js | 10 +- lib/dijit/form/RangeBoundTextBox.js | 9 +- lib/dijit/form/Select.js | 419 +++++--- lib/dijit/form/SimpleTextarea.js | 144 ++- lib/dijit/form/Slider.js | 13 +- lib/dijit/form/TextBox.js | 616 +++++++---- lib/dijit/form/Textarea.js | 252 +++-- lib/dijit/form/TimeTextBox.js | 81 +- lib/dijit/form/ToggleButton.js | 9 +- lib/dijit/form/ValidationTextBox.js | 675 ++++++++---- lib/dijit/form/VerticalRule.js | 30 +- lib/dijit/form/VerticalRuleLabels.js | 30 +- lib/dijit/form/VerticalSlider.js | 39 +- lib/dijit/form/_DateTimeTextBox.js | 374 ++++--- lib/dijit/form/_FormMixin.js | 601 ++++++++--- lib/dijit/form/_FormSelectWidget.js | 867 +++++++++------ lib/dijit/form/_FormWidget.js | 515 ++++++--- lib/dijit/form/_Spinner.js | 167 ++- lib/dijit/form/nls/da/validate.js | 2 +- lib/dijit/form/nls/kk/ComboBox.js | 1 + lib/dijit/form/nls/kk/Textarea.js | 1 + lib/dijit/form/nls/kk/validate.js | 1 + lib/dijit/form/nls/pl/Textarea.js | 2 +- lib/dijit/form/nls/sl/ComboBox.js | 2 +- lib/dijit/form/nls/sl/Textarea.js | 2 +- lib/dijit/form/nls/sl/validate.js | 2 +- 42 files changed, 6581 insertions(+), 2856 deletions(-) create mode 100644 lib/dijit/form/nls/kk/ComboBox.js create mode 100644 lib/dijit/form/nls/kk/Textarea.js create mode 100644 lib/dijit/form/nls/kk/validate.js (limited to 'lib/dijit/form') diff --git a/lib/dijit/form/Button.js b/lib/dijit/form/Button.js index 741062022..77846776f 100644 --- a/lib/dijit/form/Button.js +++ b/lib/dijit/form/Button.js @@ -1,127 +1,360 @@ /* - 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.Button"]){ -dojo._hasResource["dijit.form.Button"]=true; +if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form.Button"] = true; dojo.provide("dijit.form.Button"); dojo.require("dijit.form._FormWidget"); dojo.require("dijit._Container"); dojo.require("dijit._HasDropDown"); -dojo.declare("dijit.form.Button",dijit.form._FormWidget,{label:"",showLabel:true,iconClass:"",type:"button",baseClass:"dijitButton",templateString:dojo.cache("dijit.form","templates/Button.html","\n"),attributeMap:dojo.delegate(dijit.form._FormWidget.prototype.attributeMap,{value:"valueNode",iconClass:{node:"iconNode",type:"class"}}),_onClick:function(e){ -if(this.disabled){ -return false; -} -this._clicked(); -return this.onClick(e); -},_onButtonClick:function(e){ -if(this._onClick(e)===false){ -e.preventDefault(); -}else{ -if(this.type=="submit"&&!(this.valueNode||this.focusNode).form){ -for(var _1=this.domNode;_1.parentNode;_1=_1.parentNode){ -var _2=dijit.byNode(_1); -if(_2&&typeof _2._onSubmit=="function"){ -_2._onSubmit(e); -break; -} -} -}else{ -if(this.valueNode){ -this.valueNode.click(); -e.preventDefault(); -} -} -} -},_fillContent:function(_3){ -if(_3&&(!this.params||!("label" in this.params))){ -this.set("label",_3.innerHTML); -} -},postCreate:function(){ -dojo.setSelectable(this.focusNode,false); -this.inherited(arguments); -},_setShowLabelAttr:function(_4){ -if(this.containerNode){ -dojo.toggleClass(this.containerNode,"dijitDisplayNone",!_4); -} -this.showLabel=_4; -},onClick:function(e){ -return true; -},_clicked:function(e){ -},setLabel:function(_5){ -dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.","","2.0"); -this.set("label",_5); -},_setLabelAttr:function(_6){ -this.containerNode.innerHTML=this.label=_6; -if(this.showLabel==false&&!this.params.title){ -this.titleNode.title=dojo.trim(this.containerNode.innerText||this.containerNode.textContent||""); -} -}}); -dojo.declare("dijit.form.DropDownButton",[dijit.form.Button,dijit._Container,dijit._HasDropDown],{baseClass:"dijitDropDownButton",templateString:dojo.cache("dijit.form","templates/DropDownButton.html","\n"),_fillContent:function(){ -if(this.srcNodeRef){ -var _7=dojo.query("*",this.srcNodeRef); -dijit.form.DropDownButton.superclass._fillContent.call(this,_7[0]); -this.dropDownContainer=this.srcNodeRef; -} -},startup:function(){ -if(this._started){ -return; -} -if(!this.dropDown){ -var _8=dojo.query("[widgetId]",this.dropDownContainer)[0]; -this.dropDown=dijit.byNode(_8); -delete this.dropDownContainer; -} -dijit.popup.moveOffScreen(this.dropDown.domNode); -this.inherited(arguments); -},isLoaded:function(){ -var _9=this.dropDown; -return (!_9.href||_9.isLoaded); -},loadDropDown:function(){ -var _a=this.dropDown; -if(!_a){ -return; -} -if(!this.isLoaded()){ -var _b=dojo.connect(_a,"onLoad",this,function(){ -dojo.disconnect(_b); -this.openDropDown(); + + +dojo.declare("dijit.form.Button", + dijit.form._FormWidget, + { + // summary: + // Basically the same thing as a normal HTML button, but with special styling. + // description: + // Buttons can display a label, an icon, or both. + // A label should always be specified (through innerHTML) or the label + // attribute. It can be hidden via showLabel=false. + // example: + // | + // + // example: + // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo}); + // | dojo.body().appendChild(button1.domNode); + + // label: HTML String + // Text to display in button. + // If the label is hidden (showLabel=false) then and no title has + // been specified, then label is also set as title attribute of icon. + label: "", + + // showLabel: Boolean + // Set this to true to hide the label text and display only the icon. + // (If showLabel=false then iconClass must be specified.) + // Especially useful for toolbars. + // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon. + // + // The exception case is for computers in high-contrast mode, where the label + // will still be displayed, since the icon doesn't appear. + showLabel: true, + + // iconClass: String + // Class to apply to DOMNode in button to make it display an icon + iconClass: "", + + // type: String + // Defines the type of button. "button", "submit", or "reset". + type: "button", + + baseClass: "dijitButton", + + templateString: dojo.cache("dijit.form", "templates/Button.html", "\n"), + + attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { + value: "valueNode" + }), + + _onClick: function(/*Event*/ e){ + // summary: + // Internal function to handle click actions + if(this.disabled){ + return false; + } + this._clicked(); // widget click actions + return this.onClick(e); // user click actions + }, + + _onButtonClick: function(/*Event*/ e){ + // summary: + // Handler when the user activates the button portion. + if(this._onClick(e) === false){ // returning nothing is same as true + e.preventDefault(); // needed for checkbox + }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled + for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){ + var widget=dijit.byNode(node); + if(widget && typeof widget._onSubmit == "function"){ + widget._onSubmit(e); + break; + } + } + }else if(this.valueNode){ + this.valueNode.click(); + e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click + } + }, + + buildRendering: function(){ + this.inherited(arguments); + dojo.setSelectable(this.focusNode, false); + }, + + _fillContent: function(/*DomNode*/ source){ + // Overrides _Templated._fillContent(). + // If button label is specified as srcNodeRef.innerHTML rather than + // this.params.label, handle it here. + // TODO: remove the method in 2.0, parser will do it all for me + if(source && (!this.params || !("label" in this.params))){ + this.set('label', source.innerHTML); + } + }, + + _setShowLabelAttr: function(val){ + if(this.containerNode){ + dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val); + } + this._set("showLabel", val); + }, + + onClick: function(/*Event*/ e){ + // summary: + // Callback for when button is clicked. + // If type="submit", return true to perform submit, or false to cancel it. + // type: + // callback + return true; // Boolean + }, + + _clicked: function(/*Event*/ e){ + // summary: + // Internal overridable function for when the button is clicked + }, + + setLabel: function(/*String*/ content){ + // summary: + // Deprecated. Use set('label', ...) instead. + dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0"); + this.set("label", content); + }, + + _setLabelAttr: function(/*String*/ content){ + // summary: + // Hook for set('label', ...) to work. + // description: + // Set the label (text) of the button; takes an HTML string. + this._set("label", content); + this.containerNode.innerHTML = content; + if(this.showLabel == false && !this.params.title){ + this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || ''); + } + }, + + _setIconClassAttr: function(/*String*/ val){ + // Custom method so that icon node is hidden when not in use, to avoid excess padding/margin + // appearing around it (even if it's a 0x0 sized node) + + var oldVal = this.iconClass || "dijitNoIcon", + newVal = val || "dijitNoIcon"; + dojo.replaceClass(this.iconNode, newVal, oldVal); + this._set("iconClass", val); + } }); -_a.refresh(); -}else{ -this.openDropDown(); -} -},isFocusable:function(){ -return this.inherited(arguments)&&!this._mouseDown; -}}); -dojo.declare("dijit.form.ComboButton",dijit.form.DropDownButton,{templateString:dojo.cache("dijit.form","templates/ComboButton.html","
\n"),attributeMap:dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap),{id:"",tabIndex:["focusNode","titleNode"],title:"titleNode"}),optionsTitle:"",baseClass:"dijitComboButton",cssStateNodes:{"buttonNode":"dijitButtonNode","titleNode":"dijitButtonContents","_popupStateNode":"dijitDownArrowButton"},_focusedNode:null,_onButtonKeyPress:function(_c){ -if(_c.charOrCode==dojo.keys[this.isLeftToRight()?"RIGHT_ARROW":"LEFT_ARROW"]){ -dijit.focus(this._popupStateNode); -dojo.stopEvent(_c); -} -},_onArrowKeyPress:function(_d){ -if(_d.charOrCode==dojo.keys[this.isLeftToRight()?"LEFT_ARROW":"RIGHT_ARROW"]){ -dijit.focus(this.titleNode); -dojo.stopEvent(_d); -} -},focus:function(_e){ -dijit.focus(_e=="start"?this.titleNode:this._popupStateNode); -}}); -dojo.declare("dijit.form.ToggleButton",dijit.form.Button,{baseClass:"dijitToggleButton",checked:false,attributeMap:dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap),{checked:"focusNode"}),_clicked:function(_f){ -this.set("checked",!this.checked); -},_setCheckedAttr:function(_10,_11){ -this.checked=_10; -dojo.attr(this.focusNode||this.domNode,"checked",_10); -dijit.setWaiState(this.focusNode||this.domNode,"pressed",_10); -this._handleOnChange(_10,_11); -},setChecked:function(_12){ -dojo.deprecated("setChecked("+_12+") is deprecated. Use set('checked',"+_12+") instead.","","2.0"); -this.set("checked",_12); -},reset:function(){ -this._hasBeenBlurred=false; -this.set("checked",this.params.checked||false); -}}); + + +dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], { + // summary: + // A button with a drop down + // + // example: + // | + // + // example: + // | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) }); + // | dojo.body().appendChild(button1); + // + + baseClass : "dijitDropDownButton", + + templateString: dojo.cache("dijit.form", "templates/DropDownButton.html", "\n"), + + _fillContent: function(){ + // Overrides Button._fillContent(). + // + // My inner HTML contains both the button contents and a drop down widget, like + // push me ... + // The first node is assumed to be the button content. The widget is the popup. + + if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef + //FIXME: figure out how to filter out the widget and use all remaining nodes as button + // content, not just nodes[0] + var nodes = dojo.query("*", this.srcNodeRef); + dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]); + + // save pointer to srcNode so we can grab the drop down widget after it's instantiated + this.dropDownContainer = this.srcNodeRef; + } + }, + + startup: function(){ + if(this._started){ return; } + + // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM, + // make it invisible, and store a reference to pass to the popup code. + if(!this.dropDown && this.dropDownContainer){ + var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0]; + this.dropDown = dijit.byNode(dropDownNode); + delete this.dropDownContainer; + } + if(this.dropDown){ + dijit.popup.hide(this.dropDown); + } + + this.inherited(arguments); + }, + + isLoaded: function(){ + // Returns whether or not we are loaded - if our dropdown has an href, + // then we want to check that. + var dropDown = this.dropDown; + return (!!dropDown && (!dropDown.href || dropDown.isLoaded)); + }, + + loadDropDown: function(){ + // Loads our dropdown + var dropDown = this.dropDown; + if(!dropDown){ return; } + if(!this.isLoaded()){ + var handler = dojo.connect(dropDown, "onLoad", this, function(){ + dojo.disconnect(handler); + this.openDropDown(); + }); + dropDown.refresh(); + }else{ + this.openDropDown(); + } + }, + + isFocusable: function(){ + // Overridden so that focus is handled by the _HasDropDown mixin, not by + // the _FormWidget mixin. + return this.inherited(arguments) && !this._mouseDown; + } +}); + +dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, { + // summary: + // A combination button and drop-down button. + // Users can click one side to "press" the button, or click an arrow + // icon to display the drop down. + // + // example: + // | + // + // example: + // | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"}); + // | dojo.body().appendChild(button1.domNode); + // + + templateString: dojo.cache("dijit.form", "templates/ComboButton.html", "
\n"), + + attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), { + id: "", + tabIndex: ["focusNode", "titleNode"], + title: "titleNode" + }), + + // optionsTitle: String + // Text that describes the options menu (accessibility) + optionsTitle: "", + + baseClass: "dijitComboButton", + + // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on + // mouse action over specified node + cssStateNodes: { + "buttonNode": "dijitButtonNode", + "titleNode": "dijitButtonContents", + "_popupStateNode": "dijitDownArrowButton" + }, + + _focusedNode: null, + + _onButtonKeyPress: function(/*Event*/ evt){ + // summary: + // Handler for right arrow key when focus is on left part of button + if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){ + dijit.focus(this._popupStateNode); + dojo.stopEvent(evt); + } + }, + + _onArrowKeyPress: function(/*Event*/ evt){ + // summary: + // Handler for left arrow key when focus is on right part of button + if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){ + dijit.focus(this.titleNode); + dojo.stopEvent(evt); + } + }, + + focus: function(/*String*/ position){ + // summary: + // Focuses this widget to according to position, if specified, + // otherwise on arrow node + // position: + // "start" or "end" + if(!this.disabled){ + dijit.focus(position == "start" ? this.titleNode : this._popupStateNode); + } + } +}); + +dojo.declare("dijit.form.ToggleButton", dijit.form.Button, { + // summary: + // A button that can be in two states (checked or not). + // Can be base class for things like tabs or checkbox or radio buttons + + baseClass: "dijitToggleButton", + + // checked: Boolean + // Corresponds to the native HTML element's attribute. + // In markup, specified as "checked='checked'" or just "checked". + // True if the button is depressed, or the checkbox is checked, + // or the radio button is selected, etc. + checked: false, + + attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), { + checked:"focusNode" + }), + + _clicked: function(/*Event*/ evt){ + this.set('checked', !this.checked); + }, + + _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){ + this._set("checked", value); + dojo.attr(this.focusNode || this.domNode, "checked", value); + dijit.setWaiState(this.focusNode || this.domNode, "pressed", value); + this._handleOnChange(value, priorityChange); + }, + + setChecked: function(/*Boolean*/ checked){ + // summary: + // Deprecated. Use set('checked', true/false) instead. + dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0"); + this.set('checked', checked); + }, + + reset: function(){ + // summary: + // Reset the widget's value to what it was at initialization time + + this._hasBeenBlurred = false; + + // set checked state to original setting + this.set('checked', this.params.checked || false); + } +}); + } diff --git a/lib/dijit/form/CheckBox.js b/lib/dijit/form/CheckBox.js index ea5cdd170..939f6e8bb 100644 --- a/lib/dijit/form/CheckBox.js +++ b/lib/dijit/form/CheckBox.js @@ -1,76 +1,204 @@ /* - 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.CheckBox"]){ -dojo._hasResource["dijit.form.CheckBox"]=true; +if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form.CheckBox"] = true; dojo.provide("dijit.form.CheckBox"); dojo.require("dijit.form.ToggleButton"); -dojo.declare("dijit.form.CheckBox",dijit.form.ToggleButton,{templateString:dojo.cache("dijit.form","templates/CheckBox.html","
\n"),baseClass:"dijitCheckBox",type:"checkbox",value:"on",readOnly:false,attributeMap:dojo.delegate(dijit.form._FormWidget.prototype.attributeMap,{readOnly:"focusNode"}),_setReadOnlyAttr:function(_1){ -this.readOnly=_1; -dojo.attr(this.focusNode,"readOnly",_1); -dijit.setWaiState(this.focusNode,"readonly",_1); -},_setValueAttr:function(_2,_3){ -if(typeof _2=="string"){ -this.value=_2; -dojo.attr(this.focusNode,"value",_2); -_2=true; -} -if(this._created){ -this.set("checked",_2,_3); -} -},_getValueAttr:function(){ -return (this.checked?this.value:false); -},_setLabelAttr:undefined,postMixInProperties:function(){ -if(this.value==""){ -this.value="on"; -} -this.checkedAttrSetting=this.checked?"checked":""; -this.inherited(arguments); -},_fillContent:function(_4){ -},reset:function(){ -this._hasBeenBlurred=false; -this.set("checked",this.params.checked||false); -this.value=this.params.value||"on"; -dojo.attr(this.focusNode,"value",this.value); -},_onFocus:function(){ -if(this.id){ -dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel"); -} -this.inherited(arguments); -},_onBlur:function(){ -if(this.id){ -dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel"); -} -this.inherited(arguments); -},_onClick:function(e){ -if(this.readOnly){ -return false; -} -return this.inherited(arguments); -}}); -dojo.declare("dijit.form.RadioButton",dijit.form.CheckBox,{type:"radio",baseClass:"dijitRadio",_setCheckedAttr:function(_5){ -this.inherited(arguments); -if(!this._created){ -return; -} -if(_5){ -var _6=this; -dojo.query("INPUT[type=radio]",this.focusNode.form||dojo.doc).forEach(function(_7){ -if(_7.name==_6.name&&_7!=_6.focusNode&&_7.form==_6.focusNode.form){ -var _8=dijit.getEnclosingWidget(_7); -if(_8&&_8.checked){ -_8.set("checked",false); -} -} -}); -} -},_clicked:function(e){ -if(!this.checked){ -this.set("checked",true); -} -}}); + + +dojo.declare( + "dijit.form.CheckBox", + dijit.form.ToggleButton, + { + // summary: + // Same as an HTML checkbox, but with fancy styling. + // + // description: + // User interacts with real html inputs. + // On onclick (which occurs by mouse click, space-bar, or + // using the arrow keys to switch the selected radio button), + // we update the state of the checkbox/radio. + // + // There are two modes: + // 1. High contrast mode + // 2. Normal mode + // + // In case 1, the regular html inputs are shown and used by the user. + // In case 2, the regular html inputs are invisible but still used by + // the user. They are turned quasi-invisible and overlay the background-image. + + templateString: dojo.cache("dijit.form", "templates/CheckBox.html", "
\n"), + + baseClass: "dijitCheckBox", + + // type: [private] String + // type attribute on node. + // Overrides `dijit.form.Button.type`. Users should not change this value. + type: "checkbox", + + // value: String + // As an initialization parameter, equivalent to value field on normal checkbox + // (if checked, the value is passed as the value when form is submitted). + // + // However, get('value') will return either the string or false depending on + // whether or not the checkbox is checked. + // + // set('value', string) will check the checkbox and change the value to the + // specified string + // + // set('value', boolean) will change the checked state. + value: "on", + + // readOnly: Boolean + // Should this widget respond to user input? + // In markup, this is specified as "readOnly". + // Similar to disabled except readOnly form values are submitted. + readOnly: false, + + // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap + // instead of ToggleButton as the icon mapping has no meaning for a CheckBox + attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { + readOnly: "focusNode" + }), + + _setReadOnlyAttr: function(/*Boolean*/ value){ + this._set("readOnly", value); + dojo.attr(this.focusNode, 'readOnly', value); + dijit.setWaiState(this.focusNode, "readonly", value); + }, + + _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){ + // summary: + // Handler for value= attribute to constructor, and also calls to + // set('value', val). + // description: + // During initialization, just saves as attribute to the . + // + // After initialization, + // when passed a boolean, controls whether or not the CheckBox is checked. + // If passed a string, changes the value attribute of the CheckBox (the one + // specified as "value" when the CheckBox was constructed (ex: ) + if(typeof newValue == "string"){ + this._set("value", newValue); + dojo.attr(this.focusNode, 'value', newValue); + newValue = true; + } + if(this._created){ + this.set('checked', newValue, priorityChange); + } + }, + _getValueAttr: function(){ + // summary: + // Hook so get('value') works. + // description: + // If the CheckBox is checked, returns the value attribute. + // Otherwise returns false. + return (this.checked ? this.value : false); + }, + + // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode. + // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer + _setLabelAttr: undefined, + + postMixInProperties: function(){ + if(this.value == ""){ + this.value = "on"; + } + + // Need to set initial checked state as part of template, so that form submit works. + // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached + // to , see #8666 + this.checkedAttrSetting = this.checked ? "checked" : ""; + + this.inherited(arguments); + }, + + _fillContent: function(/*DomNode*/ source){ + // Override Button::_fillContent() since it doesn't make sense for CheckBox, + // since CheckBox doesn't even have a container + }, + + reset: function(){ + // Override ToggleButton.reset() + + this._hasBeenBlurred = false; + + this.set('checked', this.params.checked || false); + + // Handle unlikely event that the value attribute has changed + this._set("value", this.params.value || "on"); + dojo.attr(this.focusNode, 'value', this.value); + }, + + _onFocus: function(){ + if(this.id){ + dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel"); + } + this.inherited(arguments); + }, + + _onBlur: function(){ + if(this.id){ + dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel"); + } + this.inherited(arguments); + }, + + _onClick: function(/*Event*/ e){ + // summary: + // Internal function to handle click actions - need to check + // readOnly, since button no longer does that check. + if(this.readOnly){ + dojo.stopEvent(e); + return false; + } + return this.inherited(arguments); + } + } +); + +dojo.declare( + "dijit.form.RadioButton", + dijit.form.CheckBox, + { + // summary: + // Same as an HTML radio, but with fancy styling. + + type: "radio", + baseClass: "dijitRadio", + + _setCheckedAttr: function(/*Boolean*/ value){ + // If I am being checked then have to deselect currently checked radio button + this.inherited(arguments); + if(!this._created){ return; } + if(value){ + var _this = this; + // search for radio buttons with the same name that need to be unchecked + dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name + function(inputNode){ + if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){ + var widget = dijit.getEnclosingWidget(inputNode); + if(widget && widget.checked){ + widget.set('checked', false); + } + } + } + ); + } + }, + + _clicked: function(/*Event*/ e){ + if(!this.checked){ + this.set('checked', true); + } + } + } +); + } 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","
\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$2"); -return ret; -},_escapeHtml:function(str){ -str=String(str).replace(/&/gm,"&").replace(//gm,">").replace(/"/gm,"""); -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:"
    "+"
  • "+"
  • "+"
",_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=" "; -} -_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=""; -_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 `` 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 `
', + + // count: Integer + // Number of hash marks to generate + count: 3, + + // container: String + // For HorizontalSlider, this is either "topDecoration" or "bottomDecoration", + // and indicates whether this rule goes above or below the slider. + container: "containerNode", + + // ruleStyle: String + // CSS style to apply to individual hash marks + ruleStyle: "", + + _positionPrefix: '
', + + _genHTML: function(pos, ndx){ + return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix; + }, + + // _isHorizontal: [protected extension] Boolean + // VerticalRule will override this... + _isHorizontal: true, + + buildRendering: function(){ + this.inherited(arguments); + + var innerHTML; + if(this.count == 1){ + innerHTML = this._genHTML(50, 0); + }else{ + var i; + var interval = 100 / (this.count-1); + if(!this._isHorizontal || this.isLeftToRight()){ + innerHTML = this._genHTML(0, 0); + for(i=1; i < this.count-1; i++){ + innerHTML += this._genHTML(interval*i, i); + } + innerHTML += this._genHTML(100, this.count-1); + }else{ + innerHTML = this._genHTML(100, 0); + for(i=1; i < this.count-1; i++){ + innerHTML += this._genHTML(100-interval*i, i); + } + innerHTML += this._genHTML(0, this.count-1); + } + } + this.domNode.innerHTML = innerHTML; + } +}); + } diff --git a/lib/dijit/form/HorizontalRuleLabels.js b/lib/dijit/form/HorizontalRuleLabels.js index 6cf6742c5..d923e8081 100644 --- a/lib/dijit/form/HorizontalRuleLabels.js +++ b/lib/dijit/form/HorizontalRuleLabels.js @@ -1,38 +1,97 @@ /* - 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.HorizontalRuleLabels"]){ -dojo._hasResource["dijit.form.HorizontalRuleLabels"]=true; +if(!dojo._hasResource["dijit.form.HorizontalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form.HorizontalRuleLabels"] = true; dojo.provide("dijit.form.HorizontalRuleLabels"); dojo.require("dijit.form.HorizontalRule"); -dojo.declare("dijit.form.HorizontalRuleLabels",dijit.form.HorizontalRule,{templateString:"
",labelStyle:"",labels:[],numericMargin:0,minimum:0,maximum:1,constraints:{pattern:"#%"},_positionPrefix:"
",_suffix:"
",_calcPosition:function(_1){ -return _1; -},_genHTML:function(_2,_3){ -return this._positionPrefix+this._calcPosition(_2)+this._positionSuffix+this.labelStyle+this._labelPrefix+this.labels[_3]+this._suffix; -},getLabels:function(){ -var _4=this.labels; -if(!_4.length){ -_4=dojo.query("> li",this.srcNodeRef).map(function(_5){ -return String(_5.innerHTML); + + +dojo.declare("dijit.form.HorizontalRuleLabels", dijit.form.HorizontalRule, +{ + // summary: + // Labels for `dijit.form.HorizontalSlider` + + templateString: '
', + + // labelStyle: String + // CSS style to apply to individual text labels + labelStyle: "", + + // labels: String[]? + // Array of text labels to render - evenly spaced from left-to-right or bottom-to-top. + // Alternately, minimum and maximum can be specified, to get numeric labels. + labels: [], + + // numericMargin: Integer + // Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified + numericMargin: 0, + + // numericMinimum: Integer + // Leftmost label value for generated numeric labels when labels[] are not specified + minimum: 0, + + // numericMaximum: Integer + // Rightmost label value for generated numeric labels when labels[] are not specified + maximum: 1, + + // constraints: Object + // pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified + constraints: {pattern:"#%"}, + + _positionPrefix: '
', + _suffix: '
', + + _calcPosition: function(pos){ + // summary: + // Returns the value to be used in HTML for the label as part of the left: attribute + // tags: + // protected extension + return pos; + }, + + _genHTML: function(pos, ndx){ + return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix; + }, + + getLabels: function(){ + // summary: + // Overridable function to return array of labels to use for this slider. + // Can specify a getLabels() method instead of a labels[] array, or min/max attributes. + // tags: + // protected extension + + // if the labels array was not specified directly, then see if
  • children were + var labels = this.labels; + if(!labels.length){ + // for markup creation, labels are specified as child elements + labels = dojo.query("> li", this.srcNodeRef).map(function(node){ + return String(node.innerHTML); + }); + } + this.srcNodeRef.innerHTML = ''; + // if the labels were not specified directly and not as
  • children, then calculate numeric labels + if(!labels.length && this.count > 1){ + var start = this.minimum; + var inc = (this.maximum - start) / (this.count-1); + for(var i=0; i < this.count; i++){ + labels.push((i < this.numericMargin || i >= (this.count-this.numericMargin)) ? '' : dojo.number.format(start, this.constraints)); + start += inc; + } + } + return labels; + }, + + postMixInProperties: function(){ + this.inherited(arguments); + this.labels = this.getLabels(); + this.count = this.labels.length; + } }); -} -this.srcNodeRef.innerHTML=""; -if(!_4.length&&this.count>1){ -var _6=this.minimum; -var _7=(this.maximum-_6)/(this.count-1); -for(var i=0;i=(this.count-this.numericMargin))?"":dojo.number.format(_6,this.constraints)); -_6+=_7; -} -} -return _4; -},postMixInProperties:function(){ -this.inherited(arguments); -this.labels=this.getLabels(); -this.count=this.labels.length; -}}); + } diff --git a/lib/dijit/form/HorizontalSlider.js b/lib/dijit/form/HorizontalSlider.js index a0cb8cf2b..93dc4a0c2 100644 --- a/lib/dijit/form/HorizontalSlider.js +++ b/lib/dijit/form/HorizontalSlider.js @@ -1,209 +1,343 @@ /* - 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.HorizontalSlider"]){ -dojo._hasResource["dijit.form.HorizontalSlider"]=true; +if(!dojo._hasResource["dijit.form.HorizontalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form.HorizontalSlider"] = true; dojo.provide("dijit.form.HorizontalSlider"); dojo.require("dijit.form._FormWidget"); dojo.require("dijit._Container"); dojo.require("dojo.dnd.move"); dojo.require("dijit.form.Button"); dojo.require("dojo.number"); -dojo.require("dojo._base.fx"); -dojo.declare("dijit.form.HorizontalSlider",[dijit.form._FormValueWidget,dijit._Container],{templateString:dojo.cache("dijit.form","templates/HorizontalSlider.html","
    -
    +
    \n"),value:0,showButtons:true,minimum:0,maximum:100,discreteValues:Infinity,pageIncrement:2,clickSelect:true,slideDuration:dijit.defaultDuration,widgetsInTemplate:true,attributeMap:dojo.delegate(dijit.form._FormWidget.prototype.attributeMap,{id:""}),baseClass:"dijitSlider",cssStateNodes:{incrementButton:"dijitSliderIncrementButton",decrementButton:"dijitSliderDecrementButton",focusNode:"dijitSliderThumb"},_mousePixelCoord:"pageX",_pixelCount:"w",_startingPixelCoord:"x",_startingPixelCount:"l",_handleOffsetCoord:"left",_progressPixelSize:"width",_onKeyUp:function(e){ -if(this.disabled||this.readOnly||e.altKey||e.ctrlKey||e.metaKey){ -return; -} -this._setValueAttr(this.value,true); -},_onKeyPress:function(e){ -if(this.disabled||this.readOnly||e.altKey||e.ctrlKey||e.metaKey){ -return; -} -switch(e.charOrCode){ -case dojo.keys.HOME: -this._setValueAttr(this.minimum,false); -break; -case dojo.keys.END: -this._setValueAttr(this.maximum,false); -break; -case ((this._descending||this.isLeftToRight())?dojo.keys.RIGHT_ARROW:dojo.keys.LEFT_ARROW): -case (this._descending===false?dojo.keys.DOWN_ARROW:dojo.keys.UP_ARROW): -case (this._descending===false?dojo.keys.PAGE_DOWN:dojo.keys.PAGE_UP): -this.increment(e); -break; -case ((this._descending||this.isLeftToRight())?dojo.keys.LEFT_ARROW:dojo.keys.RIGHT_ARROW): -case (this._descending===false?dojo.keys.UP_ARROW:dojo.keys.DOWN_ARROW): -case (this._descending===false?dojo.keys.PAGE_UP:dojo.keys.PAGE_DOWN): -this.decrement(e); -break; -default: -return; -} -dojo.stopEvent(e); -},_onHandleClick:function(e){ -if(this.disabled||this.readOnly){ -return; -} -if(!dojo.isIE){ -dijit.focus(this.sliderHandle); -} -dojo.stopEvent(e); -},_isReversed:function(){ -return !this.isLeftToRight(); -},_onBarClick:function(e){ -if(this.disabled||this.readOnly||!this.clickSelect){ -return; -} -dijit.focus(this.sliderHandle); -dojo.stopEvent(e); -var _1=dojo.position(this.sliderBarContainer,true); -var _2=e[this._mousePixelCoord]-_1[this._startingPixelCoord]; -this._setPixelValue(this._isReversed()?(_1[this._pixelCount]-_2):_2,_1[this._pixelCount],true); -this._movable.onMouseDown(e); -},_setPixelValue:function(_3,_4,_5){ -if(this.disabled||this.readOnly){ -return; -} -_3=_3<0?0:_4<_3?_4:_3; -var _6=this.discreteValues; -if(_6<=1||_6==Infinity){ -_6=_4; -} -_6--; -var _7=_4/_6; -var _8=Math.round(_3/_7); -this._setValueAttr((this.maximum-this.minimum)*_8/_6+this.minimum,_5); -},_setValueAttr:function(_9,_a){ -this.valueNode.value=this.value=_9; -dijit.setWaiState(this.focusNode,"valuenow",_9); -this.inherited(arguments); -var _b=(_9-this.minimum)/(this.maximum-this.minimum); -var _c=(this._descending===false)?this.remainingBar:this.progressBar; -var _d=(this._descending===false)?this.progressBar:this.remainingBar; -if(this._inProgressAnim&&this._inProgressAnim.status!="stopped"){ -this._inProgressAnim.stop(true); -} -if(_a&&this.slideDuration>0&&_c.style[this._progressPixelSize]){ -var _e=this; -var _f={}; -var _10=parseFloat(_c.style[this._progressPixelSize]); -var _11=this.slideDuration*(_b-_10/100); -if(_11==0){ -return; -} -if(_11<0){ -_11=0-_11; -} -_f[this._progressPixelSize]={start:_10,end:_b*100,units:"%"}; -this._inProgressAnim=dojo.animateProperty({node:_c,duration:_11,onAnimate:function(v){ -_d.style[_e._progressPixelSize]=(100-parseFloat(v[_e._progressPixelSize]))+"%"; -},onEnd:function(){ -delete _e._inProgressAnim; -},properties:_f}); -this._inProgressAnim.play(); -}else{ -_c.style[this._progressPixelSize]=(_b*100)+"%"; -_d.style[this._progressPixelSize]=((1-_b)*100)+"%"; -} -},_bumpValue:function(_12,_13){ -if(this.disabled||this.readOnly){ -return; -} -var s=dojo.getComputedStyle(this.sliderBarContainer); -var c=dojo._getContentBox(this.sliderBarContainer,s); -var _14=this.discreteValues; -if(_14<=1||_14==Infinity){ -_14=c[this._pixelCount]; -} -_14--; -var _15=(this.value-this.minimum)*_14/(this.maximum-this.minimum)+_12; -if(_15<0){ -_15=0; -} -if(_15>_14){ -_15=_14; -} -_15=_15*(this.maximum-this.minimum)/_14+this.minimum; -this._setValueAttr(_15,_13); -},_onClkBumper:function(val){ -if(this.disabled||this.readOnly||!this.clickSelect){ -return; -} -this._setValueAttr(val,true); -},_onClkIncBumper:function(){ -this._onClkBumper(this._descending===false?this.minimum:this.maximum); -},_onClkDecBumper:function(){ -this._onClkBumper(this._descending===false?this.maximum:this.minimum); -},decrement:function(e){ -this._bumpValue(e.charOrCode==dojo.keys.PAGE_DOWN?-this.pageIncrement:-1); -},increment:function(e){ -this._bumpValue(e.charOrCode==dojo.keys.PAGE_UP?this.pageIncrement:1); -},_mouseWheeled:function(evt){ -dojo.stopEvent(evt); -var _16=!dojo.isMozilla; -var _17=evt[(_16?"wheelDelta":"detail")]*(_16?1:-1); -this._bumpValue(_17<0?-1:1,true); -},startup:function(){ -if(this._started){ -return; -} -dojo.forEach(this.getChildren(),function(_18){ -if(this[_18.container]!=this.containerNode){ -this[_18.container].appendChild(_18.domNode); -} -},this); -this.inherited(arguments); -},_typematicCallback:function(_19,_1a,e){ -if(_19==-1){ -this._setValueAttr(this.value,true); -}else{ -this[(_1a==(this._descending?this.incrementButton:this.decrementButton))?"decrement":"increment"](e); -} -},postCreate:function(){ -if(this.showButtons){ -this.incrementButton.style.display=""; -this.decrementButton.style.display=""; -this._connects.push(dijit.typematic.addMouseListener(this.decrementButton,this,"_typematicCallback",25,500)); -this._connects.push(dijit.typematic.addMouseListener(this.incrementButton,this,"_typematicCallback",25,500)); -} -this.connect(this.domNode,!dojo.isMozilla?"onmousewheel":"DOMMouseScroll","_mouseWheeled"); -var _1b=dojo.declare(dijit.form._SliderMover,{widget:this}); -this._movable=new dojo.dnd.Moveable(this.sliderHandle,{mover:_1b}); -var _1c=dojo.query("label[for=\""+this.id+"\"]"); -if(_1c.length){ -_1c[0].id=(this.id+"_label"); -dijit.setWaiState(this.focusNode,"labelledby",_1c[0].id); -} -dijit.setWaiState(this.focusNode,"valuemin",this.minimum); -dijit.setWaiState(this.focusNode,"valuemax",this.maximum); -this.inherited(arguments); -this._layoutHackIE7(); -},destroy:function(){ -this._movable.destroy(); -if(this._inProgressAnim&&this._inProgressAnim.status!="stopped"){ -this._inProgressAnim.stop(true); -} -this._supportingWidgets=dijit.findWidgets(this.domNode); -this.inherited(arguments); -}}); -dojo.declare("dijit.form._SliderMover",dojo.dnd.Mover,{onMouseMove:function(e){ -var _1d=this.widget; -var _1e=_1d._abspos; -if(!_1e){ -_1e=_1d._abspos=dojo.position(_1d.sliderBarContainer,true); -_1d._setPixelValue_=dojo.hitch(_1d,"_setPixelValue"); -_1d._isReversed_=_1d._isReversed(); -} -var _1f=e[_1d._mousePixelCoord]-_1e[_1d._startingPixelCoord]; -_1d._setPixelValue_(_1d._isReversed_?(_1e[_1d._pixelCount]-_1f):_1f,_1e[_1d._pixelCount],false); -},destroy:function(e){ -dojo.dnd.Mover.prototype.destroy.apply(this,arguments); -var _20=this.widget; -_20._abspos=null; -_20._setValueAttr(_20.value,true); -}}); + + +dojo.declare( + "dijit.form.HorizontalSlider", + [dijit.form._FormValueWidget, dijit._Container], +{ + // summary: + // A form widget that allows one to select a value with a horizontally draggable handle + + templateString: dojo.cache("dijit.form", "templates/HorizontalSlider.html", "
    -
    +
    \n"), + + // Overrides FormValueWidget.value to indicate numeric value + value: 0, + + // showButtons: [const] Boolean + // Show increment/decrement buttons at the ends of the slider? + showButtons: true, + + // minimum:: [const] Integer + // The minimum value the slider can be set to. + minimum: 0, + + // maximum: [const] Integer + // The maximum value the slider can be set to. + maximum: 100, + + // discreteValues: Integer + // If specified, indicates that the slider handle has only 'discreteValues' possible positions, + // and that after dragging the handle, it will snap to the nearest possible position. + // Thus, the slider has only 'discreteValues' possible values. + // + // For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has + // three possible positions, representing values 10, 20, or 30. + // + // If discreteValues is not specified or if it's value is higher than the number of pixels + // in the slider bar, then the slider handle can be moved freely, and the slider's value will be + // computed/reported based on pixel position (in this case it will likely be fractional, + // such as 123.456789). + discreteValues: Infinity, + + // pageIncrement: Integer + // If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions) + // that the slider handle is moved via pageup/pagedown keys. + // If discreteValues is not specified, it indicates the number of pixels. + pageIncrement: 2, + + // clickSelect: Boolean + // If clicking the slider bar changes the value or not + clickSelect: true, + + // slideDuration: Number + // The time in ms to take to animate the slider handle from 0% to 100%, + // when clicking the slider bar to make the handle move. + slideDuration: dijit.defaultDuration, + + // Flag to _Templated (TODO: why is this here? I see no widgets in the template.) + widgetsInTemplate: true, + + attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { + id: "" + }), + + baseClass: "dijitSlider", + + // Apply CSS classes to up/down arrows and handle per mouse state + cssStateNodes: { + incrementButton: "dijitSliderIncrementButton", + decrementButton: "dijitSliderDecrementButton", + focusNode: "dijitSliderThumb" + }, + + _mousePixelCoord: "pageX", + _pixelCount: "w", + _startingPixelCoord: "x", + _startingPixelCount: "l", + _handleOffsetCoord: "left", + _progressPixelSize: "width", + + _onKeyUp: function(/*Event*/ e){ + if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; } + this._setValueAttr(this.value, true); + }, + + _onKeyPress: function(/*Event*/ e){ + if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; } + switch(e.charOrCode){ + case dojo.keys.HOME: + this._setValueAttr(this.minimum, false); + break; + case dojo.keys.END: + this._setValueAttr(this.maximum, false); + break; + // this._descending === false: if ascending vertical (min on top) + // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical + case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW): + case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW): + case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP): + this.increment(e); + break; + case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW): + case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW): + case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN): + this.decrement(e); + break; + default: + return; + } + dojo.stopEvent(e); + }, + + _onHandleClick: function(e){ + if(this.disabled || this.readOnly){ return; } + if(!dojo.isIE){ + // make sure you get focus when dragging the handle + // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus) + dijit.focus(this.sliderHandle); + } + dojo.stopEvent(e); + }, + + _isReversed: function(){ + // summary: + // Returns true if direction is from right to left + // tags: + // protected extension + return !this.isLeftToRight(); + }, + + _onBarClick: function(e){ + if(this.disabled || this.readOnly || !this.clickSelect){ return; } + dijit.focus(this.sliderHandle); + dojo.stopEvent(e); + var abspos = dojo.position(this.sliderBarContainer, true); + var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord]; + this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true); + this._movable.onMouseDown(e); + }, + + _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean?*/ priorityChange){ + if(this.disabled || this.readOnly){ return; } + pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue; + var count = this.discreteValues; + if(count <= 1 || count == Infinity){ count = maxPixels; } + count--; + var pixelsPerValue = maxPixels / count; + var wholeIncrements = Math.round(pixelValue / pixelsPerValue); + this._setValueAttr((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange); + }, + + _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){ + // summary: + // Hook so set('value', value) works. + this._set("value", value); + this.valueNode.value = value; + dijit.setWaiState(this.focusNode, "valuenow", value); + this.inherited(arguments); + var percent = (value - this.minimum) / (this.maximum - this.minimum); + var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar; + var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar; + if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){ + this._inProgressAnim.stop(true); + } + if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){ + // animate the slider + var _this = this; + var props = {}; + var start = parseFloat(progressBar.style[this._progressPixelSize]); + var duration = this.slideDuration * (percent-start/100); + if(duration == 0){ return; } + if(duration < 0){ duration = 0 - duration; } + props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" }; + this._inProgressAnim = dojo.animateProperty({ node: progressBar, duration: duration, + onAnimate: function(v){ remainingBar.style[_this._progressPixelSize] = (100-parseFloat(v[_this._progressPixelSize])) + "%"; }, + onEnd: function(){ delete _this._inProgressAnim; }, + properties: props + }) + this._inProgressAnim.play(); + }else{ + progressBar.style[this._progressPixelSize] = (percent*100) + "%"; + remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%"; + } + }, + + _bumpValue: function(signedChange, /*Boolean?*/ priorityChange){ + if(this.disabled || this.readOnly){ return; } + var s = dojo.getComputedStyle(this.sliderBarContainer); + var c = dojo._getContentBox(this.sliderBarContainer, s); + var count = this.discreteValues; + if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; } + count--; + var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange; + if(value < 0){ value = 0; } + if(value > count){ value = count; } + value = value * (this.maximum - this.minimum) / count + this.minimum; + this._setValueAttr(value, priorityChange); + }, + + _onClkBumper: function(val){ + if(this.disabled || this.readOnly || !this.clickSelect){ return; } + this._setValueAttr(val, true); + }, + + _onClkIncBumper: function(){ + this._onClkBumper(this._descending === false ? this.minimum : this.maximum); + }, + + _onClkDecBumper: function(){ + this._onClkBumper(this._descending === false ? this.maximum : this.minimum); + }, + + decrement: function(/*Event*/ e){ + // summary: + // Decrement slider + // tags: + // private + this._bumpValue(e.charOrCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1); + }, + + increment: function(/*Event*/ e){ + // summary: + // Increment slider + // tags: + // private + this._bumpValue(e.charOrCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1); + }, + + _mouseWheeled: function(/*Event*/ evt){ + // summary: + // Event handler for mousewheel where supported + dojo.stopEvent(evt); + var janky = !dojo.isMozilla; + var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1); + this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement + }, + + startup: function(){ + if(this._started){ return; } + + dojo.forEach(this.getChildren(), function(child){ + if(this[child.container] != this.containerNode){ + this[child.container].appendChild(child.domNode); + } + }, this); + + this.inherited(arguments); + }, + + _typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){ + if(count == -1){ + this._setValueAttr(this.value, true); + }else{ + this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e); + } + }, + + buildRendering: function(){ + this.inherited(arguments); + if(this.showButtons){ + this.incrementButton.style.display=""; + this.decrementButton.style.display=""; + } + + // find any associated label element and add to slider focusnode. + var label = dojo.query('label[for="'+this.id+'"]'); + if(label.length){ + label[0].id = (this.id+"_label"); + dijit.setWaiState(this.focusNode, "labelledby", label[0].id); + } + + dijit.setWaiState(this.focusNode, "valuemin", this.minimum); + dijit.setWaiState(this.focusNode, "valuemax", this.maximum); + }, + + postCreate: function(){ + this.inherited(arguments); + + if(this.showButtons){ + this._connects.push(dijit.typematic.addMouseListener( + this.decrementButton, this, "_typematicCallback", 25, 500)); + this._connects.push(dijit.typematic.addMouseListener( + this.incrementButton, this, "_typematicCallback", 25, 500)); + } + this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled"); + + // define a custom constructor for a SliderMover that points back to me + var mover = dojo.declare(dijit.form._SliderMover, { + widget: this + }); + this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover}); + + this._layoutHackIE7(); + }, + + destroy: function(){ + this._movable.destroy(); + if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){ + this._inProgressAnim.stop(true); + } + this._supportingWidgets = dijit.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels) + this.inherited(arguments); + } +}); + +dojo.declare("dijit.form._SliderMover", + dojo.dnd.Mover, +{ + onMouseMove: function(e){ + var widget = this.widget; + var abspos = widget._abspos; + if(!abspos){ + abspos = widget._abspos = dojo.position(widget.sliderBarContainer, true); + widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue"); + widget._isReversed_ = widget._isReversed(); + } + var coordEvent = e.touches ? e.touches[0] : e, // if multitouch take first touch for coords + pixelValue = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord]; + widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false); + }, + + destroy: function(e){ + dojo.dnd.Mover.prototype.destroy.apply(this, arguments); + var widget = this.widget; + widget._abspos = null; + widget._setValueAttr(widget.value, true); + } +}); + } diff --git a/lib/dijit/form/MappedTextBox.js b/lib/dijit/form/MappedTextBox.js index e7dd213c0..ebdaaf15b 100644 --- a/lib/dijit/form/MappedTextBox.js +++ b/lib/dijit/form/MappedTextBox.js @@ -1,12 +1,15 @@ /* - 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.MappedTextBox"]){ -dojo._hasResource["dijit.form.MappedTextBox"]=true; +if(!dojo._hasResource["dijit.form.MappedTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form.MappedTextBox"] = true; dojo.provide("dijit.form.MappedTextBox"); dojo.require("dijit.form.ValidationTextBox"); + + + } diff --git a/lib/dijit/form/MultiSelect.js b/lib/dijit/form/MultiSelect.js index 8aacb4148..12f3b40e7 100644 --- a/lib/dijit/form/MultiSelect.js +++ b/lib/dijit/form/MultiSelect.js @@ -1,49 +1,119 @@ /* - 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.MultiSelect"]){ -dojo._hasResource["dijit.form.MultiSelect"]=true; +if(!dojo._hasResource["dijit.form.MultiSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form.MultiSelect"] = true; dojo.provide("dijit.form.MultiSelect"); dojo.require("dijit.form._FormWidget"); -dojo.declare("dijit.form.MultiSelect",dijit.form._FormValueWidget,{size:7,templateString:"",attributeMap:dojo.delegate(dijit.form._FormWidget.prototype.attributeMap,{size:"focusNode"}),reset:function(){ -this._hasBeenBlurred=false; -this._setValueAttr(this._resetValue,true); -},addSelected:function(_1){ -_1.getSelected().forEach(function(n){ -this.containerNode.appendChild(n); -this.domNode.scrollTop=this.domNode.offsetHeight; -var _2=_1.domNode.scrollTop; -_1.domNode.scrollTop=0; -_1.domNode.scrollTop=_2; -},this); -},getSelected:function(){ -return dojo.query("option",this.containerNode).filter(function(n){ -return n.selected; -}); -},_getValueAttr:function(){ -return this.getSelected().map(function(n){ -return n.value; -}); -},multiple:true,_setValueAttr:function(_3){ -dojo.query("option",this.containerNode).forEach(function(n){ -n.selected=(dojo.indexOf(_3,n.value)!=-1); -}); -},invertSelection:function(_4){ -dojo.query("option",this.containerNode).forEach(function(n){ -n.selected=!n.selected; + + +dojo.declare("dijit.form.MultiSelect", dijit.form._FormValueWidget, { + // summary: + // Widget version of a ", + + attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { + size: "focusNode" + }), + + reset: function(){ + // summary: + // Reset the widget's value to what it was at initialization time + + // TODO: once we inherit from FormValueWidget this won't be needed + this._hasBeenBlurred = false; + this._setValueAttr(this._resetValue, true); + }, + + addSelected: function(/*dijit.form.MultiSelect*/ select){ + // summary: + // Move the selected nodes of a passed Select widget + // instance to this Select widget. + // + // example: + // | // move all the selected values from "bar" to "foo" + // | dijit.byId("foo").addSelected(dijit.byId("bar")); + + select.getSelected().forEach(function(n){ + this.containerNode.appendChild(n); + // scroll to bottom to see item + // cannot use scrollIntoView since