]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/ui/ListBox.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / tiny_mce / classes / ui / ListBox.js
1 /**
2  * ListBox.js
3  *
4  * Copyright 2009, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 (function(tinymce) {
12         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
13
14         /**
15          * This class is used to create list boxes/select list. This one will generate
16          * a non native control. This one has the benefits of having visual items added.
17          *
18          * @class tinymce.ui.ListBox
19          * @extends tinymce.ui.Control
20          * @example
21          * // Creates a new plugin class and a custom listbox
22          * tinymce.create('tinymce.plugins.ExamplePlugin', {
23          *     createControl: function(n, cm) {
24          *         switch (n) {
25          *             case 'mylistbox':
26          *                 var mlb = cm.createListBox('mylistbox', {
27          *                      title : 'My list box',
28          *                      onselect : function(v) {
29          *                          tinyMCE.activeEditor.windowManager.alert('Value selected:' + v);
30          *                      }
31          *                 });
32          * 
33          *                 // Add some values to the list box
34          *                 mlb.add('Some item 1', 'val1');
35          *                 mlb.add('some item 2', 'val2');
36          *                 mlb.add('some item 3', 'val3');
37          * 
38          *                 // Return the new listbox instance
39          *                 return mlb;
40          *         }
41          * 
42          *         return null;
43          *     }
44          * });
45          * 
46          * // Register plugin with a short name
47          * tinymce.PluginManager.add('example', tinymce.plugins.ExamplePlugin);
48          * 
49          * // Initialize TinyMCE with the new plugin and button
50          * tinyMCE.init({
51          *    ...
52          *    plugins : '-example', // - means TinyMCE will not try to load it
53          *    theme_advanced_buttons1 : 'mylistbox' // Add the new example listbox to the toolbar
54          * });
55          */
56         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
57                 /**
58                  * Constructs a new listbox control instance.
59                  *
60                  * @constructor
61                  * @method ListBox
62                  * @param {String} id Control id for the list box.
63                  * @param {Object} s Optional name/value settings object.
64                  * @param {Editor} ed Optional the editor instance this button is for.
65                  */
66                 ListBox : function(id, s, ed) {
67                         var t = this;
68
69                         t.parent(id, s, ed);
70
71                         /**
72                          * Array of ListBox items.
73                          *
74                          * @property items
75                          * @type Array
76                          */
77                         t.items = [];
78
79                         /**
80                          * Fires when the selection has been changed.
81                          *
82                          * @event onChange
83                          */
84                         t.onChange = new Dispatcher(t);
85
86                         /**
87                          * Fires after the element has been rendered to DOM.
88                          *
89                          * @event onPostRender
90                          */
91                         t.onPostRender = new Dispatcher(t);
92
93                         /**
94                          * Fires when a new item is added.
95                          *
96                          * @event onAdd
97                          */
98                         t.onAdd = new Dispatcher(t);
99
100                         /**
101                          * Fires when the menu gets rendered.
102                          *
103                          * @event onRenderMenu
104                          */
105                         t.onRenderMenu = new tinymce.util.Dispatcher(this);
106
107                         t.classPrefix = 'mceListBox';
108                 },
109
110                 /**
111                  * Selects a item/option by value. This will both add a visual selection to the
112                  * item and change the title of the control to the title of the option.
113                  *
114                  * @method select
115                  * @param {String/function} va Value to look for inside the list box or a function selector.
116                  */
117                 select : function(va) {
118                         var t = this, fv, f;
119
120                         if (va == undefined)
121                                 return t.selectByIndex(-1);
122
123                         // Is string or number make function selector
124                         if (va && va.call)
125                                 f = va;
126                         else {
127                                 f = function(v) {
128                                         return v == va;
129                                 };
130                         }
131
132                         // Do we need to do something?
133                         if (va != t.selectedValue) {
134                                 // Find item
135                                 each(t.items, function(o, i) {
136                                         if (f(o.value)) {
137                                                 fv = 1;
138                                                 t.selectByIndex(i);
139                                                 return false;
140                                         }
141                                 });
142
143                                 if (!fv)
144                                         t.selectByIndex(-1);
145                         }
146                 },
147
148                 /**
149                  * Selects a item/option by index. This will both add a visual selection to the
150                  * item and change the title of the control to the title of the option.
151                  *
152                  * @method selectByIndex
153                  * @param {String} idx Index to select, pass -1 to select menu/title of select box.
154                  */
155                 selectByIndex : function(idx) {
156                         var t = this, e, o;
157
158                         if (idx != t.selectedIndex) {
159                                 e = DOM.get(t.id + '_text');
160                                 o = t.items[idx];
161
162                                 if (o) {
163                                         t.selectedValue = o.value;
164                                         t.selectedIndex = idx;
165                                         DOM.setHTML(e, DOM.encode(o.title));
166                                         DOM.removeClass(e, 'mceTitle');
167                                         DOM.setAttrib(t.id, 'aria-valuenow', o.title);
168                                 } else {
169                                         DOM.setHTML(e, DOM.encode(t.settings.title));
170                                         DOM.addClass(e, 'mceTitle');
171                                         t.selectedValue = t.selectedIndex = null;
172                                         DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title);
173                                 }
174                                 e = 0;
175                         }
176                 },
177
178                 /**
179                  * Adds a option item to the list box.
180                  *
181                  * @method add
182                  * @param {String} n Title for the new option.
183                  * @param {String} v Value for the new option.
184                  * @param {Object} o Optional object with settings like for example class.
185                  */
186                 add : function(n, v, o) {
187                         var t = this;
188
189                         o = o || {};
190                         o = tinymce.extend(o, {
191                                 title : n,
192                                 value : v
193                         });
194
195                         t.items.push(o);
196                         t.onAdd.dispatch(t, o);
197                 },
198
199                 /**
200                  * Returns the number of items inside the list box.
201                  *
202                  * @method getLength
203                  * @param {Number} Number of items inside the list box.
204                  */
205                 getLength : function() {
206                         return this.items.length;
207                 },
208
209                 /**
210                  * Renders the list box as a HTML string. This method is much faster than using the DOM and when
211                  * creating a whole toolbar with buttons it does make a lot of difference.
212                  *
213                  * @method renderHTML
214                  * @return {String} HTML for the list box control element.
215                  */
216                 renderHTML : function() {
217                         var h = '', t = this, s = t.settings, cp = t.classPrefix;
218
219                         h = '<span role="button" aria-haspopup="true" aria-labelledby="' + t.id +'_text" aria-describedby="' + t.id + '_voiceDesc"><table role="presentation" tabindex="0" id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';
220                         h += '<td>' + DOM.createHTML('span', {id: t.id + '_voiceDesc', 'class': 'voiceLabel', style:'display:none;'}, t.settings.title); 
221                         h += DOM.createHTML('a', {id : t.id + '_text', tabindex : -1, href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';
222                         h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>') + '</td>';
223                         h += '</tr></tbody></table></span>';
224
225                         return h;
226                 },
227
228                 /**
229                  * Displays the drop menu with all items.
230                  *
231                  * @method showMenu
232                  */
233                 showMenu : function() {
234                         var t = this, p2, e = DOM.get(this.id), m;
235
236                         if (t.isDisabled() || t.items.length == 0)
237                                 return;
238
239                         if (t.menu && t.menu.isMenuVisible)
240                                 return t.hideMenu();
241
242                         if (!t.isMenuRendered) {
243                                 t.renderMenu();
244                                 t.isMenuRendered = true;
245                         }
246
247                         p2 = DOM.getPos(e);
248
249                         m = t.menu;
250                         m.settings.offset_x = p2.x;
251                         m.settings.offset_y = p2.y;
252                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
253
254                         // Select in menu
255                         if (t.oldID)
256                                 m.items[t.oldID].setSelected(0);
257
258                         each(t.items, function(o) {
259                                 if (o.value === t.selectedValue) {
260                                         m.items[o.id].setSelected(1);
261                                         t.oldID = o.id;
262                                 }
263                         });
264
265                         m.showMenu(0, e.clientHeight);
266
267                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
268                         DOM.addClass(t.id, t.classPrefix + 'Selected');
269
270                         //DOM.get(t.id + '_text').focus();
271                 },
272
273                 /**
274                  * Hides the drop menu.
275                  *
276                  * @method hideMenu
277                  */
278                 hideMenu : function(e) {
279                         var t = this;
280
281                         if (t.menu && t.menu.isMenuVisible) {
282                                 DOM.removeClass(t.id, t.classPrefix + 'Selected');
283
284                                 // Prevent double toogles by canceling the mouse click event to the button
285                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))
286                                         return;
287
288                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {
289                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');
290                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
291                                         t.menu.hideMenu();
292                                 }
293                         }
294                 },
295
296                 /**
297                  * Renders the menu to the DOM.
298                  *
299                  * @method renderMenu
300                  */
301                 renderMenu : function() {
302                         var t = this, m;
303
304                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
305                                 menu_line : 1,
306                                 'class' : t.classPrefix + 'Menu mceNoIcons',
307                                 max_width : 150,
308                                 max_height : 150
309                         });
310
311                         m.onHideMenu.add(function() {
312                                 t.hideMenu();
313                                 t.focus();
314                         });
315
316                         m.add({
317                                 title : t.settings.title,
318                                 'class' : 'mceMenuItemTitle',
319                                 onclick : function() {
320                                         if (t.settings.onselect('') !== false)
321                                                 t.select(''); // Must be runned after
322                                 }
323                         });
324
325                         each(t.items, function(o) {
326                                 // No value then treat it as a title
327                                 if (o.value === undefined) {
328                                         m.add({
329                                                 title : o.title,
330                                                 'class' : 'mceMenuItemTitle',
331                                                 onclick : function() {
332                                                         if (t.settings.onselect('') !== false)
333                                                                 t.select(''); // Must be runned after
334                                                 }
335                                         });
336                                 } else {
337                                         o.id = DOM.uniqueId();
338                                         o.onclick = function() {
339                                                 if (t.settings.onselect(o.value) !== false)
340                                                         t.select(o.value); // Must be runned after
341                                         };
342
343                                         m.add(o);
344                                 }
345                         });
346
347                         t.onRenderMenu.dispatch(t, m);
348                         t.menu = m;
349                 },
350
351                 /**
352                  * Post render event. This will be executed after the control has been rendered and can be used to
353                  * set states, add events to the control etc. It's recommended for subclasses of the control to call this method by using this.parent().
354                  *
355                  * @method postRender
356                  */
357                 postRender : function() {
358                         var t = this, cp = t.classPrefix;
359
360                         Event.add(t.id, 'click', t.showMenu, t);
361                         Event.add(t.id, 'keydown', function(evt) {
362                                 if (evt.keyCode == 32) { // Space
363                                         t.showMenu(evt);
364                                         Event.cancel(evt);
365                                 }
366                         });
367                         Event.add(t.id, 'focus', function() {
368                                 if (!t._focused) {
369                                         t.keyDownHandler = Event.add(t.id, 'keydown', function(e) {
370                                                 if (e.keyCode == 40) {
371                                                         t.showMenu();
372                                                         Event.cancel(e);
373                                                 }
374                                         });
375                                         t.keyPressHandler = Event.add(t.id, 'keypress', function(e) {
376                                                 var v;
377                                                 if (e.keyCode == 13) {
378                                                         // Fake select on enter
379                                                         v = t.selectedValue;
380                                                         t.selectedValue = null; // Needs to be null to fake change
381                                                         Event.cancel(e);
382                                                         t.settings.onselect(v);
383                                                 }
384                                         });
385                                 }
386
387                                 t._focused = 1;
388                         });
389                         Event.add(t.id, 'blur', function() {
390                                 Event.remove(t.id, 'keydown', t.keyDownHandler);
391                                 Event.remove(t.id, 'keypress', t.keyPressHandler);
392                                 t._focused = 0;
393                         });
394
395                         // Old IE doesn't have hover on all elements
396                         if (tinymce.isIE6 || !DOM.boxModel) {
397                                 Event.add(t.id, 'mouseover', function() {
398                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))
399                                                 DOM.addClass(t.id, cp + 'Hover');
400                                 });
401
402                                 Event.add(t.id, 'mouseout', function() {
403                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))
404                                                 DOM.removeClass(t.id, cp + 'Hover');
405                                 });
406                         }
407
408                         t.onPostRender.dispatch(t, DOM.get(t.id));
409                 },
410
411                 /**
412                  * Destroys the ListBox i.e. clear memory and events.
413                  *
414                  * @method destroy
415                  */
416                 destroy : function() {
417                         this.parent();
418
419                         Event.clear(this.id + '_text');
420                         Event.clear(this.id + '_open');
421                 }
422         });
423 })(tinymce);