PHP Classes

File: public/js/tinymce/src/ui/src/main/js/Control.js

Recommend this page to a friend!
  Classes of Abed Nego Ragil Putra   GoLavaCMS   public/js/tinymce/src/ui/src/main/js/Control.js   Download  
File: public/js/tinymce/src/ui/src/main/js/Control.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: GoLavaCMS
Publish content on Web pages with SEO support
Author: By
Last change:
Date: 6 years ago
Size: 38,495 bytes
 

Contents

Class file image Download
/** * Control.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * 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', [ 'global!document', 'tinymce.core.dom.DomQuery', 'tinymce.core.util.Class', 'tinymce.core.util.EventDispatcher', 'tinymce.core.util.Tools', 'tinymce.ui.BoxUtils', 'tinymce.ui.ClassList', 'tinymce.ui.Collection', 'tinymce.ui.data.ObservableObject', 'tinymce.ui.DomUtils', 'tinymce.ui.ReflowQueue' ], function (document, DomQuery, Class, EventDispatcher, Tools, BoxUtils, ClassList, Collection, ObservableObject, DomUtils, ReflowQueue) { "use strict"; var hasMouseWheelEventSupport = "onmousewheel" in document; var hasWheelEventSupport = false; var classPrefix = "mce-"; var Control, idCounter = 0; var proto = { Statics: { 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, defaultClasses; function applyClasses(classes) { var i; classes = classes.split(' '); for (i = 0; i < classes.length; i++) { self.classes.add(classes[i]); } } self.settings = settings = Tools.extend({}, self.Defaults, settings); // Initial states self._id = settings.id || ('mceu_' + (idCounter++)); self._aria = { role: settings.role }; self._elmCache = {}; self.$ = DomQuery; self.state = new ObservableObject({ visible: true, active: false, disabled: false, value: '' }); self.data = new ObservableObject(settings.data); self.classes = new ClassList(function () { if (self.state.get('rendered')) { self.getEl().className = this.toString(); } }); self.classes.prefix = self.classPrefix; // Setup classes classes = settings.classes; if (classes) { if (self.Defaults) { defaultClasses = self.Defaults.classes; if (defaultClasses && classes != defaultClasses) { applyClasses(defaultClasses); } } applyClasses(classes); } Tools.each('title text name visible disabled active value'.split(' '), function (name) { if (name in settings) { self[name](settings[name]); } }); self.on('click', function () { if (self.disabled()) { return false; } }); /** * Name/value object with settings for the current control. * * @field {Object} settings */ self.settings = settings; self.borderBox = BoxUtils.parseBox(settings.border); self.paddingBox = BoxUtils.parseBox(settings.padding); self.marginBox = BoxUtils.parseBox(settings.margin); if (settings.hidden) { self.hide(); } }, // Will generate getter/setter methods for these properties Properties: 'parent,name', /** * Returns the root element to render controls into. * * @method getContainerElm * @return {Element} HTML DOM element to render into. */ getContainerElm: function () { return DomUtils.getContainer(); }, /** * 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; }, /** * 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 || BoxUtils.measureBox(elm, 'border'); self.paddingBox = self.paddingBox || BoxUtils.measureBox(elm, 'padding'); self.marginBox = self.marginBox || BoxUtils.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, bodyElm, rect, borderBox; var borderW, borderH, lastRepaintRect, round, value; // 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) { value = round(rect.w - borderW); style.width = (value >= 0 ? value : 0) + 'px'; lastRepaintRect.w = rect.w; } if (rect.h !== lastRepaintRect.h) { value = round(rect.h - borderH); style.height = (value >= 0 ? value : 0) + 'px'; lastRepaintRect.h = rect.h; } // Update body if needed if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) { value = round(rect.innerW); bodyElm = self.getEl('body'); if (bodyElm) { bodyStyle = bodyElm.style; bodyStyle.width = (value >= 0 ? value : 0) + 'px'; } lastRepaintRect.innerW = rect.innerW; } if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) { value = round(rect.innerH); bodyElm = bodyElm || self.getEl('body'); if (bodyElm) { bodyStyle = bodyStyle || bodyElm.style; bodyStyle.height = (value >= 0 ? value : 0) + 'px'; } lastRepaintRect.innerH = rect.innerH; } self._lastRepaintRect = lastRepaintRect; self.fire('repaint', {}, false); }, /** * Updates the controls layout rect by re-measuing it. */ updateLayoutRect: function () { var self = this; self.parent()._lastRect = null; DomUtils.css(self.getEl(), { width: '', height: '' }); self._layoutRect = self._lastRepaintRect = self._lastLayoutRect = null; self.initLayoutRect(); }, /** * 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; function resolveCallbackName(name) { var callback, scope; if (typeof name != 'string') { return name; } return function (e) { if (!callback) { self.parentsAndSelf().each(function (ctrl) { var callbacks = ctrl.settings.callbacks; if (callbacks && (callback = callbacks[name])) { scope = ctrl; return false; } }); } if (!callback) { e.action = name; this.fire('execute', e); return; } return callback.call(scope, e); }; } getEventDispatcher(self).on(name, resolveCallbackName(callback)); 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 {tinymce.ui.Control} Current control object. */ off: function (name, callback) { getEventDispatcher(this).off(name, callback); return this; }, /** * 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 bubbling. Defaults to true. * @return {Object} Current arguments object. */ fire: function (name, args, bubble) { var self = this; args = args || {}; if (!args.control) { args.control = self; } args = getEventDispatcher(self).fire(name, args); // Bubble event up to parents if (bubble !== false && self.parent) { var parent = self.parent(); while (parent && !args.isPropagationStopped()) { parent.fire(name, args, false); parent = parent.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 getEventDispatcher(this).has(name); }, /** * 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]; }, /** * 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) { this.$el.html(html); return this; }, /** * Returns the control DOM element or sub element. * * @method getEl * @param {String} [suffix] Suffix to get element by. * @return {Element} HTML DOM element for the current control or it's children. */ getEl: function (suffix) { var id = suffix ? this._id + '-' + suffix : this._id; if (!this._elmCache[id]) { this._elmCache[id] = DomQuery('#' + id)[0]; } return this._elmCache[id]; }, /** * 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]; } self._aria[name] = value; if (self.state.get('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) { DomQuery(elm).off(); } var lookup = self.getRoot().controlIdLookup; if (lookup) { delete lookup[self._id]; } if (elm && elm.parentNode) { elm.parentNode.removeChild(elm); } self.state.set('rendered', false); self.state.destroy(); self.fire('remove'); 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) { DomQuery(elm).before(this.renderHtml()); this.postRender(); return this; }, /** * 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) { DomQuery(elm || this.getContainerElm()).append(this.renderHtml()); this.postRender(); return this; }, preRender: function () { }, render: function () { }, renderHtml: function () { return '<div id="' + this._id + '" class="' + this.classes + '"></div>'; }, /** * 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; self.$el = DomQuery(self.getEl()); self.state.set('rendered', true); // 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; } } } bindPendingEvents(self); if (settings.style) { elm = self.getEl(); if (elm) { elm.setAttribute('style', settings.style); elm.style.cssText = settings.style; } } if (self.settings.border) { box = self.borderBox; self.$el.css({ '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]); } if (self.state.get('visible') === false) { self.getEl().style.display = 'none'; } self.bindStates(); self.state.on('change:visible', function (e) { var state = e.value, parentCtrl; if (self.state.get('rendered')) { self.getEl().style.display = state === false ? 'none' : ''; // Need to force a reflow here on IE 8 self.getEl().getBoundingClientRect(); } // Parent container needs to reflow parentCtrl = self.parent(); if (parentCtrl) { parentCtrl._lastRect = null; } self.fire(state ? 'show' : 'hide'); ReflowQueue.add(self); }); self.fire('postrender', {}, false); }, bindStates: function () { }, /** * 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; }, 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 () { ReflowQueue.remove(this); var parent = this.parent(); if (parent && parent._layout && !parent._layout.isNative()) { parent.reflow(); } 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 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 /** * 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(value) {} -- Generated }; /** * Setup state properties. */ Tools.each('text title visible disabled active value'.split(' '), function (name) { proto[name] = function (value) { if (arguments.length === 0) { return this.state.get(name); } if (typeof value != "undefined") { this.state.set(name, value); } return this; }; }); Control = Class.extend(proto); function getEventDispatcher(obj) { if (!obj._eventDispatcher) { obj._eventDispatcher = new EventDispatcher({ scope: obj, toggleEvent: function (name, state) { if (state && EventDispatcher.isNative(name)) { if (!obj._nativeEvents) { obj._nativeEvents = {}; } obj._nativeEvents[name] = true; if (obj.state.get('rendered')) { bindPendingEvents(obj); } } } }); } return obj._eventDispatcher; } function bindPendingEvents(eventCtrl) { var i, l, parents, eventRootCtrl, nativeEvents, name; function delegate(e) { var control = eventCtrl.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 = eventCtrl.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 = eventCtrl.fire("wheel", e); } nativeEvents = eventCtrl._nativeEvents; if (nativeEvents) { // Find event root element if it exists parents = eventCtrl.parents().toArray(); parents.unshift(eventCtrl); 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] || eventCtrl; } // Set the eventsRoot property on children that didn't have it eventCtrl._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) { DomQuery(eventCtrl.getEl()).on("mousewheel", fixWheelEvent); } else { DomQuery(eventCtrl.getEl()).on("DOMMouseScroll", fixWheelEvent); } continue; } // Special treatment for mousenter/mouseleave since these doesn't bubble if (name === "mouseenter" || name === "mouseleave") { // Fake mousenter/mouseleave if (!eventRootCtrl._hasMouseEnter) { DomQuery(eventRootCtrl.getEl()).on("mouseleave", mouseLeaveHandler).on("mouseover", mouseEnterHandler); eventRootCtrl._hasMouseEnter = 1; } } else if (!eventRootDelegates[name]) { DomQuery(eventRootCtrl.getEl()).on(name, delegate); eventRootDelegates[name] = true; } // Remove the event once it's bound nativeEvents[name] = false; } } } return Control; } );