1 /**
  2  * @fileOverview manage a list of single-select options
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("menubutton/select", function (S, Node, MenuButton, Menu, Option, undefined) {
  6 
  7     function getSelectedItem(self) {
  8         var menu = self.get("menu"),
  9             cs = menu.children || menu.get && menu.get("children") || [],
 10             value = self.get("value"),
 11             c,
 12             i;
 13         for (i = 0; i < cs.length; i++) {
 14             c = cs[i];
 15             if (getItemValue(c) == value) {
 16                 return c;
 17             }
 18         }
 19         return null;
 20     }
 21 
 22     // c : Option
 23     // c.get("value")
 24     // c: Object
 25     // c.value
 26     function getItemValue(c) {
 27         var v;
 28         if (c) {
 29             if (c.get) {
 30                 if ((v = c.get("value")) === undefined) {
 31                     v = c.get("textContent") || c.get("content");
 32                 }
 33 
 34             } else {
 35                 if ((v = c.value) === undefined) {
 36                     v = c.textContent || c.content;
 37                 }
 38             }
 39         }
 40         return v;
 41     }
 42 
 43     function deSelectAllExcept(self) {
 44         var menu = self.get("menu"),
 45             value = self.get("value"),
 46             cs = menu && menu.get && menu.get("children");
 47         S.each(cs, function (c) {
 48             if (c && c.set) {
 49                 c.set("selected", getItemValue(c) == value)
 50             }
 51         });
 52     }
 53 
 54     /**
 55      *  different from menubutton by highlighting the currently selected option
 56      *  on open menu.
 57      */
 58     function _handleMenuShow() {
 59         var self = this,
 60             selectedItem = getSelectedItem(self),
 61             m = self.get("menu");
 62         m.set("highlightedItem", selectedItem || m.getChildAt(0));
 63         // 初始化选中
 64         if (selectedItem) {
 65             selectedItem.set("selected", true);
 66         }
 67     }
 68 
 69     function _updateCaption(self) {
 70         var item = getSelectedItem(self),
 71             textContent = item && ( item.textContent || item.get && item.get("textContent")),
 72             content = item && (item.content || item.get && item.get('content'));
 73         // 可能设置到 select content 的内容并不和 menuitem 的内容完全一致
 74         self.set("content", textContent || content || self.get("defaultCaption"));
 75     }
 76 
 77     /**
 78      * @class
 79      * Select component which supports single selection from a drop down menu
 80      * with semantics similar to native HTML select.
 81      * xclass: 'select'.
 82      * @name Select
 83      * @memberOf MenuButton
 84      * @extends MenuButton
 85      */
 86     var Select = MenuButton.extend(
 87         /**
 88          * @lends MenuButton.Select.prototype
 89          */
 90         {
 91 
 92             /**
 93              * Bind menu to current Select. When menu shows, set highlightedItem to current selectedItem.
 94              * @protected
 95              */
 96             bindMenu:function () {
 97                 var self = this;
 98                 Select.superclass.bindMenu.call(self);
 99                 self.get("menu").on("show", _handleMenuShow, self);
100             },
101 
102             /**
103              * Handle click on drop down menu.
104              * Set selected menu item as current selectedItem and hide drop down menu.
105              * Protected, should only be overridden by subclasses.
106              * @protected
107              * @override
108              * @param {Event.Object} e
109              */
110             handleMenuClick:function (e) {
111                 var self = this,
112                     target = e.target,
113                     prevTarget = getSelectedItem(self);
114                 self.set("value", getItemValue(target));
115                 self.set("collapsed", true);
116                 self.fire("click", {
117                     target:target,
118                     prevTarget:prevTarget
119                 });
120             },
121 
122             /**
123              * Removes all menu items from current select, and set selectedItem to null.
124              * @override
125              */
126             removeItems:function () {
127                 var self = this;
128                 Select.superclass.removeItems.apply(self, arguments);
129                 self.set("value", null);
130             },
131 
132             /**
133              * Remove specified item from current select.
134              * If specified item is selectedItem, then set selectedItem to null.
135              * @override
136              */
137             removeItem:function (c) {
138                 var self = this;
139                 Select.superclass.removeItem.apply(self, arguments);
140                 if (c.get("value") == self.get("value")) {
141                     self.set("value", null);
142                 }
143             },
144 
145             _uiSetValue:function () {
146                 var self = this;
147                 deSelectAllExcept(self);
148                 _updateCaption(self);
149             },
150 
151             _uiSetDefaultCaption:function () {
152                 _updateCaption(this);
153             }
154         },
155         {
156             ATTRS:/**
157              * @lends MenuButton.Select.prototype
158              */
159             {
160 
161                 /**
162                  * Get current select 's value.
163                  */
164                 value:{
165                 },
166 
167                 /**
168                  * Default caption to be shown when no option is selected.
169                  * @type String
170                  */
171                 defaultCaption:{
172                     value:""
173                 }
174             },
175 
176             /**
177              * Generate a select component from native select element.
178              * @param {HTMLElement} element Native html select element.
179              * @param {Object} cfg Extra configuration for current select component.
180              * @memberOf MenuButton.Select
181              */
182             decorate:function (element, cfg) {
183                 element = S.one(element);
184                 cfg = cfg || {};
185                 cfg.elBefore = element;
186 
187                 var name,
188                     allItems = [],
189                     select,
190                     selectedItem = null,
191                     curValue = element.val(),
192                     options = element.all("option");
193 
194                 options.each(function (option) {
195                     var item = {
196                         content:option.text(),
197                         elCls:option.attr("class"),
198                         value:option.val(),
199                         xclass:'option'
200                     };
201                     if (curValue == option.val()) {
202                         selectedItem = {
203                             content:item.content,
204                             value:item.value
205                         };
206                     }
207                     allItems.push(item);
208                 });
209 
210                 S.mix(cfg, {
211                     menu:S.mix({
212                         xclass:'popupmenu',
213                         children:allItems
214                     }, cfg.menuCfg)
215                 });
216 
217                 delete cfg.menuCfg;
218 
219                 select = new Select(S.mix(cfg, selectedItem)).render();
220 
221                 if (name = element.attr("name")) {
222                     var input = new Node("<input" +
223                         " type='hidden'" +
224                         " name='" + name
225                         + "' value='" + curValue + "'>")
226                         .insertBefore(element, undefined);
227 
228                     select.on("afterValueChange", function (e) {
229                         input.val(e.newVal || "");
230                     });
231                 }
232 
233                 element.remove();
234                 return select;
235             }
236 
237         }, {
238             xclass:'select',
239             priority:30
240         });
241 
242     return Select;
243 
244 }, {
245     requires:['node', './base', 'menu', './option']
246 });
247 
248 /**
249  * TODO
250  *  how to emulate multiple ?
251  **/