1 define("dijit/form/MultiSelect", [
2 "dojo/_base/array", // array.indexOf, array.map
3 "dojo/_base/declare", // declare
4 "dojo/dom-geometry", // domGeometry.setMarginBox
7 ], function(array, declare, domGeometry, query, _FormValueWidget){
10 // dijit/form/MultiSelect
12 return declare("dijit.form.MultiSelect", _FormValueWidget, {
14 // Widget version of a `<select multiple=true>` element,
15 // for selecting multiple options.
18 // Number of elements to display on a page
19 // NOTE: may be removed in version 2.0, since elements may have variable height;
20 // set the size via style="..." or CSS class names instead.
23 templateString: "<select multiple='true' ${!nameAttrSetting} data-dojo-attach-point='containerNode,focusNode' data-dojo-attach-event='onchange: _onChange'></select>",
25 addSelected: function(/*dijit/form/MultiSelect*/ select){
27 // Move the selected nodes of a passed Select widget
28 // instance to this Select widget.
31 // | // move all the selected values from "bar" to "foo"
32 // | dijit.byId("foo").addSelected(dijit.byId("bar"));
34 select.getSelected().forEach(function(n){
35 if(this.restoreOriginalText){
36 n.text = this.enforceTextDirWithUcc(this.restoreOriginalText(n), n.text);
38 this.containerNode.appendChild(n);
39 // scroll to bottom to see item
40 // cannot use scrollIntoView since <option> tags don't support all attributes
41 // does not work on IE due to a bug where <select> always shows scrollTop = 0
42 this.domNode.scrollTop = this.domNode.offsetHeight; // overshoot will be ignored
43 // scrolling the source select is trickier esp. on safari who forgets to change the scrollbar size
44 var oldscroll = select.domNode.scrollTop;
45 select.domNode.scrollTop = 0;
46 select.domNode.scrollTop = oldscroll;
48 this._set('value', this.get('value'));
51 getSelected: function(){
53 // Access the NodeList of the selected options directly
54 return query("option",this.containerNode).filter(function(n){
55 return n.selected; // Boolean
59 _getValueAttr: function(){
61 // Hook so get('value') works.
63 // Returns an array of the selected options' values.
65 // Don't call getSelect.map() because it doesn't return a real array,
66 // and that messes up dojo.toJson() calls like in the Form.html test
67 return array.map(this.getSelected(), function(n){
72 multiple: true, // for Form
74 _setValueAttr: function(/*Array*/ values, /*Boolean?*/ priorityChange){
76 // Hook so set('value', values) works.
78 // Set the value(s) of this Select based on passed values
79 query("option",this.containerNode).forEach(function(n){
80 n.selected = (array.indexOf(values,n.value) != -1);
82 this.inherited(arguments);
85 invertSelection: function(/*Boolean?*/ onChange){
87 // Invert the selection
89 // If false, onChange is not fired.
91 query("option",this.containerNode).forEach(function(n){
92 if(!n.selected){ val.push(n.value); }
94 this._setValueAttr(val, !(onChange === false || onChange == null));
97 _onChange: function(/*Event*/){
98 this._handleOnChange(this.get('value'), true);
101 // for layout widgets:
102 resize: function(/*Object*/ size){
104 domGeometry.setMarginBox(this.domNode, size);
108 postCreate: function(){
109 this._set('value', this.get('value'));
110 this.inherited(arguments);
113 _setTextDirAttr: function(textDir){
114 // to insure the code executed only when _BidiSupport loaded, and only
115 // when there was a change in textDir
116 if((this.textDir != textDir || !this._created) && this.enforceTextDirWithUcc){
117 this._set("textDir", textDir);
119 query("option",this.containerNode).forEach(function(option){
120 // If the value wasn't defined explicitly, it the same object as
121 // option.text. Since the option.text will be modified (by wrapping of UCC)
122 // we want to save the original option.value for form submission.
123 if(!this._created && option.value === option.text){
124 option.value = option.text;
126 // apply the bidi support
127 option.text = this.enforceTextDirWithUcc(option, option.originalText || option.text);