]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/debian/missing-sources/framework/Web/Javascripts/source/tinymce-405/classes/ui/Control.js
baculum: Add missing-sources directory in debian metadata structure
[bacula/bacula] / gui / baculum / debian / missing-sources / framework / Web / Javascripts / source / tinymce-405 / classes / ui / Control.js
1 /**
2  * Control.js
3  *
4  * Copyright, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://www.tinymce.com/license
8  * Contributing: http://www.tinymce.com/contributing
9  */
10
11 /**
12  * This is the base class for all controls and containers. All UI control instances inherit
13  * from this one as it has the base logic needed by all of them.
14  *
15  * @class tinymce.ui.Control
16  */
17 define("tinymce/ui/Control", [
18         "tinymce/util/Class",
19         "tinymce/util/Tools",
20         "tinymce/ui/Collection",
21         "tinymce/ui/DomUtils"
22 ], function(Class, Tools, Collection, DomUtils) {
23         "use strict";
24
25         var nativeEvents = Tools.makeMap("focusin focusout scroll click dblclick mousedown mouseup mousemove mouseover" +
26                                                                 " mouseout mouseenter mouseleave wheel keydown keypress keyup contextmenu", " ");
27
28         var elementIdCache = {};
29         var hasMouseWheelEventSupport = "onmousewheel" in document;
30         var hasWheelEventSupport = false;
31
32         var Control = Class.extend({
33                 Statics: {
34                         controlIdLookup: {}
35                 },
36
37                 /**
38                  * Class/id prefix to use for all controls.
39                  *
40                  * @final
41                  * @field {String} classPrefix
42                  */
43                 classPrefix: "mce-",
44
45                 /**
46                  * Constructs a new control instance with the specified settings.
47                  *
48                  * @constructor
49                  * @param {Object} settings Name/value object with settings.
50                  * @setting {String} style Style CSS properties to add.
51                  * @setting {String} border Border box values example: 1 1 1 1
52                  * @setting {String} padding Padding box values example: 1 1 1 1
53                  * @setting {String} margin Margin box values example: 1 1 1 1
54                  * @setting {Number} minWidth Minimal width for the control.
55                  * @setting {Number} minHeight Minimal height for the control.
56                  * @setting {String} classes Space separated list of classes to add.
57                  * @setting {String} role WAI-ARIA role to use for control.
58                  * @setting {Boolean} hidden Is the control hidden by default.
59                  * @setting {Boolean} disabled Is the control disabled by default.
60                  * @setting {String} name Name of the control instance.
61                  */
62                 init: function(settings) {
63                         var self = this, classes, i;
64
65                         self.settings = settings = Tools.extend({}, self.Defaults, settings);
66
67                         // Initial states
68                         self._id = DomUtils.id();
69                         self._text = self._name = '';
70                         self._width = self._height = 0;
71                         self._aria = {role: settings.role};
72
73                         // Setup classes
74                         classes = settings.classes;
75                         if (classes) {
76                                 classes = classes.split(' ');
77                                 classes.map = {};
78                                 i = classes.length;
79                                 while (i--) {
80                                         classes.map[classes[i]] = true;
81                                 }
82                         }
83
84                         self._classes = classes || [];
85                         self.visible(true);
86
87                         // Set some properties
88                         Tools.each('title text width height name classes visible disabled active value'.split(' '), function(name) {
89                                 var value = settings[name], undef;
90
91                                 if (value !== undef) {
92                                         self[name](value);
93                                 } else if (self['_' + name] === undef) {
94                                         self['_' + name] = false;
95                                 }
96                         });
97
98                         self.on('click', function() {
99                                 if (self.disabled()) {
100                                         return false;
101                                 }
102                         });
103
104                         // TODO: Is this needed duplicate code see above?
105                         if (settings.classes) {
106                                 Tools.each(settings.classes.split(' '), function(cls) {
107                                         self.addClass(cls);
108                                 });
109                         }
110
111                         /**
112                          * Name/value object with settings for the current control.
113                          *
114                          * @field {Object} settings
115                          */
116                         self.settings = settings;
117
118                         self._borderBox = self.parseBox(settings.border);
119                         self._paddingBox = self.parseBox(settings.padding);
120                         self._marginBox = self.parseBox(settings.margin);
121
122                         if (settings.hidden) {
123                                 self.hide();
124                         }
125                 },
126
127                 // Will generate getter/setter methods for these properties
128                 Properties: 'parent,title,text,width,height,disabled,active,name,value',
129
130                 // Will generate empty dummy functions for these
131                 Methods: 'renderHtml',
132
133                 /**
134                  * Returns the root element to render controls into.
135                  *
136                  * @method getContainerElm
137                  * @return {Element} HTML DOM element to render into.
138                  */
139                 getContainerElm: function() {
140                         return document.body;
141                 },
142
143                 /**
144                  * Returns a control instance for the current DOM element.
145                  *
146                  * @method getParentCtrl
147                  * @param {Element} elm HTML dom element to get parent control from.
148                  * @return {tinymce.ui.Control} Control instance or undefined.
149                  */
150                 getParentCtrl: function(elm) {
151                         var ctrl;
152
153                         while (elm) {
154                                 ctrl = Control.controlIdLookup[elm.id];
155                                 if (ctrl) {
156                                         break;
157                                 }
158
159                                 elm = elm.parentNode;
160                         }
161
162                         return ctrl;
163                 },
164
165                 /**
166                  * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
167                  *
168                  * @method parseBox
169                  * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
170                  * @return {Object} Object with top/right/bottom/left properties.
171                  * @private
172                  */
173                 parseBox: function(value) {
174                         var len, radix = 10;
175
176                         if (!value) {
177                                 return;
178                         }
179
180                         if (typeof(value) === "number") {
181                                 value = value || 0;
182
183                                 return {
184                                         top: value,
185                                         left: value,
186                                         bottom: value,
187                                         right: value
188                                 };
189                         }
190
191                         value = value.split(' ');
192                         len = value.length;
193
194                         if (len === 1) {
195                                 value[1] = value[2] = value[3] = value[0];
196                         } else if (len === 2) {
197                                 value[2] = value[0];
198                                 value[3] = value[1];
199                         } else if (len === 3) {
200                                 value[3] = value[1];
201                         }
202
203                         return {
204                                 top: parseInt(value[0], radix) || 0,
205                                 right: parseInt(value[1], radix) || 0,
206                                 bottom: parseInt(value[2], radix) || 0,
207                                 left: parseInt(value[3], radix) || 0
208                         };
209                 },
210
211                 borderBox: function() {
212                         return this._borderBox;
213                 },
214
215                 paddingBox: function() {
216                         return this._paddingBox;
217                 },
218
219                 marginBox: function() {
220                         return this._marginBox;
221                 },
222
223                 measureBox: function(elm, prefix) {
224                         function getStyle(name) {
225                                 var defaultView = document.defaultView;
226
227                                 if (defaultView) {
228                                         // Remove camelcase
229                                         name = name.replace(/[A-Z]/g, function(a) {
230                                                 return '-' + a;
231                                         });
232
233                                         return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
234                                 }
235
236                                 return elm.currentStyle[name];
237                         }
238
239                         function getSide(name) {
240                                 var val = parseInt(getStyle(name), 10);
241
242                                 return isNaN(val) ? 0 : val;
243                         }
244
245                         return {
246                                 top: getSide(prefix + "TopWidth"),
247                                 right: getSide(prefix + "RightWidth"),
248                                 bottom: getSide(prefix + "BottomWidth"),
249                                 left: getSide(prefix + "LeftWidth")
250                         };
251                 },
252
253                 /**
254                  * Initializes the current controls layout rect.
255                  * This will be executed by the layout managers to determine the
256                  * default minWidth/minHeight etc.
257                  *
258                  * @method initLayoutRect
259                  * @return {Object} Layout rect instance.
260                  */
261                 initLayoutRect: function() {
262                         var self = this, settings = self.settings, borderBox, layoutRect;
263                         var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
264                         var startMinWidth, startMinHeight;
265
266                         // Measure boxes
267                         borderBox = self._borderBox = self._borderBox || self.measureBox(elm, 'border');
268                         self._paddingBox = self._paddingBox || self.measureBox(elm, 'padding');
269                         self._marginBox = self._marginBox || self.measureBox(elm, 'margin');
270
271                         // Setup minWidth/minHeight and width/height
272                         startMinWidth = settings.minWidth;
273                         startMinHeight = settings.minHeight;
274                         minWidth = startMinWidth || elm.offsetWidth;
275                         minHeight = startMinHeight || elm.offsetHeight;
276                         width = settings.width;
277                         height = settings.height;
278                         autoResize = settings.autoResize;
279                         autoResize = typeof(autoResize) != "undefined" ? autoResize : !width && !height;
280
281                         width = width || minWidth;
282                         height = height || minHeight;
283
284                         var deltaW = borderBox.left + borderBox.right;
285                         var deltaH = borderBox.top + borderBox.bottom;
286
287                         var maxW = settings.maxWidth || 0xFFFF;
288                         var maxH = settings.maxHeight || 0xFFFF;
289
290                         // Setup initial layout rect
291                         self._layoutRect = layoutRect = {
292                                 x: settings.x || 0,
293                                 y: settings.y || 0,
294                                 w: width,
295                                 h: height,
296                                 deltaW: deltaW,
297                                 deltaH: deltaH,
298                                 contentW: width - deltaW,
299                                 contentH: height - deltaH,
300                                 innerW: width - deltaW,
301                                 innerH: height - deltaH,
302                                 startMinWidth: startMinWidth || 0,
303                                 startMinHeight: startMinHeight || 0,
304                                 minW: Math.min(minWidth, maxW),
305                                 minH: Math.min(minHeight, maxH),
306                                 maxW: maxW,
307                                 maxH: maxH,
308                                 autoResize: autoResize,
309                                 scrollW: 0
310                         };
311
312                         self._lastLayoutRect = {};
313
314                         return layoutRect;
315                 },
316
317                 /**
318                  * Getter/setter for the current layout rect.
319                  *
320                  * @method layoutRect
321                  * @param {Object} [newRect] Optional new layout rect.
322                  * @return {tinymce.ui.Control/Object} Current control or rect object.
323                  */
324                 layoutRect: function(newRect) {
325                         var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
326
327                         // Initialize default layout rect
328                         if (!curRect) {
329                                 curRect = self.initLayoutRect();
330                         }
331
332                         // Set new rect values
333                         if (newRect) {
334                                 // Calc deltas between inner and outer sizes
335                                 deltaWidth = curRect.deltaW;
336                                 deltaHeight = curRect.deltaH;
337
338                                 // Set x position
339                                 if (newRect.x !== undef) {
340                                         curRect.x = newRect.x;
341                                 }
342
343                                 // Set y position
344                                 if (newRect.y !== undef) {
345                                         curRect.y = newRect.y;
346                                 }
347
348                                 // Set minW
349                                 if (newRect.minW !== undef) {
350                                         curRect.minW = newRect.minW;
351                                 }
352
353                                 // Set minH
354                                 if (newRect.minH !== undef) {
355                                         curRect.minH = newRect.minH;
356                                 }
357
358                                 // Set new width and calculate inner width
359                                 size = newRect.w;
360                                 if (size !== undef) {
361                                         size = size < curRect.minW ? curRect.minW : size;
362                                         size = size > curRect.maxW ? curRect.maxW : size;
363                                         curRect.w = size;
364                                         curRect.innerW = size - deltaWidth;
365                                 }
366
367                                 // Set new height and calculate inner height
368                                 size = newRect.h;
369                                 if (size !== undef) {
370                                         size = size < curRect.minH ? curRect.minH : size;
371                                         size = size > curRect.maxH ? curRect.maxH : size;
372                                         curRect.h = size;
373                                         curRect.innerH = size - deltaHeight;
374                                 }
375
376                                 // Set new inner width and calculate width
377                                 size = newRect.innerW;
378                                 if (size !== undef) {
379                                         size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
380                                         size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
381                                         curRect.innerW = size;
382                                         curRect.w = size + deltaWidth;
383                                 }
384
385                                 // Set new height and calculate inner height
386                                 size = newRect.innerH;
387                                 if (size !== undef) {
388                                         size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
389                                         size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
390                                         curRect.innerH = size;
391                                         curRect.h = size + deltaHeight;
392                                 }
393
394                                 // Set new contentW
395                                 if (newRect.contentW !== undef) {
396                                         curRect.contentW = newRect.contentW;
397                                 }
398
399                                 // Set new contentH
400                                 if (newRect.contentH !== undef) {
401                                         curRect.contentH = newRect.contentH;
402                                 }
403
404                                 // Compare last layout rect with the current one to see if we need to repaint or not
405                                 lastLayoutRect = self._lastLayoutRect;
406                                 if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
407                                         lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
408                                         repaintControls = Control.repaintControls;
409
410                                         if (repaintControls) {
411                                                 if (repaintControls.map && !repaintControls.map[self._id]) {
412                                                         repaintControls.push(self);
413                                                         repaintControls.map[self._id] = true;
414                                                 }
415                                         }
416
417                                         lastLayoutRect.x = curRect.x;
418                                         lastLayoutRect.y = curRect.y;
419                                         lastLayoutRect.w = curRect.w;
420                                         lastLayoutRect.h = curRect.h;
421                                 }
422
423                                 return self;
424                         }
425
426                         return curRect;
427                 },
428
429                 /**
430                  * Repaints the control after a layout operation.
431                  *
432                  * @method repaint
433                  */
434                 repaint: function() {
435                         var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect;
436
437                         style = self.getEl().style;
438                         rect = self._layoutRect;
439                         lastRepaintRect = self._lastRepaintRect || {};
440
441                         borderBox = self._borderBox;
442                         borderW = borderBox.left + borderBox.right;
443                         borderH = borderBox.top + borderBox.bottom;
444
445                         if (rect.x !== lastRepaintRect.x) {
446                                 style.left = rect.x + 'px';
447                                 lastRepaintRect.x = rect.x;
448                         }
449
450                         if (rect.y !== lastRepaintRect.y) {
451                                 style.top = rect.y + 'px';
452                                 lastRepaintRect.y = rect.y;
453                         }
454
455                         if (rect.w !== lastRepaintRect.w) {
456                                 style.width = (rect.w - borderW) + 'px';
457                                 lastRepaintRect.w = rect.w;
458                         }
459
460                         if (rect.h !== lastRepaintRect.h) {
461                                 style.height = (rect.h - borderH) + 'px';
462                                 lastRepaintRect.h = rect.h;
463                         }
464
465                         // Update body if needed
466                         if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
467                                 bodyStyle = self.getEl('body').style;
468                                 bodyStyle.width = (rect.innerW) + 'px';
469                                 lastRepaintRect.innerW = rect.innerW;
470                         }
471
472                         if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
473                                 bodyStyle = bodyStyle || self.getEl('body').style;
474                                 bodyStyle.height = (rect.innerH) + 'px';
475                                 lastRepaintRect.innerH = rect.innerH;
476                         }
477
478                         self._lastRepaintRect = lastRepaintRect;
479                         self.fire('repaint', {}, false);
480                 },
481
482                 /**
483                  * Binds a callback to the specified event. This event can both be
484                  * native browser events like "click" or custom ones like PostRender.
485                  *
486                  * The callback function will be passed a DOM event like object that enables yout do stop propagation.
487                  *
488                  * @method on
489                  * @param {String} name Name of the event to bind. For example "click".
490                  * @param {String/function} callback Callback function to execute ones the event occurs.
491                  * @return {tinymce.ui.Control} Current control object.
492                  */
493                 on: function(name, callback) {
494                         var self = this, bindings, handlers, names, i;
495
496                         function resolveCallbackName(name) {
497                                 var callback, scope;
498
499                                 return function(e) {
500                                         if (!callback) {
501                                                 self.parents().each(function(ctrl) {
502                                                         var callbacks = ctrl.settings.callbacks;
503
504                                                         if (callbacks && (callback = callbacks[name])) {
505                                                                 scope = ctrl;
506                                                                 return false;
507                                                         }
508                                                 });
509                                         }
510
511                                         return callback.call(scope, e);
512                                 };
513                         }
514
515                         if (callback) {
516                                 if (typeof(callback) == 'string') {
517                                         callback = resolveCallbackName(callback);
518                                 }
519
520                                 names = name.toLowerCase().split(' ');
521                                 i = names.length;
522                                 while (i--) {
523                                         name = names[i];
524
525                                         bindings = self._bindings;
526                                         if (!bindings) {
527                                                 bindings = self._bindings = {};
528                                         }
529
530                                         handlers = bindings[name];
531                                         if (!handlers) {
532                                                 handlers = bindings[name] = [];
533                                         }
534
535                                         handlers.push(callback);
536
537                                         if (nativeEvents[name]) {
538                                                 if (!self._nativeEvents) {
539                                                         self._nativeEvents = {name: true};
540                                                 } else {
541                                                         self._nativeEvents[name] = true;
542                                                 }
543
544                                                 if (self._rendered) {
545                                                         self.bindPendingEvents();
546                                                 }
547                                         }
548                                 }
549                         }
550
551                         return self;
552                 },
553
554                 /**
555                  * Unbinds the specified event and optionally a specific callback. If you omit the name
556                  * parameter all event handlers will be removed. If you omit the callback all event handles
557                  * by the specified name will be removed.
558                  *
559                  * @method off
560                  * @param {String} [name] Name for the event to unbind.
561                  * @param {function} [callback] Callback function to unbind.
562                  * @return {mxex.ui.Control} Current control object.
563                  */
564                 off: function(name, callback) {
565                         var self = this, i, bindings = self._bindings, handlers, bindingName, names, hi;
566
567                         if (bindings) {
568                                 if (name) {
569                                         names = name.toLowerCase().split(' ');
570                                         i = names.length;
571                                         while (i--) {
572                                                 name = names[i];
573                                                 handlers = bindings[name];
574
575                                                 // Unbind all handlers
576                                                 if (!name) {
577                                                         for (bindingName in bindings) {
578                                                                 bindings[bindingName].length = 0;
579                                                         }
580
581                                                         return self;
582                                                 }
583
584                                                 if (handlers) {
585                                                         // Unbind all by name
586                                                         if (!callback) {
587                                                                 handlers.length = 0;
588                                                         } else {
589                                                                 // Unbind specific ones
590                                                                 hi = handlers.length;
591                                                                 while (hi--) {
592                                                                         if (handlers[hi] === callback) {
593                                                                                 handlers.splice(hi, 1);
594                                                                         }
595                                                                 }
596                                                         }
597                                                 }
598                                         }
599                                 } else {
600                                         self._bindings = [];
601                                 }
602                         }
603
604                         return self;
605                 },
606
607                 /**
608                  * Fires the specified event by name and arguments on the control. This will execute all
609                  * bound event handlers.
610                  *
611                  * @method fire
612                  * @param {String} name Name of the event to fire.
613                  * @param {Object} [args] Arguments to pass to the event.
614                  * @param {Boolean} [bubble] Value to control bubbeling. Defaults to true.
615                  * @return {Object} Current arguments object.
616                  */
617                 fire: function(name, args, bubble) {
618                         var self = this, i, l, handlers, parentCtrl;
619
620                         name = name.toLowerCase();
621
622                         // Dummy function that gets replaced on the delegation state functions
623                         function returnFalse() {
624                                 return false;
625                         }
626
627                         // Dummy function that gets replaced on the delegation state functions
628                         function returnTrue() {
629                                 return true;
630                         }
631
632                         // Setup empty object if args is omited
633                         args = args || {};
634
635                         // Stick type into event object
636                         if (!args.type) {
637                                 args.type = name;
638                         }
639
640                         // Stick control into event
641                         if (!args.control) {
642                                 args.control = self;
643                         }
644
645                         // Add event delegation methods if they are missing
646                         if (!args.preventDefault) {
647                                 // Add preventDefault method
648                                 args.preventDefault = function() {
649                                         args.isDefaultPrevented = returnTrue;
650                                 };
651
652                                 // Add stopPropagation
653                                 args.stopPropagation = function() {
654                                         args.isPropagationStopped = returnTrue;
655                                 };
656
657                                 // Add stopImmediatePropagation
658                                 args.stopImmediatePropagation = function() {
659                                         args.isImmediatePropagationStopped = returnTrue;
660                                 };
661
662                                 // Add event delegation states
663                                 args.isDefaultPrevented = returnFalse;
664                                 args.isPropagationStopped = returnFalse;
665                                 args.isImmediatePropagationStopped = returnFalse;
666                         }
667
668                         if (self._bindings) {
669                                 handlers = self._bindings[name];
670
671                                 if (handlers) {
672                                         for (i = 0, l = handlers.length; i < l; i++) {
673                                                 // Execute callback and break if the callback returns a false
674                                                 if (!args.isImmediatePropagationStopped() && handlers[i].call(self, args) === false) {
675                                                         break;
676                                                 }
677                                         }
678                                 }
679                         }
680
681                         // Bubble event up to parent controls
682                         if (bubble !== false) {
683                                 parentCtrl = self.parent();
684                                 while (parentCtrl && !args.isPropagationStopped()) {
685                                         parentCtrl.fire(name, args, false);
686                                         parentCtrl = parentCtrl.parent();
687                                 }
688                         }
689
690                         return args;
691                 },
692
693                 /**
694                  * Returns a control collection with all parent controls.
695                  *
696                  * @method parents
697                  * @param {String} selector Optional selector expression to find parents.
698                  * @return {tinymce.ui.Collection} Collection with all parent controls.
699                  */
700                 parents: function(selector) {
701                         var ctrl = this, parents = new Collection();
702
703                         // Add each parent to collection
704                         for (ctrl = ctrl.parent(); ctrl; ctrl = ctrl.parent()) {
705                                 parents.add(ctrl);
706                         }
707
708                         // Filter away everything that doesn't match the selector
709                         if (selector) {
710                                 parents = parents.filter(selector);
711                         }
712
713                         return parents;
714                 },
715
716                 /**
717                  * Returns the control next to the current control.
718                  *
719                  * @method next
720                  * @return {tinymce.ui.Control} Next control instance.
721                  */
722                 next: function() {
723                         var parentControls = this.parent().items();
724
725                         return parentControls[parentControls.indexOf(this) + 1];
726                 },
727
728                 /**
729                  * Returns the control previous to the current control.
730                  *
731                  * @method prev
732                  * @return {tinymce.ui.Control} Previous control instance.
733                  */
734                 prev: function() {
735                         var parentControls = this.parent().items();
736
737                         return parentControls[parentControls.indexOf(this) - 1];
738                 },
739
740                 /**
741                  * Find the common ancestor for two control instances.
742                  *
743                  * @method findCommonAncestor
744                  * @param {tinymce.ui.Control} ctrl1 First control.
745                  * @param {tinymce.ui.Control} ctrl2 Second control.
746                  * @return {tinymce.ui.Control} Ancestor control instance.
747                  */
748                 findCommonAncestor: function(ctrl1, ctrl2) {
749                         var parentCtrl;
750
751                         while (ctrl1) {
752                                 parentCtrl = ctrl2;
753
754                                 while (parentCtrl && ctrl1 != parentCtrl) {
755                                         parentCtrl = parentCtrl.parent();
756                                 }
757
758                                 if (ctrl1 == parentCtrl) {
759                                         break;
760                                 }
761
762                                 ctrl1 = ctrl1.parent();
763                         }
764
765                         return ctrl1;
766                 },
767
768                 /**
769                  * Returns true/false if the specific control has the specific class.
770                  *
771                  * @method hasClass
772                  * @param {String} cls Class to check for.
773                  * @param {String} [group] Sub element group name.
774                  * @return {Boolean} True/false if the control has the specified class.
775                  */
776                 hasClass: function(cls, group) {
777                         var classes = this._classes[group || 'control'];
778
779                         cls = this.classPrefix + cls;
780
781                         return classes && !!classes.map[cls];
782                 },
783
784                 /**
785                  * Adds the specified class to the control
786                  *
787                  * @method addClass
788                  * @param {String} cls Class to check for.
789                  * @param {String} [group] Sub element group name.
790                  * @return {tinymce.ui.Control} Current control object.
791                  */
792                 addClass: function(cls, group) {
793                         var self = this, classes, elm;
794
795                         cls = this.classPrefix + cls;
796                         classes = self._classes[group || 'control'];
797
798                         if (!classes) {
799                                 classes = [];
800                                 classes.map = {};
801                                 self._classes[group || 'control'] = classes;
802                         }
803
804                         if (!classes.map[cls]) {
805                                 classes.map[cls] = cls;
806                                 classes.push(cls);
807
808                                 if (self._rendered) {
809                                         elm = self.getEl(group);
810
811                                         if (elm) {
812                                                 elm.className = classes.join(' ');
813                                         }
814                                 }
815                         }
816
817                         return self;
818                 },
819
820                 /**
821                  * Removes the specified class from the control.
822                  *
823                  * @method removeClass
824                  * @param {String} cls Class to remove.
825                  * @param {String} [group] Sub element group name.
826                  * @return {tinymce.ui.Control} Current control object.
827                  */
828                 removeClass: function(cls, group) {
829                         var self = this, classes, i, elm;
830
831                         cls = this.classPrefix + cls;
832                         classes = self._classes[group || 'control'];
833                         if (classes && classes.map[cls]) {
834                                 delete classes.map[cls];
835
836                                 i = classes.length;
837                                 while (i--) {
838                                         if (classes[i] === cls) {
839                                                 classes.splice(i, 1);
840                                         }
841                                 }
842                         }
843
844                         if (self._rendered) {
845                                 elm = self.getEl(group);
846
847                                 if (elm) {
848                                         elm.className = classes.join(' ');
849                                 }
850                         }
851
852                         return self;
853                 },
854
855                 /**
856                  * Toggles the specified class on the control.
857                  *
858                  * @method toggleClass
859                  * @param {String} cls Class to remove.
860                  * @param {Boolean} state True/false state to add/remove class.
861                  * @param {String} [group] Sub element group name.
862                  * @return {tinymce.ui.Control} Current control object.
863                  */
864                 toggleClass: function(cls, state, group) {
865                         var self = this;
866
867                         if (state) {
868                                 self.addClass(cls, group);
869                         } else {
870                                 self.removeClass(cls, group);
871                         }
872
873                         return self;
874                 },
875
876                 /**
877                  * Returns the class string for the specified group name.
878                  *
879                  * @method classes
880                  * @param {String} [group] Group to get clases by.
881                  * @return {String} Classes for the specified group.
882                  */
883                 classes: function(group) {
884                         var classes = this._classes[group || 'control'];
885
886                         return classes ? classes.join(' ') : '';
887                 },
888
889                 /**
890                  * Sets the inner HTML of the control element.
891                  *
892                  * @method innerHtml
893                  * @param {String} html Html string to set as inner html.
894                  * @return {tinymce.ui.Control} Current control object.
895                  */
896                 innerHtml: function(html) {
897                         DomUtils.innerHtml(this.getEl(), html);
898                         return this;
899                 },
900
901                 /**
902                  * Returns the control DOM element or sub element.
903                  *
904                  * @method getEl
905                  * @param {String} [suffix] Suffix to get element by.
906                  * @param {Boolean} [dropCache] True if the cache for the element should be dropped.
907                  * @return {Element} HTML DOM element for the current control or it's children.
908                  */
909                 getEl: function(suffix, dropCache) {
910                         var elm, id = suffix ? this._id + '-' + suffix : this._id;
911
912                         elm = elementIdCache[id] = (dropCache === true ? null : elementIdCache[id]) || DomUtils.get(id);
913
914                         return elm;
915                 },
916
917                 /**
918                  * Sets/gets the visible for the control.
919                  *
920                  * @method visible
921                  * @param {Boolean} state Value to set to control.
922                  * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
923                  */
924                 visible: function(state) {
925                         var self = this, parentCtrl;
926
927                         if (typeof(state) !== "undefined") {
928                                 if (self._visible !== state) {
929                                         if (self._rendered) {
930                                                 self.getEl().style.display = state ? '' : 'none';
931                                         }
932
933                                         self._visible = state;
934
935                                         // Parent container needs to reflow
936                                         parentCtrl = self.parent();
937                                         if (parentCtrl) {
938                                                 parentCtrl._lastRect = null;
939                                         }
940
941                                         self.fire(state ? 'show' : 'hide');
942                                 }
943
944                                 return self;
945                         }
946
947                         return self._visible;
948                 },
949
950                 /**
951                  * Sets the visible state to true.
952                  *
953                  * @method show
954                  * @return {tinymce.ui.Control} Current control instance.
955                  */
956                 show: function() {
957                         return this.visible(true);
958                 },
959
960                 /**
961                  * Sets the visible state to false.
962                  *
963                  * @method hide
964                  * @return {tinymce.ui.Control} Current control instance.
965                  */
966                 hide: function() {
967                         return this.visible(false);
968                 },
969
970                 /**
971                  * Focuses the current control.
972                  *
973                  * @method focus
974                  * @return {tinymce.ui.Control} Current control instance.
975                  */
976                 focus: function() {
977                         try {
978                                 this.getEl().focus();
979                         } catch (ex) {
980                                 // Ignore IE error
981                         }
982
983                         return this;
984                 },
985
986                 /**
987                  * Blurs the current control.
988                  *
989                  * @method blur
990                  * @return {tinymce.ui.Control} Current control instance.
991                  */
992                 blur: function() {
993                         this.getEl().blur();
994
995                         return this;
996                 },
997
998                 /**
999                  * Sets the specified aria property.
1000                  *
1001                  * @method aria
1002                  * @param {String} name Name of the aria property to set.
1003                  * @param {String} value Value of the aria property.
1004                  * @return {tinymce.ui.Control} Current control instance.
1005                  */
1006                 aria: function(name, value) {
1007                         var self = this, elm = self.getEl();
1008
1009                         if (typeof(value) === "undefined") {
1010                                 return self._aria[name];
1011                         } else {
1012                                 self._aria[name] = value;
1013                         }
1014
1015                         if (self._rendered) {
1016                                 if (name == 'label') {
1017                                         elm.setAttribute('aria-labeledby', self._id);
1018                                 }
1019
1020                                 elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
1021                         }
1022
1023                         return self;
1024                 },
1025
1026                 /**
1027                  * Encodes the specified string with HTML entities. It will also
1028                  * translate the string to different languages.
1029                  *
1030                  * @method encode
1031                  * @param {String/Object/Array} text Text to entity encode.
1032                  * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
1033                  * @return {String} Encoded and possible traslated string. 
1034                  */
1035                 encode: function(text, translate) {
1036                         if (translate !== false && Control.translate) {
1037                                 text = Control.translate(text);
1038                         }
1039
1040                         return (text || '').replace(/[&<>"]/g, function(match) {
1041                                 return '&#' + match.charCodeAt(0) + ';';
1042                         });
1043                 },
1044
1045                 /**
1046                  * Adds items before the current control.
1047                  *
1048                  * @method before
1049                  * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
1050                  * @return {tinymce.ui.Control} Current control instance.
1051                  */
1052                 before: function(items) {
1053                         var self = this, parent = self.parent();
1054
1055                         if (parent) {
1056                                 parent.insert(items, parent.items().indexOf(self), true);
1057                         }
1058
1059                         return self;
1060                 },
1061
1062                 /**
1063                  * Adds items after the current control.
1064                  *
1065                  * @method after
1066                  * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
1067                  * @return {tinymce.ui.Control} Current control instance.
1068                  */
1069                 after: function(items) {
1070                         var self = this, parent = self.parent();
1071
1072                         if (parent) {
1073                                 parent.insert(items, parent.items().indexOf(self));
1074                         }
1075
1076                         return self;
1077                 },
1078
1079                 /**
1080                  * Removes the current control from DOM and from UI collections.
1081                  *
1082                  * @method remove
1083                  * @return {tinymce.ui.Control} Current control instance.
1084                  */
1085                 remove: function() {
1086                         var self = this, elm = self.getEl(), parent = self.parent(), newItems;
1087
1088                         if (self.items) {
1089                                 var controls = self.items().toArray();
1090                                 var i = controls.length;
1091                                 while (i--) {
1092                                         controls[i].remove();
1093                                 }
1094                         }
1095
1096                         if (parent && parent.items) {
1097                                 newItems = [];
1098
1099                                 parent.items().each(function(item) {
1100                                         if (item !== self) {
1101                                                 newItems.push(item);
1102                                         }
1103                                 });
1104
1105                                 parent.items().set(newItems);
1106                                 parent._lastRect = null;
1107                         }
1108
1109                         if (self._eventsRoot && self._eventsRoot == self) {
1110                                 DomUtils.off(elm);
1111                         }
1112
1113                         delete Control.controlIdLookup[self._id];
1114
1115                         if (elm.parentNode) {
1116                                 elm.parentNode.removeChild(elm);
1117                         }
1118
1119                         return self;
1120                 },
1121
1122                 /**
1123                  * Renders the control before the specified element.
1124                  *
1125                  * @method renderBefore
1126                  * @param {Element} elm Element to render before.
1127                  * @return {tinymce.ui.Control} Current control instance.
1128                  */
1129                 renderBefore: function(elm) {
1130                         var self = this;
1131
1132                         elm.parentNode.insertBefore(DomUtils.createFragment(self.renderHtml()), elm);
1133                         self.postRender();
1134
1135                         return self;
1136                 },
1137
1138                 /**
1139                  * Renders the control to the specified element.
1140                  *
1141                  * @method renderBefore
1142                  * @param {Element} elm Element to render to.
1143                  * @return {tinymce.ui.Control} Current control instance.
1144                  */
1145                 renderTo: function(elm) {
1146                         var self = this;
1147
1148                         elm = elm || self.getContainerElm();
1149                         elm.appendChild(DomUtils.createFragment(self.renderHtml()));
1150                         self.postRender();
1151
1152                         return self;
1153                 },
1154
1155                 /**
1156                  * Post render method. Called after the control has been rendered to the target.
1157                  *
1158                  * @method postRender
1159                  * @return {tinymce.ui.Control} Current control instance.
1160                  */
1161                 postRender: function() {
1162                         var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
1163
1164                         // Bind on<event> settings
1165                         for (name in settings) {
1166                                 if (name.indexOf("on") === 0) {
1167                                         self.on(name.substr(2), settings[name]);
1168                                 }
1169                         }
1170
1171                         if (self._eventsRoot) {
1172                                 for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
1173                                         parentEventsRoot = parent._eventsRoot;
1174                                 }
1175
1176                                 if (parentEventsRoot) {
1177                                         for (name in parentEventsRoot._nativeEvents) {
1178                                                 self._nativeEvents[name] = true;
1179                                         }
1180                                 }
1181                         }
1182
1183                         self.bindPendingEvents();
1184
1185                         if (settings.style) {
1186                                 elm = self.getEl();
1187                                 if (elm) {
1188                                         elm.setAttribute('style', settings.style);
1189                                         elm.style.cssText = settings.style;
1190                                 }
1191                         }
1192
1193                         if (!self._visible) {
1194                                 DomUtils.css(self.getEl(), 'display', 'none');
1195                         }
1196
1197                         if (self.settings.border) {
1198                                 box = self.borderBox();
1199                                 DomUtils.css(self.getEl(), {
1200                                         'border-top-width': box.top,
1201                                         'border-right-width': box.right,
1202                                         'border-bottom-width': box.bottom,
1203                                         'border-left-width': box.left
1204                                 });
1205                         }
1206
1207                         // Add instance to lookup
1208                         Control.controlIdLookup[self._id] = self;
1209
1210                         for (var key in self._aria) {
1211                                 self.aria(key, self._aria[key]);
1212                         }
1213
1214                         self.fire('postrender', {}, false);
1215                 },
1216
1217                 /**
1218                  * Scrolls the current control into view.
1219                  *
1220                  * @method scrollIntoView
1221                  * @param {String} align Alignment in view top|center|bottom.
1222                  * @return {tinymce.ui.Control} Current control instance.
1223                  */
1224                 scrollIntoView: function(align) {
1225                         function getOffset(elm, rootElm) {
1226                                 var x, y, parent = elm;
1227
1228                                 x = y = 0;
1229                                 while (parent && parent != rootElm && parent.nodeType) {
1230                                         x += parent.offsetLeft || 0;
1231                                         y += parent.offsetTop || 0;
1232                                         parent = parent.offsetParent;
1233                                 }
1234
1235                                 return {x: x, y: y};
1236                         }
1237
1238                         var elm = this.getEl(), parentElm = elm.parentNode;
1239                         var x, y, width, height, parentWidth, parentHeight;
1240                         var pos = getOffset(elm, parentElm);
1241
1242                         x = pos.x;
1243                         y = pos.y;
1244                         width = elm.offsetWidth;
1245                         height = elm.offsetHeight;
1246                         parentWidth = parentElm.clientWidth;
1247                         parentHeight = parentElm.clientHeight;
1248
1249                         if (align == "end") {
1250                                 x -= parentWidth - width;
1251                                 y -= parentHeight - height;
1252                         } else if (align == "center") {
1253                                 x -= (parentWidth / 2) - (width / 2);
1254                                 y -= (parentHeight / 2) - (height / 2);
1255                         }
1256
1257                         parentElm.scrollLeft = x;
1258                         parentElm.scrollTop = y;
1259
1260                         return this;
1261                 },
1262
1263                 /**
1264                  * Binds pending DOM events.
1265                  *
1266                  * @private
1267                  */
1268                 bindPendingEvents: function() {
1269                         var self = this, i, l, parents, eventRootCtrl, nativeEvents, name;
1270
1271                         function delegate(e) {
1272                                 var control = self.getParentCtrl(e.target);
1273
1274                                 if (control) {
1275                                         control.fire(e.type, e);
1276                                 }
1277                         }
1278
1279                         function mouseLeaveHandler() {
1280                                 var ctrl = eventRootCtrl._lastHoverCtrl;
1281
1282                                 if (ctrl) {
1283                                         ctrl.fire("mouseleave", {target: ctrl.getEl()});
1284
1285                                         ctrl.parents().each(function(ctrl) {
1286                                                 ctrl.fire("mouseleave", {target: ctrl.getEl()});
1287                                         });
1288
1289                                         eventRootCtrl._lastHoverCtrl = null;
1290                                 }
1291                         }
1292
1293                         function mouseEnterHandler(e) {
1294                                 var ctrl = self.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
1295
1296                                 // Over on a new control
1297                                 if (ctrl !== lastCtrl) {
1298                                         eventRootCtrl._lastHoverCtrl = ctrl;
1299
1300                                         parents = ctrl.parents().toArray().reverse();
1301                                         parents.push(ctrl);
1302
1303                                         if (lastCtrl) {
1304                                                 lastParents = lastCtrl.parents().toArray().reverse();
1305                                                 lastParents.push(lastCtrl);
1306
1307                                                 for (idx = 0; idx < lastParents.length; idx++) {
1308                                                         if (parents[idx] !== lastParents[idx]) {
1309                                                                 break;
1310                                                         }
1311                                                 }
1312
1313                                                 for (i = lastParents.length - 1; i >= idx; i--) {
1314                                                         lastCtrl = lastParents[i];
1315                                                         lastCtrl.fire("mouseleave", {
1316                                                                 target : lastCtrl.getEl()
1317                                                         });
1318                                                 }
1319                                         }
1320
1321                                         for (i = idx; i < parents.length; i++) {
1322                                                 ctrl = parents[i];
1323                                                 ctrl.fire("mouseenter", {
1324                                                         target : ctrl.getEl()
1325                                                 });
1326                                         }
1327                                 }
1328                         }
1329
1330                         function fixWheelEvent(e) {
1331                                 e.preventDefault();
1332
1333                                 if (e.type == "mousewheel") {
1334                                         e.deltaY = - 1/40 * e.wheelDelta;
1335
1336                                         if (e.wheelDeltaX) {
1337                                                 e.deltaX = -1/40 * e.wheelDeltaX;
1338                                         }
1339                                 } else {
1340                                         e.deltaX = 0;
1341                                         e.deltaY = e.detail;
1342                                 }
1343
1344                                 e = self.fire("wheel", e);
1345                         }
1346
1347                         self._rendered = true;
1348
1349                         nativeEvents = self._nativeEvents;
1350                         if (nativeEvents) {
1351                                 // Find event root element if it exists
1352                                 parents = self.parents().toArray();
1353                                 parents.unshift(self);
1354                                 for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
1355                                         eventRootCtrl = parents[i]._eventsRoot;
1356                                 }
1357
1358                                 // Event root wasn't found the use the root control
1359                                 if (!eventRootCtrl) {
1360                                         eventRootCtrl = parents[parents.length - 1] || self;
1361                                 }
1362
1363                                 // Set the eventsRoot property on children that didn't have it
1364                                 self._eventsRoot = eventRootCtrl;
1365                                 for (l = i, i = 0; i < l; i++) {
1366                                         parents[i]._eventsRoot = eventRootCtrl;
1367                                 }
1368
1369                                 // Bind native event delegates
1370                                 for (name in nativeEvents) {
1371                                         if (!nativeEvents) {
1372                                                 return false;
1373                                         }
1374
1375                                         if (name === "wheel" && !hasWheelEventSupport) {
1376                                                 if (hasMouseWheelEventSupport) {
1377                                                         DomUtils.on(self.getEl(), "mousewheel", fixWheelEvent);
1378                                                 } else {
1379                                                         DomUtils.on(self.getEl(), "DOMMouseScroll", fixWheelEvent);
1380                                                 }
1381
1382                                                 continue;
1383                                         }
1384
1385                                         // Special treatment for mousenter/mouseleave since these doesn't bubble
1386                                         if (name === "mouseenter" || name === "mouseleave") {
1387                                                 // Fake mousenter/mouseleave
1388                                                 if (!eventRootCtrl._hasMouseEnter) {
1389                                                         DomUtils.on(eventRootCtrl.getEl(), "mouseleave", mouseLeaveHandler);
1390                                                         DomUtils.on(eventRootCtrl.getEl(), "mouseover", mouseEnterHandler);
1391                                                         eventRootCtrl._hasMouseEnter = 1;
1392                                                 }
1393                                         } else if (!eventRootCtrl[name]) {
1394                                                 DomUtils.on(eventRootCtrl.getEl(), name, delegate);
1395                                                 eventRootCtrl[name] = true;
1396                                         }
1397
1398                                         // Remove the event once it's bound
1399                                         nativeEvents[name] = false;
1400                                 }
1401                         }
1402                 },
1403
1404                 /**
1405                  * Reflows the current control and it's parents.
1406                  * This should be used after you for example append children to the current control so
1407                  * that the layout managers know that they need to reposition everything.
1408                  *
1409                  * @example
1410                  * container.append({type: 'button', text: 'My button'}).reflow();
1411                  *
1412                  * @method reflow
1413                  * @return {tinymce.ui.Control} Current control instance.
1414                  */
1415                 reflow: function() {
1416                         this.repaint();
1417
1418                         return this;
1419                 }
1420
1421                 /**
1422                  * Sets/gets the parent container for the control.
1423                  *
1424                  * @method parent
1425                  * @param {tinymce.ui.Container} parent Optional parent to set.
1426                  * @return {tinymce.ui.Control} Parent control or the current control on a set action.
1427                  */
1428                 // parent: function(parent) {} -- Generated
1429
1430                 /**
1431                  * Sets/gets the text for the control.
1432                  *
1433                  * @method text
1434                  * @param {String} value Value to set to control.
1435                  * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
1436                  */
1437                 // text: function(value) {} -- Generated
1438
1439                 /**
1440                  * Sets/gets the width for the control.
1441                  *
1442                  * @method width
1443                  * @param {Number} value Value to set to control.
1444                  * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
1445                  */
1446                 // width: function(value) {} -- Generated
1447
1448                 /**
1449                  * Sets/gets the height for the control.
1450                  *
1451                  * @method height
1452                  * @param {Number} value Value to set to control.
1453                  * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
1454                  */
1455                 // height: function(value) {} -- Generated
1456
1457                 /**
1458                  * Sets/gets the disabled state on the control.
1459                  *
1460                  * @method disabled
1461                  * @param {Boolean} state Value to set to control.
1462                  * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
1463                  */
1464                 // disabled: function(state) {} -- Generated
1465
1466                 /**
1467                  * Sets/gets the active for the control.
1468                  *
1469                  * @method active
1470                  * @param {Boolean} state Value to set to control.
1471                  * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
1472                  */
1473                 // active: function(state) {} -- Generated
1474
1475                 /**
1476                  * Sets/gets the name for the control.
1477                  *
1478                  * @method name
1479                  * @param {String} value Value to set to control.
1480                  * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
1481                  */
1482                 // name: function(value) {} -- Generated
1483
1484                 /**
1485                  * Sets/gets the title for the control.
1486                  *
1487                  * @method title
1488                  * @param {String} value Value to set to control.
1489                  * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
1490                  */
1491                 // title: function(value) {} -- Generated
1492         });
1493
1494         return Control;
1495 });