| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588 |
- /**
- * Control.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
- /*eslint consistent-this:0 */
- /**
- * This is the base class for all controls and containers. All UI control instances inherit
- * from this one as it has the base logic needed by all of them.
- *
- * @class tinymce.ui.Control
- */
- define("tinymce/ui/Control", [
- "tinymce/util/Class",
- "tinymce/util/Tools",
- "tinymce/ui/Collection",
- "tinymce/ui/DomUtils"
- ], function(Class, Tools, Collection, DomUtils) {
- "use strict";
- var nativeEvents = Tools.makeMap("focusin focusout scroll click dblclick mousedown mouseup mousemove mouseover" +
- " mouseout mouseenter mouseleave wheel keydown keypress input keyup contextmenu", " ");
- var elementIdCache = {};
- var hasMouseWheelEventSupport = "onmousewheel" in document;
- var hasWheelEventSupport = false;
- var classPrefix = "mce-";
- var Control = Class.extend({
- Statics: {
- elementIdCache: elementIdCache,
- classPrefix: classPrefix
- },
- isRtl: function() {
- return Control.rtl;
- },
- /**
- * Class/id prefix to use for all controls.
- *
- * @final
- * @field {String} classPrefix
- */
- classPrefix: classPrefix,
- /**
- * Constructs a new control instance with the specified settings.
- *
- * @constructor
- * @param {Object} settings Name/value object with settings.
- * @setting {String} style Style CSS properties to add.
- * @setting {String} border Border box values example: 1 1 1 1
- * @setting {String} padding Padding box values example: 1 1 1 1
- * @setting {String} margin Margin box values example: 1 1 1 1
- * @setting {Number} minWidth Minimal width for the control.
- * @setting {Number} minHeight Minimal height for the control.
- * @setting {String} classes Space separated list of classes to add.
- * @setting {String} role WAI-ARIA role to use for control.
- * @setting {Boolean} hidden Is the control hidden by default.
- * @setting {Boolean} disabled Is the control disabled by default.
- * @setting {String} name Name of the control instance.
- */
- init: function(settings) {
- var self = this, classes, i;
- self.settings = settings = Tools.extend({}, self.Defaults, settings);
- // Initial states
- self._id = settings.id || DomUtils.id();
- self._text = self._name = '';
- self._width = self._height = 0;
- self._aria = {role: settings.role};
- // Setup classes
- classes = settings.classes;
- if (classes) {
- classes = classes.split(' ');
- classes.map = {};
- i = classes.length;
- while (i--) {
- classes.map[classes[i]] = true;
- }
- }
- self._classes = classes || [];
- self.visible(true);
- // Set some properties
- Tools.each('title text width height name classes visible disabled active value'.split(' '), function(name) {
- var value = settings[name], undef;
- if (value !== undef) {
- self[name](value);
- } else if (self['_' + name] === undef) {
- self['_' + name] = false;
- }
- });
- self.on('click', function() {
- if (self.disabled()) {
- return false;
- }
- });
- // TODO: Is this needed duplicate code see above?
- if (settings.classes) {
- Tools.each(settings.classes.split(' '), function(cls) {
- self.addClass(cls);
- });
- }
- /**
- * Name/value object with settings for the current control.
- *
- * @field {Object} settings
- */
- self.settings = settings;
- self._borderBox = self.parseBox(settings.border);
- self._paddingBox = self.parseBox(settings.padding);
- self._marginBox = self.parseBox(settings.margin);
- if (settings.hidden) {
- self.hide();
- }
- },
- // Will generate getter/setter methods for these properties
- Properties: 'parent,title,text,width,height,disabled,active,name,value',
- // Will generate empty dummy functions for these
- Methods: 'renderHtml',
- /**
- * Returns the root element to render controls into.
- *
- * @method getContainerElm
- * @return {Element} HTML DOM element to render into.
- */
- getContainerElm: function() {
- return document.body;
- },
- /**
- * Returns a control instance for the current DOM element.
- *
- * @method getParentCtrl
- * @param {Element} elm HTML dom element to get parent control from.
- * @return {tinymce.ui.Control} Control instance or undefined.
- */
- getParentCtrl: function(elm) {
- var ctrl, lookup = this.getRoot().controlIdLookup;
- while (elm && lookup) {
- ctrl = lookup[elm.id];
- if (ctrl) {
- break;
- }
- elm = elm.parentNode;
- }
- return ctrl;
- },
- /**
- * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
- *
- * @method parseBox
- * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
- * @return {Object} Object with top/right/bottom/left properties.
- * @private
- */
- parseBox: function(value) {
- var len, radix = 10;
- if (!value) {
- return;
- }
- if (typeof(value) === "number") {
- value = value || 0;
- return {
- top: value,
- left: value,
- bottom: value,
- right: value
- };
- }
- value = value.split(' ');
- len = value.length;
- if (len === 1) {
- value[1] = value[2] = value[3] = value[0];
- } else if (len === 2) {
- value[2] = value[0];
- value[3] = value[1];
- } else if (len === 3) {
- value[3] = value[1];
- }
- return {
- top: parseInt(value[0], radix) || 0,
- right: parseInt(value[1], radix) || 0,
- bottom: parseInt(value[2], radix) || 0,
- left: parseInt(value[3], radix) || 0
- };
- },
- borderBox: function() {
- return this._borderBox;
- },
- paddingBox: function() {
- return this._paddingBox;
- },
- marginBox: function() {
- return this._marginBox;
- },
- measureBox: function(elm, prefix) {
- function getStyle(name) {
- var defaultView = document.defaultView;
- if (defaultView) {
- // Remove camelcase
- name = name.replace(/[A-Z]/g, function(a) {
- return '-' + a;
- });
- return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
- }
- return elm.currentStyle[name];
- }
- function getSide(name) {
- var val = parseFloat(getStyle(name), 10);
- return isNaN(val) ? 0 : val;
- }
- return {
- top: getSide(prefix + "TopWidth"),
- right: getSide(prefix + "RightWidth"),
- bottom: getSide(prefix + "BottomWidth"),
- left: getSide(prefix + "LeftWidth")
- };
- },
- /**
- * Initializes the current controls layout rect.
- * This will be executed by the layout managers to determine the
- * default minWidth/minHeight etc.
- *
- * @method initLayoutRect
- * @return {Object} Layout rect instance.
- */
- initLayoutRect: function() {
- var self = this, settings = self.settings, borderBox, layoutRect;
- var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
- var startMinWidth, startMinHeight, initialSize;
- // Measure the current element
- borderBox = self._borderBox = self._borderBox || self.measureBox(elm, 'border');
- self._paddingBox = self._paddingBox || self.measureBox(elm, 'padding');
- self._marginBox = self._marginBox || self.measureBox(elm, 'margin');
- initialSize = DomUtils.getSize(elm);
- // Setup minWidth/minHeight and width/height
- startMinWidth = settings.minWidth;
- startMinHeight = settings.minHeight;
- minWidth = startMinWidth || initialSize.width;
- minHeight = startMinHeight || initialSize.height;
- width = settings.width;
- height = settings.height;
- autoResize = settings.autoResize;
- autoResize = typeof(autoResize) != "undefined" ? autoResize : !width && !height;
- width = width || minWidth;
- height = height || minHeight;
- var deltaW = borderBox.left + borderBox.right;
- var deltaH = borderBox.top + borderBox.bottom;
- var maxW = settings.maxWidth || 0xFFFF;
- var maxH = settings.maxHeight || 0xFFFF;
- // Setup initial layout rect
- self._layoutRect = layoutRect = {
- x: settings.x || 0,
- y: settings.y || 0,
- w: width,
- h: height,
- deltaW: deltaW,
- deltaH: deltaH,
- contentW: width - deltaW,
- contentH: height - deltaH,
- innerW: width - deltaW,
- innerH: height - deltaH,
- startMinWidth: startMinWidth || 0,
- startMinHeight: startMinHeight || 0,
- minW: Math.min(minWidth, maxW),
- minH: Math.min(minHeight, maxH),
- maxW: maxW,
- maxH: maxH,
- autoResize: autoResize,
- scrollW: 0
- };
- self._lastLayoutRect = {};
- return layoutRect;
- },
- /**
- * Getter/setter for the current layout rect.
- *
- * @method layoutRect
- * @param {Object} [newRect] Optional new layout rect.
- * @return {tinymce.ui.Control/Object} Current control or rect object.
- */
- layoutRect: function(newRect) {
- var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
- // Initialize default layout rect
- if (!curRect) {
- curRect = self.initLayoutRect();
- }
- // Set new rect values
- if (newRect) {
- // Calc deltas between inner and outer sizes
- deltaWidth = curRect.deltaW;
- deltaHeight = curRect.deltaH;
- // Set x position
- if (newRect.x !== undef) {
- curRect.x = newRect.x;
- }
- // Set y position
- if (newRect.y !== undef) {
- curRect.y = newRect.y;
- }
- // Set minW
- if (newRect.minW !== undef) {
- curRect.minW = newRect.minW;
- }
- // Set minH
- if (newRect.minH !== undef) {
- curRect.minH = newRect.minH;
- }
- // Set new width and calculate inner width
- size = newRect.w;
- if (size !== undef) {
- size = size < curRect.minW ? curRect.minW : size;
- size = size > curRect.maxW ? curRect.maxW : size;
- curRect.w = size;
- curRect.innerW = size - deltaWidth;
- }
- // Set new height and calculate inner height
- size = newRect.h;
- if (size !== undef) {
- size = size < curRect.minH ? curRect.minH : size;
- size = size > curRect.maxH ? curRect.maxH : size;
- curRect.h = size;
- curRect.innerH = size - deltaHeight;
- }
- // Set new inner width and calculate width
- size = newRect.innerW;
- if (size !== undef) {
- size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
- size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
- curRect.innerW = size;
- curRect.w = size + deltaWidth;
- }
- // Set new height and calculate inner height
- size = newRect.innerH;
- if (size !== undef) {
- size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
- size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
- curRect.innerH = size;
- curRect.h = size + deltaHeight;
- }
- // Set new contentW
- if (newRect.contentW !== undef) {
- curRect.contentW = newRect.contentW;
- }
- // Set new contentH
- if (newRect.contentH !== undef) {
- curRect.contentH = newRect.contentH;
- }
- // Compare last layout rect with the current one to see if we need to repaint or not
- lastLayoutRect = self._lastLayoutRect;
- if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
- lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
- repaintControls = Control.repaintControls;
- if (repaintControls) {
- if (repaintControls.map && !repaintControls.map[self._id]) {
- repaintControls.push(self);
- repaintControls.map[self._id] = true;
- }
- }
- lastLayoutRect.x = curRect.x;
- lastLayoutRect.y = curRect.y;
- lastLayoutRect.w = curRect.w;
- lastLayoutRect.h = curRect.h;
- }
- return self;
- }
- return curRect;
- },
- /**
- * Repaints the control after a layout operation.
- *
- * @method repaint
- */
- repaint: function() {
- var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect, round;
- // Use Math.round on all values on IE < 9
- round = !document.createRange ? Math.round : function(value) {
- return value;
- };
- style = self.getEl().style;
- rect = self._layoutRect;
- lastRepaintRect = self._lastRepaintRect || {};
- borderBox = self._borderBox;
- borderW = borderBox.left + borderBox.right;
- borderH = borderBox.top + borderBox.bottom;
- if (rect.x !== lastRepaintRect.x) {
- style.left = round(rect.x) + 'px';
- lastRepaintRect.x = rect.x;
- }
- if (rect.y !== lastRepaintRect.y) {
- style.top = round(rect.y) + 'px';
- lastRepaintRect.y = rect.y;
- }
- if (rect.w !== lastRepaintRect.w) {
- style.width = round(rect.w - borderW) + 'px';
- lastRepaintRect.w = rect.w;
- }
- if (rect.h !== lastRepaintRect.h) {
- style.height = round(rect.h - borderH) + 'px';
- lastRepaintRect.h = rect.h;
- }
- // Update body if needed
- if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
- bodyStyle = self.getEl('body').style;
- bodyStyle.width = round(rect.innerW) + 'px';
- lastRepaintRect.innerW = rect.innerW;
- }
- if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
- bodyStyle = bodyStyle || self.getEl('body').style;
- bodyStyle.height = round(rect.innerH) + 'px';
- lastRepaintRect.innerH = rect.innerH;
- }
- self._lastRepaintRect = lastRepaintRect;
- self.fire('repaint', {}, false);
- },
- /**
- * Binds a callback to the specified event. This event can both be
- * native browser events like "click" or custom ones like PostRender.
- *
- * The callback function will be passed a DOM event like object that enables yout do stop propagation.
- *
- * @method on
- * @param {String} name Name of the event to bind. For example "click".
- * @param {String/function} callback Callback function to execute ones the event occurs.
- * @return {tinymce.ui.Control} Current control object.
- */
- on: function(name, callback) {
- var self = this, bindings, handlers, names, i;
- function resolveCallbackName(name) {
- var callback, scope;
- return function(e) {
- if (!callback) {
- self.parentsAndSelf().each(function(ctrl) {
- var callbacks = ctrl.settings.callbacks;
- if (callbacks && (callback = callbacks[name])) {
- scope = ctrl;
- return false;
- }
- });
- }
- return callback.call(scope, e);
- };
- }
- if (callback) {
- if (typeof(callback) == 'string') {
- callback = resolveCallbackName(callback);
- }
- names = name.toLowerCase().split(' ');
- i = names.length;
- while (i--) {
- name = names[i];
- bindings = self._bindings;
- if (!bindings) {
- bindings = self._bindings = {};
- }
- handlers = bindings[name];
- if (!handlers) {
- handlers = bindings[name] = [];
- }
- handlers.push(callback);
- if (nativeEvents[name]) {
- if (!self._nativeEvents) {
- self._nativeEvents = {};
- }
- self._nativeEvents[name] = true;
- if (self._rendered) {
- self.bindPendingEvents();
- }
- }
- }
- }
- return self;
- },
- /**
- * Unbinds the specified event and optionally a specific callback. If you omit the name
- * parameter all event handlers will be removed. If you omit the callback all event handles
- * by the specified name will be removed.
- *
- * @method off
- * @param {String} [name] Name for the event to unbind.
- * @param {function} [callback] Callback function to unbind.
- * @return {mxex.ui.Control} Current control object.
- */
- off: function(name, callback) {
- var self = this, i, bindings = self._bindings, handlers, bindingName, names, hi;
- if (bindings) {
- if (name) {
- names = name.toLowerCase().split(' ');
- i = names.length;
- while (i--) {
- name = names[i];
- handlers = bindings[name];
- // Unbind all handlers
- if (!name) {
- for (bindingName in bindings) {
- bindings[bindingName].length = 0;
- }
- return self;
- }
- if (handlers) {
- // Unbind all by name
- if (!callback) {
- handlers.length = 0;
- } else {
- // Unbind specific ones
- hi = handlers.length;
- while (hi--) {
- if (handlers[hi] === callback) {
- handlers.splice(hi, 1);
- }
- }
- }
- }
- }
- } else {
- self._bindings = [];
- }
- }
- return self;
- },
- /**
- * Fires the specified event by name and arguments on the control. This will execute all
- * bound event handlers.
- *
- * @method fire
- * @param {String} name Name of the event to fire.
- * @param {Object} [args] Arguments to pass to the event.
- * @param {Boolean} [bubble] Value to control bubbeling. Defaults to true.
- * @return {Object} Current arguments object.
- */
- fire: function(name, args, bubble) {
- var self = this, i, l, handlers, parentCtrl;
- name = name.toLowerCase();
- // Dummy function that gets replaced on the delegation state functions
- function returnFalse() {
- return false;
- }
- // Dummy function that gets replaced on the delegation state functions
- function returnTrue() {
- return true;
- }
- // Setup empty object if args is omited
- args = args || {};
- // Stick type into event object
- if (!args.type) {
- args.type = name;
- }
- // Stick control into event
- if (!args.control) {
- args.control = self;
- }
- // Add event delegation methods if they are missing
- if (!args.preventDefault) {
- // Add preventDefault method
- args.preventDefault = function() {
- args.isDefaultPrevented = returnTrue;
- };
- // Add stopPropagation
- args.stopPropagation = function() {
- args.isPropagationStopped = returnTrue;
- };
- // Add stopImmediatePropagation
- args.stopImmediatePropagation = function() {
- args.isImmediatePropagationStopped = returnTrue;
- };
- // Add event delegation states
- args.isDefaultPrevented = returnFalse;
- args.isPropagationStopped = returnFalse;
- args.isImmediatePropagationStopped = returnFalse;
- }
- if (self._bindings) {
- handlers = self._bindings[name];
- if (handlers) {
- for (i = 0, l = handlers.length; i < l; i++) {
- // Execute callback and break if the callback returns a false
- if (!args.isImmediatePropagationStopped() && handlers[i].call(self, args) === false) {
- break;
- }
- }
- }
- }
- // Bubble event up to parent controls
- if (bubble !== false) {
- parentCtrl = self.parent();
- while (parentCtrl && !args.isPropagationStopped()) {
- parentCtrl.fire(name, args, false);
- parentCtrl = parentCtrl.parent();
- }
- }
- return args;
- },
- /**
- * Returns true/false if the specified event has any listeners.
- *
- * @method hasEventListeners
- * @param {String} name Name of the event to check for.
- * @return {Boolean} True/false state if the event has listeners.
- */
- hasEventListeners: function(name) {
- return name in this._bindings;
- },
- /**
- * Returns a control collection with all parent controls.
- *
- * @method parents
- * @param {String} selector Optional selector expression to find parents.
- * @return {tinymce.ui.Collection} Collection with all parent controls.
- */
- parents: function(selector) {
- var self = this, ctrl, parents = new Collection();
- // Add each parent to collection
- for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) {
- parents.add(ctrl);
- }
- // Filter away everything that doesn't match the selector
- if (selector) {
- parents = parents.filter(selector);
- }
- return parents;
- },
- /**
- * Returns the current control and it's parents.
- *
- * @method parentsAndSelf
- * @param {String} selector Optional selector expression to find parents.
- * @return {tinymce.ui.Collection} Collection with all parent controls.
- */
- parentsAndSelf: function(selector) {
- return new Collection(this).add(this.parents(selector));
- },
- /**
- * Returns the control next to the current control.
- *
- * @method next
- * @return {tinymce.ui.Control} Next control instance.
- */
- next: function() {
- var parentControls = this.parent().items();
- return parentControls[parentControls.indexOf(this) + 1];
- },
- /**
- * Returns the control previous to the current control.
- *
- * @method prev
- * @return {tinymce.ui.Control} Previous control instance.
- */
- prev: function() {
- var parentControls = this.parent().items();
- return parentControls[parentControls.indexOf(this) - 1];
- },
- /**
- * Find the common ancestor for two control instances.
- *
- * @method findCommonAncestor
- * @param {tinymce.ui.Control} ctrl1 First control.
- * @param {tinymce.ui.Control} ctrl2 Second control.
- * @return {tinymce.ui.Control} Ancestor control instance.
- */
- findCommonAncestor: function(ctrl1, ctrl2) {
- var parentCtrl;
- while (ctrl1) {
- parentCtrl = ctrl2;
- while (parentCtrl && ctrl1 != parentCtrl) {
- parentCtrl = parentCtrl.parent();
- }
- if (ctrl1 == parentCtrl) {
- break;
- }
- ctrl1 = ctrl1.parent();
- }
- return ctrl1;
- },
- /**
- * Returns true/false if the specific control has the specific class.
- *
- * @method hasClass
- * @param {String} cls Class to check for.
- * @param {String} [group] Sub element group name.
- * @return {Boolean} True/false if the control has the specified class.
- */
- hasClass: function(cls, group) {
- var classes = this._classes[group || 'control'];
- cls = this.classPrefix + cls;
- return classes && !!classes.map[cls];
- },
- /**
- * Adds the specified class to the control
- *
- * @method addClass
- * @param {String} cls Class to check for.
- * @param {String} [group] Sub element group name.
- * @return {tinymce.ui.Control} Current control object.
- */
- addClass: function(cls, group) {
- var self = this, classes, elm;
- cls = this.classPrefix + cls;
- classes = self._classes[group || 'control'];
- if (!classes) {
- classes = [];
- classes.map = {};
- self._classes[group || 'control'] = classes;
- }
- if (!classes.map[cls]) {
- classes.map[cls] = cls;
- classes.push(cls);
- if (self._rendered) {
- elm = self.getEl(group);
- if (elm) {
- elm.className = classes.join(' ');
- }
- }
- }
- return self;
- },
- /**
- * Removes the specified class from the control.
- *
- * @method removeClass
- * @param {String} cls Class to remove.
- * @param {String} [group] Sub element group name.
- * @return {tinymce.ui.Control} Current control object.
- */
- removeClass: function(cls, group) {
- var self = this, classes, i, elm;
- cls = this.classPrefix + cls;
- classes = self._classes[group || 'control'];
- if (classes && classes.map[cls]) {
- delete classes.map[cls];
- i = classes.length;
- while (i--) {
- if (classes[i] === cls) {
- classes.splice(i, 1);
- }
- }
- }
- if (self._rendered) {
- elm = self.getEl(group);
- if (elm) {
- elm.className = classes.join(' ');
- }
- }
- return self;
- },
- /**
- * Toggles the specified class on the control.
- *
- * @method toggleClass
- * @param {String} cls Class to remove.
- * @param {Boolean} state True/false state to add/remove class.
- * @param {String} [group] Sub element group name.
- * @return {tinymce.ui.Control} Current control object.
- */
- toggleClass: function(cls, state, group) {
- var self = this;
- if (state) {
- self.addClass(cls, group);
- } else {
- self.removeClass(cls, group);
- }
- return self;
- },
- /**
- * Returns the class string for the specified group name.
- *
- * @method classes
- * @param {String} [group] Group to get clases by.
- * @return {String} Classes for the specified group.
- */
- classes: function(group) {
- var classes = this._classes[group || 'control'];
- return classes ? classes.join(' ') : '';
- },
- /**
- * Sets the inner HTML of the control element.
- *
- * @method innerHtml
- * @param {String} html Html string to set as inner html.
- * @return {tinymce.ui.Control} Current control object.
- */
- innerHtml: function(html) {
- DomUtils.innerHtml(this.getEl(), html);
- return this;
- },
- /**
- * Returns the control DOM element or sub element.
- *
- * @method getEl
- * @param {String} [suffix] Suffix to get element by.
- * @param {Boolean} [dropCache] True if the cache for the element should be dropped.
- * @return {Element} HTML DOM element for the current control or it's children.
- */
- getEl: function(suffix, dropCache) {
- var elm, id = suffix ? this._id + '-' + suffix : this._id;
- elm = elementIdCache[id] = (dropCache === true ? null : elementIdCache[id]) || DomUtils.get(id);
- return elm;
- },
- /**
- * Sets/gets the visible for the control.
- *
- * @method visible
- * @param {Boolean} state Value to set to control.
- * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
- */
- visible: function(state) {
- var self = this, parentCtrl;
- if (typeof(state) !== "undefined") {
- if (self._visible !== state) {
- if (self._rendered) {
- self.getEl().style.display = state ? '' : 'none';
- }
- self._visible = state;
- // Parent container needs to reflow
- parentCtrl = self.parent();
- if (parentCtrl) {
- parentCtrl._lastRect = null;
- }
- self.fire(state ? 'show' : 'hide');
- }
- return self;
- }
- return self._visible;
- },
- /**
- * Sets the visible state to true.
- *
- * @method show
- * @return {tinymce.ui.Control} Current control instance.
- */
- show: function() {
- return this.visible(true);
- },
- /**
- * Sets the visible state to false.
- *
- * @method hide
- * @return {tinymce.ui.Control} Current control instance.
- */
- hide: function() {
- return this.visible(false);
- },
- /**
- * Focuses the current control.
- *
- * @method focus
- * @return {tinymce.ui.Control} Current control instance.
- */
- focus: function() {
- try {
- this.getEl().focus();
- } catch (ex) {
- // Ignore IE error
- }
- return this;
- },
- /**
- * Blurs the current control.
- *
- * @method blur
- * @return {tinymce.ui.Control} Current control instance.
- */
- blur: function() {
- this.getEl().blur();
- return this;
- },
- /**
- * Sets the specified aria property.
- *
- * @method aria
- * @param {String} name Name of the aria property to set.
- * @param {String} value Value of the aria property.
- * @return {tinymce.ui.Control} Current control instance.
- */
- aria: function(name, value) {
- var self = this, elm = self.getEl(self.ariaTarget);
- if (typeof(value) === "undefined") {
- return self._aria[name];
- } else {
- self._aria[name] = value;
- }
- if (self._rendered) {
- elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
- }
- return self;
- },
- /**
- * Encodes the specified string with HTML entities. It will also
- * translate the string to different languages.
- *
- * @method encode
- * @param {String/Object/Array} text Text to entity encode.
- * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
- * @return {String} Encoded and possible traslated string.
- */
- encode: function(text, translate) {
- if (translate !== false) {
- text = this.translate(text);
- }
- return (text || '').replace(/[&<>"]/g, function(match) {
- return '&#' + match.charCodeAt(0) + ';';
- });
- },
- /**
- * Returns the translated string.
- *
- * @method translate
- * @param {String} text Text to translate.
- * @return {String} Translated string or the same as the input.
- */
- translate: function(text) {
- return Control.translate ? Control.translate(text) : text;
- },
- /**
- * Adds items before the current control.
- *
- * @method before
- * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
- * @return {tinymce.ui.Control} Current control instance.
- */
- before: function(items) {
- var self = this, parent = self.parent();
- if (parent) {
- parent.insert(items, parent.items().indexOf(self), true);
- }
- return self;
- },
- /**
- * Adds items after the current control.
- *
- * @method after
- * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
- * @return {tinymce.ui.Control} Current control instance.
- */
- after: function(items) {
- var self = this, parent = self.parent();
- if (parent) {
- parent.insert(items, parent.items().indexOf(self));
- }
- return self;
- },
- /**
- * Removes the current control from DOM and from UI collections.
- *
- * @method remove
- * @return {tinymce.ui.Control} Current control instance.
- */
- remove: function() {
- var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
- if (self.items) {
- var controls = self.items().toArray();
- i = controls.length;
- while (i--) {
- controls[i].remove();
- }
- }
- if (parent && parent.items) {
- newItems = [];
- parent.items().each(function(item) {
- if (item !== self) {
- newItems.push(item);
- }
- });
- parent.items().set(newItems);
- parent._lastRect = null;
- }
- if (self._eventsRoot && self._eventsRoot == self) {
- DomUtils.off(elm);
- }
- var lookup = self.getRoot().controlIdLookup;
- if (lookup) {
- delete lookup[self._id];
- }
- delete elementIdCache[self._id];
- if (elm && elm.parentNode) {
- var nodes = elm.getElementsByTagName('*');
- i = nodes.length;
- while (i--) {
- delete elementIdCache[nodes[i].id];
- }
- elm.parentNode.removeChild(elm);
- }
- self._rendered = false;
- return self;
- },
- /**
- * Renders the control before the specified element.
- *
- * @method renderBefore
- * @param {Element} elm Element to render before.
- * @return {tinymce.ui.Control} Current control instance.
- */
- renderBefore: function(elm) {
- var self = this;
- elm.parentNode.insertBefore(DomUtils.createFragment(self.renderHtml()), elm);
- self.postRender();
- return self;
- },
- /**
- * Renders the control to the specified element.
- *
- * @method renderBefore
- * @param {Element} elm Element to render to.
- * @return {tinymce.ui.Control} Current control instance.
- */
- renderTo: function(elm) {
- var self = this;
- elm = elm || self.getContainerElm();
- elm.appendChild(DomUtils.createFragment(self.renderHtml()));
- self.postRender();
- return self;
- },
- /**
- * Post render method. Called after the control has been rendered to the target.
- *
- * @method postRender
- * @return {tinymce.ui.Control} Current control instance.
- */
- postRender: function() {
- var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
- // Bind on<event> settings
- for (name in settings) {
- if (name.indexOf("on") === 0) {
- self.on(name.substr(2), settings[name]);
- }
- }
- if (self._eventsRoot) {
- for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
- parentEventsRoot = parent._eventsRoot;
- }
- if (parentEventsRoot) {
- for (name in parentEventsRoot._nativeEvents) {
- self._nativeEvents[name] = true;
- }
- }
- }
- self.bindPendingEvents();
- if (settings.style) {
- elm = self.getEl();
- if (elm) {
- elm.setAttribute('style', settings.style);
- elm.style.cssText = settings.style;
- }
- }
- if (!self._visible) {
- DomUtils.css(self.getEl(), 'display', 'none');
- }
- if (self.settings.border) {
- box = self.borderBox();
- DomUtils.css(self.getEl(), {
- 'border-top-width': box.top,
- 'border-right-width': box.right,
- 'border-bottom-width': box.bottom,
- 'border-left-width': box.left
- });
- }
- // Add instance to lookup
- var root = self.getRoot();
- if (!root.controlIdLookup) {
- root.controlIdLookup = {};
- }
- root.controlIdLookup[self._id] = self;
- for (var key in self._aria) {
- self.aria(key, self._aria[key]);
- }
- self.fire('postrender', {}, false);
- },
- /**
- * Scrolls the current control into view.
- *
- * @method scrollIntoView
- * @param {String} align Alignment in view top|center|bottom.
- * @return {tinymce.ui.Control} Current control instance.
- */
- scrollIntoView: function(align) {
- function getOffset(elm, rootElm) {
- var x, y, parent = elm;
- x = y = 0;
- while (parent && parent != rootElm && parent.nodeType) {
- x += parent.offsetLeft || 0;
- y += parent.offsetTop || 0;
- parent = parent.offsetParent;
- }
- return {x: x, y: y};
- }
- var elm = this.getEl(), parentElm = elm.parentNode;
- var x, y, width, height, parentWidth, parentHeight;
- var pos = getOffset(elm, parentElm);
- x = pos.x;
- y = pos.y;
- width = elm.offsetWidth;
- height = elm.offsetHeight;
- parentWidth = parentElm.clientWidth;
- parentHeight = parentElm.clientHeight;
- if (align == "end") {
- x -= parentWidth - width;
- y -= parentHeight - height;
- } else if (align == "center") {
- x -= (parentWidth / 2) - (width / 2);
- y -= (parentHeight / 2) - (height / 2);
- }
- parentElm.scrollLeft = x;
- parentElm.scrollTop = y;
- return this;
- },
- /**
- * Binds pending DOM events.
- *
- * @private
- */
- bindPendingEvents: function() {
- var self = this, i, l, parents, eventRootCtrl, nativeEvents, name;
- function delegate(e) {
- var control = self.getParentCtrl(e.target);
- if (control) {
- control.fire(e.type, e);
- }
- }
- function mouseLeaveHandler() {
- var ctrl = eventRootCtrl._lastHoverCtrl;
- if (ctrl) {
- ctrl.fire("mouseleave", {target: ctrl.getEl()});
- ctrl.parents().each(function(ctrl) {
- ctrl.fire("mouseleave", {target: ctrl.getEl()});
- });
- eventRootCtrl._lastHoverCtrl = null;
- }
- }
- function mouseEnterHandler(e) {
- var ctrl = self.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
- // Over on a new control
- if (ctrl !== lastCtrl) {
- eventRootCtrl._lastHoverCtrl = ctrl;
- parents = ctrl.parents().toArray().reverse();
- parents.push(ctrl);
- if (lastCtrl) {
- lastParents = lastCtrl.parents().toArray().reverse();
- lastParents.push(lastCtrl);
- for (idx = 0; idx < lastParents.length; idx++) {
- if (parents[idx] !== lastParents[idx]) {
- break;
- }
- }
- for (i = lastParents.length - 1; i >= idx; i--) {
- lastCtrl = lastParents[i];
- lastCtrl.fire("mouseleave", {
- target : lastCtrl.getEl()
- });
- }
- }
- for (i = idx; i < parents.length; i++) {
- ctrl = parents[i];
- ctrl.fire("mouseenter", {
- target : ctrl.getEl()
- });
- }
- }
- }
- function fixWheelEvent(e) {
- e.preventDefault();
- if (e.type == "mousewheel") {
- e.deltaY = -1 / 40 * e.wheelDelta;
- if (e.wheelDeltaX) {
- e.deltaX = -1 / 40 * e.wheelDeltaX;
- }
- } else {
- e.deltaX = 0;
- e.deltaY = e.detail;
- }
- e = self.fire("wheel", e);
- }
- self._rendered = true;
- nativeEvents = self._nativeEvents;
- if (nativeEvents) {
- // Find event root element if it exists
- parents = self.parents().toArray();
- parents.unshift(self);
- for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
- eventRootCtrl = parents[i]._eventsRoot;
- }
- // Event root wasn't found the use the root control
- if (!eventRootCtrl) {
- eventRootCtrl = parents[parents.length - 1] || self;
- }
- // Set the eventsRoot property on children that didn't have it
- self._eventsRoot = eventRootCtrl;
- for (l = i, i = 0; i < l; i++) {
- parents[i]._eventsRoot = eventRootCtrl;
- }
- var eventRootDelegates = eventRootCtrl._delegates;
- if (!eventRootDelegates) {
- eventRootDelegates = eventRootCtrl._delegates = {};
- }
- // Bind native event delegates
- for (name in nativeEvents) {
- if (!nativeEvents) {
- return false;
- }
- if (name === "wheel" && !hasWheelEventSupport) {
- if (hasMouseWheelEventSupport) {
- DomUtils.on(self.getEl(), "mousewheel", fixWheelEvent);
- } else {
- DomUtils.on(self.getEl(), "DOMMouseScroll", fixWheelEvent);
- }
- continue;
- }
- // Special treatment for mousenter/mouseleave since these doesn't bubble
- if (name === "mouseenter" || name === "mouseleave") {
- // Fake mousenter/mouseleave
- if (!eventRootCtrl._hasMouseEnter) {
- DomUtils.on(eventRootCtrl.getEl(), "mouseleave", mouseLeaveHandler);
- DomUtils.on(eventRootCtrl.getEl(), "mouseover", mouseEnterHandler);
- eventRootCtrl._hasMouseEnter = 1;
- }
- } else if (!eventRootDelegates[name]) {
- DomUtils.on(eventRootCtrl.getEl(), name, delegate);
- eventRootDelegates[name] = true;
- }
- // Remove the event once it's bound
- nativeEvents[name] = false;
- }
- }
- },
- getRoot: function() {
- var ctrl = this, rootControl, parents = [];
- while (ctrl) {
- if (ctrl.rootControl) {
- rootControl = ctrl.rootControl;
- break;
- }
- parents.push(ctrl);
- rootControl = ctrl;
- ctrl = ctrl.parent();
- }
- if (!rootControl) {
- rootControl = this;
- }
- var i = parents.length;
- while (i--) {
- parents[i].rootControl = rootControl;
- }
- return rootControl;
- },
- /**
- * Reflows the current control and it's parents.
- * This should be used after you for example append children to the current control so
- * that the layout managers know that they need to reposition everything.
- *
- * @example
- * container.append({type: 'button', text: 'My button'}).reflow();
- *
- * @method reflow
- * @return {tinymce.ui.Control} Current control instance.
- */
- reflow: function() {
- this.repaint();
- return this;
- }
- /**
- * Sets/gets the parent container for the control.
- *
- * @method parent
- * @param {tinymce.ui.Container} parent Optional parent to set.
- * @return {tinymce.ui.Control} Parent control or the current control on a set action.
- */
- // parent: function(parent) {} -- Generated
- /**
- * Sets/gets the text for the control.
- *
- * @method text
- * @param {String} value Value to set to control.
- * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
- */
- // text: function(value) {} -- Generated
- /**
- * Sets/gets the width for the control.
- *
- * @method width
- * @param {Number} value Value to set to control.
- * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
- */
- // width: function(value) {} -- Generated
- /**
- * Sets/gets the height for the control.
- *
- * @method height
- * @param {Number} value Value to set to control.
- * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
- */
- // height: function(value) {} -- Generated
- /**
- * Sets/gets the disabled state on the control.
- *
- * @method disabled
- * @param {Boolean} state Value to set to control.
- * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
- */
- // disabled: function(state) {} -- Generated
- /**
- * Sets/gets the active for the control.
- *
- * @method active
- * @param {Boolean} state Value to set to control.
- * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
- */
- // active: function(state) {} -- Generated
- /**
- * Sets/gets the name for the control.
- *
- * @method name
- * @param {String} value Value to set to control.
- * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
- */
- // name: function(value) {} -- Generated
- /**
- * Sets/gets the title for the control.
- *
- * @method title
- * @param {String} value Value to set to control.
- * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
- */
- // title: function(value) {} -- Generated
- });
- return Control;
- });
|