/** * @class Ext.dom.Element */ Ext.define('Ext.overrides.dom.Element', (function() { var Element, // we cannot do this yet "= Ext.dom.Element" WIN = window, DOC = document, HIDDEN = 'hidden', ISCLIPPED = 'isClipped', OVERFLOW = 'overflow', OVERFLOWX = 'overflow-x', OVERFLOWY = 'overflow-y', ORIGINALCLIP = 'originalClip', HEIGHT = 'height', WIDTH = 'width', VISIBILITY = 'visibility', DISPLAY = 'display', NONE = 'none', OFFSETS = 'offsets', ORIGINALDISPLAY = 'originalDisplay', VISMODE = 'visibilityMode', ISVISIBLE = 'isVisible', OFFSETCLASS = Ext.baseCSSPrefix + 'hidden-offsets', boxMarkup = [ '', '', '' ].join(''), scriptTagRe = /(?:]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig, replaceScriptTagRe = /(?:)((\n|\r|.)*?)(?:<\/script>)/ig, srcRe = /\ssrc=([\'\"])(.*?)\1/i, nonSpaceRe = /\S/, typeRe = /\stype=([\'\"])(.*?)\1/i, msRe = /^-ms-/, camelRe = /(-[a-z])/gi, camelReplaceFn = function(m, a) { return a.charAt(1).toUpperCase(); }, XMASKED = Ext.baseCSSPrefix + "masked", XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative", EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg", bodyRe = /^body/i, propertyCache = {}, getDisplay = function(el) { var data = el.getData(), display = data[ORIGINALDISPLAY]; if (display === undefined) { data[ORIGINALDISPLAY] = display = ''; } return display; }, getVisMode = function(el){ var data = el.getData(), visMode = data[VISMODE]; if (visMode === undefined) { data[VISMODE] = visMode = Element.VISIBILITY; } return visMode; }, emptyRange = DOC.createRange ? DOC.createRange() : null, inputTags = { INPUT: true, TEXTAREA: true }; // if (Ext.isIE8) { var removeNode = Ext.removeNode, // save a reference to the removeNode function defined in sencha-core garbageBin = DOC.createElement('div'), destroyQueue = [], // prevent memory leaks in IE8 // see http://social.msdn.microsoft.com/Forums/ie/en-US/c76967f0-dcf8-47d0-8984-8fe1282a94f5/ie-appendchildremovechild-memory-problem?forum=iewebdevelopment // This function is called to fully destroy an element on a timer so that code following the // remove call can still access the element. clearGarbage = Ext.Function.createBuffered(function() { var len = destroyQueue.length, i; for (i = 0; i < len; i++) { garbageBin.appendChild(destroyQueue[i]); } garbageBin.innerHTML = ''; destroyQueue.length = 0; }, 10); Ext.removeNode = function(node) { node = node.dom || node; removeNode(node); destroyQueue[destroyQueue.length] = node; // Will perform extra IE8 cleanup in 10 milliseconds // see http://social.msdn.microsoft.com/Forums/ie/en-US/c76967f0-dcf8-47d0-8984-8fe1282a94f5/ie-appendchildremovechild-memory-problem?forum=iewebdevelopment clearGarbage(); }; } // return { override: 'Ext.dom.Element', mixins: [ 'Ext.util.Animate' ], uses: [ 'Ext.dom.GarbageCollector', 'Ext.dom.Fly', 'Ext.event.publisher.MouseEnterLeave', 'Ext.fx.Manager', 'Ext.fx.Anim' ], skipGarbageCollection: false, _init: function (E) { Element = E; // now we can poke this into closure scope }, statics: { selectableCls: Ext.baseCSSPrefix + 'selectable', unselectableCls: Ext.baseCSSPrefix + 'unselectable', /** * tabIndex attribute name for DOM lookups; needed in IE8 because * it has a bug: dom.getAttribute('tabindex') will return null * while dom.getAttribute('tabIndex') will return the actual value. * IE9+ and all other browsers normalize attribute names to lowercase. * * @static * @private */ tabIndexAttributeName: Ext.isIE8 ? 'tabIndex' : 'tabindex', tabbableSelector: 'a[href],button,iframe,input,select,textarea,[tabindex],[contenteditable="true"]', // Anchor and link tags are special; they are only naturally focusable (and tabbable) // if they have href attribute, and tabbabledness is further platform/browser specific. // Thus we check it separately in the code. naturallyFocusableTags: { BUTTON: true, IFRAME: true, EMBED: true, INPUT: true, OBJECT: true, SELECT: true, TEXTAREA: true, HTML: Ext.isIE ? true : false }, // element is naturally tabbable only in IE8 and below naturallyTabbableTags: { BUTTON: true, IFRAME: true, INPUT: true, SELECT: true, TEXTAREA: true, OBJECT: Ext.isIE8m ? true : false }, tabbableSavedFlagAttribute: 'data-tabindexsaved', tabbableSavedAttribute: 'data-savedtabindex', normalize: function(prop) { if (prop === 'float') { prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat'; } // For '-ms-foo' we need msFoo return propertyCache[prop] || (propertyCache[prop] = prop.replace(msRe, 'ms-').replace(camelRe, camelReplaceFn)); }, getViewportHeight: function(){ return Ext.isIE9m ? DOC.documentElement.clientHeight : WIN.innerHeight; }, getViewportWidth: function() { return (!Ext.isStrict && !Ext.isOpera) ? document.body.clientWidth : Ext.isIE9m ? DOC.documentElement.clientWidth : WIN.innerWidth; } }, /** * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect) * @param {String} className The class to add * @param {Function} [testFn] A test function to execute before adding the class. The passed parameter * will be the Element instance. If this functions returns false, the class will not be added. * @param {Object} [scope] The scope to execute the testFn in. * @return {Ext.dom.Element} this */ addClsOnClick: function(className, testFn, scope) { var me = this, dom = me.dom, hasTest = Ext.isFunction(testFn); me.on("mousedown", function() { if (hasTest && testFn.call(scope || me, me) === false) { return false; } Ext.fly(dom).addCls(className); var d = Ext.getDoc(), fn = function() { Ext.fly(dom).removeCls(className); d.removeListener("mouseup", fn); }; d.on("mouseup", fn); }); return me; }, /** * Sets up event handlers to add and remove a css class when this element has the focus * @param {String} className The class to add * @param {Function} [testFn] A test function to execute before adding the class. The passed parameter * will be the Element instance. If this functions returns false, the class will not be added. * @param {Object} [scope] The scope to execute the testFn in. * @return {Ext.dom.Element} this */ addClsOnFocus: function(className, testFn, scope) { var me = this, dom = me.dom, hasTest = Ext.isFunction(testFn); me.on("focus", function() { if (hasTest && testFn.call(scope || me, me) === false) { return false; } Ext.fly(dom).addCls(className); }); me.on("blur", function() { Ext.fly(dom).removeCls(className); }); return me; }, /** * Sets up event handlers to add and remove a css class when the mouse is over this element * @param {String} className The class to add * @param {Function} [testFn] A test function to execute before adding the class. The passed parameter * will be the Element instance. If this functions returns false, the class will not be added. * @param {Object} [scope] The scope to execute the testFn in. * @return {Ext.dom.Element} this */ addClsOnOver: function(className, testFn, scope) { var me = this, dom = me.dom, hasTest = Ext.isFunction(testFn); me.hover( function() { if (hasTest && testFn.call(scope || me, me) === false) { return; } Ext.fly(dom).addCls(className); }, function() { Ext.fly(dom).removeCls(className); } ); return me; }, /** * Convenience method for constructing a KeyMap * @param {String/Number/Number[]/Object} key Either a string with the keys to listen for, the numeric key code, * array of key codes or an object with the following options: * @param {Number/Array} key.key * @param {Boolean} key.shift * @param {Boolean} key.ctrl * @param {Boolean} key.alt * @param {Function} fn The function to call * @param {Object} [scope] The scope (`this` reference) in which the specified function is executed. Defaults to this Element. * @return {Ext.util.KeyMap} The KeyMap created */ addKeyListener: function(key, fn, scope){ var config; if(typeof key !== 'object' || Ext.isArray(key)){ config = { target: this, key: key, fn: fn, scope: scope }; } else { config = { target: this, key : key.key, shift : key.shift, ctrl : key.ctrl, alt : key.alt, fn: fn, scope: scope }; } return new Ext.util.KeyMap(config); }, /** * Creates a KeyMap for this element * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details * @return {Ext.util.KeyMap} The KeyMap created */ addKeyMap: function(config) { return new Ext.util.KeyMap(Ext.apply({ target: this }, config)); }, /** * @private */ afterAnimate: function() { var shadow = this.shadow; if (shadow && !shadow.disabled && !shadow.animate) { shadow.show(); } }, /** * @private */ anchorAnimX: function(anchor) { var xName = (anchor === 'l') ? 'right' : 'left'; this.dom.style[xName] = '0px'; }, /** * @private * process the passed fx configuration. */ anim: function(config) { if (!Ext.isObject(config)) { return (config) ? {} : false; } var me = this, duration = config.duration || Ext.fx.Anim.prototype.duration, easing = config.easing || 'ease', animConfig; if (config.stopAnimation) { me.stopAnimation(); } Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id)); // Clear any 'paused' defaults. Ext.fx.Manager.setFxDefaults(me.id, { delay: 0 }); animConfig = { // Pass the DOM reference. That's tested first so will be converted to an Ext.fx.Target fastest. target: me.dom, remove: config.remove, alternate: config.alternate || false, duration: duration, easing: easing, callback: config.callback, listeners: config.listeners, iterations: config.iterations || 1, scope: config.scope, block: config.block, concurrent: config.concurrent, delay: config.delay || 0, paused: true, keyframes: config.keyframes, from: config.from || {}, to: Ext.apply({}, config) }; Ext.apply(animConfig.to, config.to); // Anim API properties - backward compat delete animConfig.to.to; delete animConfig.to.from; delete animConfig.to.remove; delete animConfig.to.alternate; delete animConfig.to.keyframes; delete animConfig.to.iterations; delete animConfig.to.listeners; delete animConfig.to.target; delete animConfig.to.paused; delete animConfig.to.callback; delete animConfig.to.scope; delete animConfig.to.duration; delete animConfig.to.easing; delete animConfig.to.concurrent; delete animConfig.to.block; delete animConfig.to.stopAnimation; delete animConfig.to.delay; return animConfig; }, /** * Performs custom animation on this Element. * * The following properties may be specified in `from`, `to`, and `keyframe` objects: * * - `x` - The page X position in pixels. * * - `y` - The page Y position in pixels * * - `left` - The element's CSS `left` value. Units must be supplied. * * - `top` - The element's CSS `top` value. Units must be supplied. * * - `width` - The element's CSS `width` value. Units must be supplied. * * - `height` - The element's CSS `height` value. Units must be supplied. * * - `scrollLeft` - The element's `scrollLeft` value. * * - `scrollTop` - The element's `scrollTop` value. * * - `opacity` - The element's `opacity` value. This must be a value between `0` and `1`. * * **Be aware** that animating an Element which is being used by an Ext Component without in some way informing the * Component about the changed element state will result in incorrect Component behaviour. This is because the * Component will be using the old state of the element. To avoid this problem, it is now possible to directly * animate certain properties of Components. * * @param {Object} config Configuration for {@link Ext.fx.Anim}. * Note that the {@link Ext.fx.Anim#to to} config is required. * @return {Ext.dom.Element} this */ animate: function(config) { var me = this, animId = me.dom.id || Ext.id(me.dom), listeners, anim, end; if (!Ext.fx.Manager.hasFxBlock(animId)) { // Bit of gymnastics here to ensure our internal listeners get bound first if (config.listeners) { listeners = config.listeners; delete config.listeners; } if (config.internalListeners) { config.listeners = config.internalListeners; delete config.internalListeners; } end = config.autoEnd; delete config.autoEnd; anim = new Ext.fx.Anim(me.anim(config)); anim.on({ afteranimate: 'afterAnimate', beforeanimate: 'beforeAnimate', scope: me, single: true }); if (listeners) { anim.on(listeners); } Ext.fx.Manager.queueFx(anim); if (end) { anim.jumpToEnd(); } } return me; }, /** * @private */ beforeAnimate: function() { var shadow = this.shadow; if (shadow && !shadow.disabled && !shadow.animate) { shadow.hide(); } }, /** * Wraps the specified element with a special 9 element markup/CSS block that renders by default as * a gray container with a gradient background, rounded corners and a 4-way shadow. * * This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button}, * {@link Ext.panel.Panel} when {@link Ext.panel.Panel#frame frame=true}, {@link Ext.window.Window}). * The markup is of this form: * *
*
*
* * Example usage: * * // Basic box wrap * Ext.get("foo").boxWrap(); * * // You can also add a custom class and use CSS inheritance rules to customize the box look. * // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example * // for how to create a custom box wrap style. * Ext.get("foo").boxWrap().addCls("x-box-blue"); * * @param {String} [class='x-box'] A base CSS class to apply to the containing wrapper element. * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work, * so if you supply an alternate base class, make sure you also supply all of the necessary rules. * @return {Ext.dom.Element} The outermost wrapping element of the created box structure. */ boxWrap: function(cls) { cls = cls || Ext.baseCSSPrefix + 'box'; var el = Ext.get(this.insertHtml("beforeBegin", "")); el.selectNode('.' + cls + '-mc').appendChild(this.dom); return el; }, /** * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes. * @param {Boolean} [forceReclean=false] By default the element keeps track if it has been cleaned already * so you can call this over and over. However, if you update the element and need to force a re-clean, you * can pass true. */ clean: function(forceReclean) { var me = this, dom = me.dom, data = me.getData(), n = dom.firstChild, ni = -1, nx; if (data.isCleaned && forceReclean !== true) { return me; } while (n) { nx = n.nextSibling; if (n.nodeType === 3) { // Remove empty/whitespace text nodes if (!(nonSpaceRe.test(n.nodeValue))) { dom.removeChild(n); // Combine adjacent text nodes } else if (nx && nx.nodeType === 3) { n.appendData(Ext.String.trim(nx.data)); dom.removeChild(nx); nx = n.nextSibling; n.nodeIndex = ++ni; } } else { // Recursively clean Ext.fly(n, '_clean').clean(); n.nodeIndex = ++ni; } n = nx; } data.isCleaned = true; return me; }, /** * Empties this element. Removes all child nodes. */ empty: emptyRange ? function() { var dom = this.dom; if (dom.firstChild) { emptyRange.setStartBefore(dom.firstChild); emptyRange.setEndAfter(dom.lastChild); emptyRange.deleteContents(); } } : function() { var dom = this.dom; while (dom.lastChild) { dom.removeChild(dom.lastChild); } }, clearListeners: function() { this.removeAnchor(); this.callParent(); }, /** * Clears positioning back to the default when the document was loaded. * @param {String} [value=''] The value to use for the left, right, top, bottom. * You could use 'auto'. * @return {Ext.dom.Element} this */ clearPositioning: function(value) { value = value || ''; return this.setStyle({ left : value, right : value, top : value, bottom : value, 'z-index' : '', position : 'static' }); }, /** * Creates a proxy element of this element * @param {String/Object} config The class name of the proxy element or a DomHelper config object * @param {String/HTMLElement} [renderTo] The element or element id to render the proxy to. Defaults to: document.body. * @param {Boolean} [matchBox=false] True to align and size the proxy to this element now. * @return {Ext.dom.Element} The new proxy element */ createProxy: function(config, renderTo, matchBox) { config = (typeof config === 'object') ? config : { tag: "div", role: 'presentation', cls: config }; var me = this, proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) : Ext.DomHelper.insertBefore(me.dom, config, true); proxy.setVisibilityMode(Element.DISPLAY); proxy.hide(); if (matchBox && me.setBox && me.getBox) { // check to make sure Element_position.js is loaded proxy.setBox(me.getBox()); } return proxy; }, /** * Clears any opacity settings from this element. Required in some cases for IE. * @return {Ext.dom.Element} this */ clearOpacity: function() { return this.setOpacity(''); }, /** * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove * @return {Ext.dom.Element} this */ clip: function() { var me = this, data = me.getData(), style; if (!data[ISCLIPPED]) { data[ISCLIPPED] = true; style = me.getStyle([OVERFLOW, OVERFLOWX, OVERFLOWY]); data[ORIGINALCLIP] = { o: style[OVERFLOW], x: style[OVERFLOWX], y: style[OVERFLOWY] }; me.setStyle(OVERFLOW, HIDDEN); me.setStyle(OVERFLOWX, HIDDEN); me.setStyle(OVERFLOWY, HIDDEN); } return me; }, destroy: function() { var me = this, dom = me.dom, data = me.getData(), maskEl, maskMsg; if (dom && me.isAnimate) { me.stopAnimation(); } me.callParent(); // // prevent memory leaks in IE8 // see http://social.msdn.microsoft.com/Forums/ie/en-US/c76967f0-dcf8-47d0-8984-8fe1282a94f5/ie-appendchildremovechild-memory-problem?forum=iewebdevelopment // must not be document, documentElement, body or window object // Have to use != instead of !== for IE8 or it will not recognize that the window // objects are equal if (dom && Ext.isIE8 && (dom.window != dom) && (dom.nodeType !== 9) && (dom.tagName !== 'BODY') && (dom.tagName !== 'HTML')) { destroyQueue[destroyQueue.length] = dom; // Will perform extra IE8 cleanup in 10 milliseconds // see http://social.msdn.microsoft.com/Forums/ie/en-US/c76967f0-dcf8-47d0-8984-8fe1282a94f5/ie-appendchildremovechild-memory-problem?forum=iewebdevelopment clearGarbage(); } // if (data) { maskEl = data.maskEl; maskMsg = data.maskMsg; if (maskEl) { maskEl.destroy(); } if (maskMsg) { maskMsg.destroy(); } } }, /** * Convenience method for setVisibilityMode(Element.DISPLAY). * @param {String} [display] What to set display to when visible * @return {Ext.dom.Element} this */ enableDisplayMode : function(display) { var me = this; me.setVisibilityMode(Element.DISPLAY); if (display !== undefined) { me.getData()[ORIGINALDISPLAY] = display; } return me; }, /** * Fade an element in (from transparent to opaque). The ending opacity can be specified using the `opacity` * config option. Usage: * * // default: fade in from opacity 0 to 100% * el.fadeIn(); * * // custom: fade in from opacity 0 to 75% over 2 seconds * el.fadeIn({ opacity: .75, duration: 2000}); * * // common config options shown with default values * el.fadeIn({ * opacity: 1, //can be any value between 0 and 1 (e.g. .5) * easing: 'easeOut', * duration: 500 * }); * * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ fadeIn: function(o) { var me = this, dom = me.dom; me.animate(Ext.apply({}, o, { opacity: 1, internalListeners: { beforeanimate: function(anim){ // restore any visibility/display that may have // been applied by a fadeout animation var el = Ext.fly(dom, '_anim'); if (el.isStyle('display', 'none')) { el.setDisplayed(''); } else { el.show(); } } } })); return this; }, /** * Fade an element out (from opaque to transparent). The ending opacity can be specified using the `opacity` * config option. Note that IE may require `useDisplay:true` in order to redisplay correctly. * Usage: * * // default: fade out from the element's current opacity to 0 * el.fadeOut(); * * // custom: fade out from the element's current opacity to 25% over 2 seconds * el.fadeOut({ opacity: .25, duration: 2000}); * * // common config options shown with default values * el.fadeOut({ * opacity: 0, //can be any value between 0 and 1 (e.g. .5) * easing: 'easeOut', * duration: 500, * remove: false, * useDisplay: false * }); * * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ fadeOut: function(o) { var me = this, dom = me.dom; o = Ext.apply({ opacity: 0, internalListeners: { afteranimate: function(anim){ if (dom && anim.to.opacity === 0) { var el = Ext.fly(dom, '_anim'); if (o.useDisplay) { el.setDisplayed(false); } else { el.hide(); } } } } }, o); me.animate(o); return me; }, /** * @private */ fixDisplay: function(){ var me = this; if (me.isStyle(DISPLAY, NONE)) { me.setStyle(VISIBILITY, HIDDEN); me.setStyle(DISPLAY, getDisplay(me)); // first try reverting to default if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block me.setStyle(DISPLAY, "block"); } } }, /** * Shows a ripple of exploding, attenuating borders to draw attention to an Element. Usage: * * // default: a single light blue ripple * el.frame(); * * // custom: 3 red ripples lasting 3 seconds total * el.frame("#ff0000", 3, { duration: 3000 }); * * // common config options shown with default values * el.frame("#C3DAF9", 1, { * duration: 1000 // duration of each individual ripple. * // Note: Easing is not configurable and will be ignored if included * }); * * @param {String} [color='#C3DAF9'] The hex color value for the border. * @param {Number} [count=1] The number of ripples to display. * @param {Object} [options] Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ frame: function(color, count, obj){ var me = this, dom = me.dom, beforeAnim; color = color || '#C3DAF9'; count = count || 1; obj = obj || {}; beforeAnim = function() { var el = Ext.fly(dom, '_anim'), animScope = this, box, proxy, proxyAnim; el.show(); box = el.getBox(); proxy = Ext.getBody().createChild({ role: 'presentation', id: el.dom.id + '-anim-proxy', style: { position : 'absolute', 'pointer-events': 'none', 'z-index': 35000, border : '0px solid ' + color } }); proxyAnim = new Ext.fx.Anim({ target: proxy, duration: obj.duration || 1000, iterations: count, from: { top: box.y, left: box.x, borderWidth: 0, opacity: 1, height: box.height, width: box.width }, to: { top: box.y - 20, left: box.x - 20, borderWidth: 10, opacity: 0, height: box.height + 40, width: box.width + 40 } }); proxyAnim.on('afteranimate', function() { proxy.destroy(); // kill the no-op element animation created below animScope.end(); }); }; me.animate({ // See "A Note About Wrapped Animations" at the top of this class: duration: (Math.max(obj.duration, 500) * 2) || 2000, listeners: { beforeanimate: { fn: beforeAnim } }, callback: obj.callback, scope: obj.scope }); return me; }, /** * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like `#fff`) * and valid values are convert to standard 6 digit hex color. * @param {String} attr The css attribute * @param {String} defaultValue The default value to use when a valid color isn't found * @param {String} [prefix] defaults to #. Use an empty string when working with * color anims. * @private */ getColor: function(attr, defaultValue, prefix) { var v = this.getStyle(attr), color = prefix || prefix === '' ? prefix : '#', h, len, i=0; if (!v || (/transparent|inherit/.test(v))) { return defaultValue; } if (/^r/.test(v)) { v = v.slice(4, v.length - 1).split(','); len = v.length; for (; i 5 ? color.toLowerCase() : defaultValue); }, /** * Gets this element's {@link Ext.ElementLoader ElementLoader} * @return {Ext.ElementLoader} The loader */ getLoader: function() { var me = this, data = me.getData(), loader = data.loader; if (!loader) { data.loader = loader = new Ext.ElementLoader({ target: me }); } return loader; }, /** * Gets an object with all CSS positioning properties. Useful along with * #setPostioning to get snapshot before performing an update and then restoring * the element. * @param {Boolean} [autoPx=false] true to return pixel values for "auto" styles. * @return {Object} */ getPositioning: function(autoPx){ var styles = this.getStyle(['left', 'top', 'position', 'z-index']), dom = this.dom; if(autoPx) { if(styles.left === 'auto') { styles.left = dom.offsetLeft + 'px'; } if(styles.top === 'auto') { styles.top = dom.offsetTop + 'px'; } } return styles; }, /** * Slides the element while fading it out of view. An anchor point can be optionally passed to set the ending point * of the effect. Usage: * * // default: slide the element downward while fading out * el.ghost(); * * // custom: slide the element out to the right with a 2-second duration * el.ghost('r', { duration: 2000 }); * * // common config options shown with default values * el.ghost('b', { * easing: 'easeOut', * duration: 500 * }); * * @param {String} anchor (optional) One of the valid {@link Ext.fx.Anim} anchor positions (defaults to bottom: 'b') * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ ghost: function(anchor, obj) { var me = this, dom = me.dom, beforeAnim; anchor = anchor || "b"; beforeAnim = function() { var el = Ext.fly(dom, '_anim'), width = el.getWidth(), height = el.getHeight(), xy = el.getXY(), position = el.getPositioning(), to = { opacity: 0 }; switch (anchor) { case 't': to.y = xy[1] - height; break; case 'l': to.x = xy[0] - width; break; case 'r': to.x = xy[0] + width; break; case 'b': to.y = xy[1] + height; break; case 'tl': to.x = xy[0] - width; to.y = xy[1] - height; break; case 'bl': to.x = xy[0] - width; to.y = xy[1] + height; break; case 'br': to.x = xy[0] + width; to.y = xy[1] + height; break; case 'tr': to.x = xy[0] + width; to.y = xy[1] - height; break; } this.to = to; this.on('afteranimate', function () { var el = Ext.fly(dom, '_anim'); if (el) { el.hide(); el.clearOpacity(); el.setPositioning(position); } }); }; me.animate(Ext.applyIf(obj || {}, { duration: 500, easing: 'ease-out', listeners: { beforeanimate: beforeAnim } })); return me; }, /** * @override * Hide this element - Uses display mode to determine whether to use "display", * "visibility", or "offsets". See {@link #setVisible}. * @param {Boolean/Object} [animate] true for the default animation or a standard * Element animation config object * @return {Ext.dom.Element} this */ hide: function(animate){ // hideMode override if (typeof animate === 'string'){ this.setVisible(false, animate); return this; } this.setVisible(false, this.anim(animate)); return this; }, /** * Highlights the Element by setting a color (applies to the background-color by default, but can be changed using * the "attr" config option) and then fading back to the original color. If no original color is available, you * should provide the "endColor" config option which will be cleared after the animation. Usage: * * // default: highlight background to yellow * el.highlight(); * * // custom: highlight foreground text to blue for 2 seconds * el.highlight("0000ff", { attr: 'color', duration: 2000 }); * * // common config options shown with default values * el.highlight("ffff9c", { * attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value * endColor: (current color) or "ffffff", * easing: 'easeIn', * duration: 1000 * }); * * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # * (defaults to yellow: 'ffff9c') * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ highlight: function(color, o) { var me = this, dom = me.dom, from = {}, restore, to, attr, lns, event, fn; o = o || {}; lns = o.listeners || {}; attr = o.attr || 'backgroundColor'; from[attr] = color || 'ffff9c'; if (!o.to) { to = {}; to[attr] = o.endColor || me.getColor(attr, 'ffffff', ''); } else { to = o.to; } // Don't apply directly on lns, since we reference it in our own callbacks below o.listeners = Ext.apply(Ext.apply({}, lns), { beforeanimate: function() { restore = dom.style[attr]; var el = Ext.fly(dom, '_anim'); el.clearOpacity(); el.show(); event = lns.beforeanimate; if (event) { fn = event.fn || event; return fn.apply(event.scope || lns.scope || WIN, arguments); } }, afteranimate: function() { if (dom) { dom.style[attr] = restore; } event = lns.afteranimate; if (event) { fn = event.fn || event; fn.apply(event.scope || lns.scope || WIN, arguments); } } }); me.animate(Ext.apply({}, o, { duration: 1000, easing: 'ease-in', from: from, to: to })); return me; }, /** * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element. * @param {Function} overFn The function to call when the mouse enters the Element. * @param {Function} outFn The function to call when the mouse leaves the Element. * @param {Object} [scope] The scope (`this` reference) in which the functions are executed. Defaults * to the Element's DOM element. * @param {Object} [options] Options for the listener. See {@link Ext.util.Observable#addListener the * options parameter}. * @return {Ext.dom.Element} this */ hover: function(overFn, outFn, scope, options) { var me = this; me.on('mouseenter', overFn, scope || me.dom, options); me.on('mouseleave', outFn, scope || me.dom, options); return me; }, /** * Initializes a {@link Ext.dd.DD} drag drop object for this element. * @param {String} group The group the DD object is member of * @param {Object} config The DD config object * @param {Object} overrides An object containing methods to override/implement on the DD object * @return {Ext.dd.DD} The DD object */ initDD: function(group, config, overrides){ var dd = new Ext.dd.DD(Ext.id(this.dom), group, config); return Ext.apply(dd, overrides); }, /** * Initializes a {@link Ext.dd.DDProxy} object for this element. * @param {String} group The group the DDProxy object is member of * @param {Object} config The DDProxy config object * @param {Object} overrides An object containing methods to override/implement on the DDProxy object * @return {Ext.dd.DDProxy} The DDProxy object */ initDDProxy: function(group, config, overrides){ var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config); return Ext.apply(dd, overrides); }, /** * Initializes a {@link Ext.dd.DDTarget} object for this element. * @param {String} group The group the DDTarget object is member of * @param {Object} config The DDTarget config object * @param {Object} overrides An object containing methods to override/implement on the DDTarget object * @return {Ext.dd.DDTarget} The DDTarget object */ initDDTarget: function(group, config, overrides){ var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config); return Ext.apply(dd, overrides); }, /** * Checks whether this element can be focused programmatically or by clicking. * To check if an element is in the document tab flow, use {@link #isTabbable}. * * @return {Boolean} True if the element is focusable */ isFocusable: function() { var dom = this.dom, focusable = false, nodeName; if (dom && !dom.disabled) { nodeName = dom.nodeName; /* * An element is focusable if: * - It is naturally focusable, or * - It is an anchor or link with href attribute, or * - It has a tabIndex, or * - It is an editing host (contenteditable="true") * * Also note that we can't check dom.tabIndex because IE will return 0 * for elements that have no tabIndex attribute defined, regardless of * whether they are naturally focusable or not. */ focusable = !!Ext.Element.naturallyFocusableTags[nodeName] || ((nodeName === 'A' || nodeName === 'LINK') && !!dom.href) || dom.getAttribute('tabindex') != null || dom.contentEditable === 'true'; // In IE8, does not have a corresponding style // so isVisible() will assume that it's not hidden. if (Ext.isIE8 && nodeName === 'INPUT' && dom.type === 'hidden') { focusable = false; } // Invisible elements cannot be focused, so check that as well focusable = focusable && this.isVisible(true); } return focusable; }, /** * Returns `true` if this Element is an input field, or is editable in any way. * @return {Boolean} `true` if this Element is an input field, or is editable in any way. */ isInputField: function() { var dom = this.dom, contentEditable = dom.contentEditable; // contentEditable will default to inherit if not specified, only check if the // attribute has been set or explicitly set to true // http://html5doctor.com/the-contenteditable-attribute/ // Also skip tags of type="button", we use them for checkboxes // and radio buttons if ((inputTags[dom.tagName] && dom.type !== 'button') || (contentEditable === '' || contentEditable === 'true')) { return true; } return false; }, /** * Checks whether this element participates in the sequential focus navigation, * and can be reached by using Tab key. * * @return {Boolean} True if the element is tabbable. */ isTabbable: function() { var dom = this.dom, tabbable = false, nodeName, hasIndex, tabIndex; if (dom && !dom.disabled) { nodeName = dom.nodeName; // Can't use dom.tabIndex here because IE will return 0 for elements // that have no tabindex attribute defined, regardless of whether they are // naturally tabbable or not. tabIndex = dom.getAttribute('tabindex'); hasIndex = tabIndex != null; tabIndex -= 0; // Anchors and links are only naturally tabbable if they have href attribute // See http://www.w3.org/TR/html5/editing.html#specially-focusable if (nodeName === 'A' || nodeName === 'LINK') { if (dom.href) { // It is also possible to make an anchor untabbable by setting // tabIndex < 0 on it tabbable = hasIndex && tabIndex < 0 ? false : true; } // Anchor w/o href is tabbable if it has tabIndex >= 0, // or if it's editable else { if (dom.contentEditable === 'true') { tabbable = !hasIndex || (hasIndex && tabIndex >= 0) ? true : false; } else { tabbable = hasIndex && tabIndex >= 0 ? true : false; } } } // If an element has contenteditable="true" or is naturally tabbable, // then it is a potential candidate unless its tabIndex is < 0. else if (dom.contentEditable === 'true' || Ext.Element.naturallyTabbableTags[nodeName]) { tabbable = hasIndex && tabIndex < 0 ? false : true; } // That leaves non-editable elements that can only be made tabbable // by slapping tabIndex >= 0 on them else { if (hasIndex && tabIndex >= 0) { tabbable = true; } } // In IE8, does not have a corresponding style // so isVisible() will assume that it's not hidden. if (Ext.isIE8 && nodeName === 'INPUT' && dom.type === 'hidden') { tabbable = false; } // Invisible elements can't be tabbed into. If we have a component ref // we'll also check if the component itself is visible before incurring // the expense of DOM style reads. tabbable = tabbable && (!this.component || this.component.isVisible(true)) && this.isVisible(true); } return tabbable; }, /** * Returns true if this element is masked. Also re-centers any displayed message * within the mask. * * @param {Boolean} [deep] Go up the DOM hierarchy to determine if any parent * element is masked. * * @return {Boolean} */ isMasked: function(deep) { var me = this, data = me.getData(), maskEl = data.maskEl, maskMsg = data.maskMsg, hasMask = false, parent; if (maskEl && maskEl.isVisible()) { if (maskMsg) { maskMsg.center(me); } hasMask = true; } else if (deep) { parent = me.findParentNode(); if (parent) { return Ext.fly(parent).isMasked(deep); } } return hasMask; }, /** * Returns true if this element is scrollable. * @return {Boolean} */ isScrollable: function() { var dom = this.dom; return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth; }, /** * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#method-load} method. * The method takes the same object parameter as {@link Ext.ElementLoader#method-load} * @param {Object} options a options object for Ext.ElementLoader {@link Ext.ElementLoader#method-load} * @return {Ext.dom.Element} this */ load: function(options) { this.getLoader().load(options); return this; }, /** * Puts a mask over this element to disable user interaction. * This method can only be applied to elements which accept child nodes. Use * {@link #unmask} to remove the mask. * * @param {String} [msg] A message to display in the mask * @param {String} [msgCls] A css class to apply to the msg element * @return {Ext.dom.Element} The mask element */ mask: function (msg, msgCls /* private - passed by AbstractComponent.mask to avoid the need to interrogate the DOM to get the height*/, elHeight) { var me = this, dom = me.dom, data = me.getData(), maskEl = data.maskEl, maskMsg; if (!(bodyRe.test(dom.tagName) && me.getStyle('position') === 'static')) { me.addCls(XMASKEDRELATIVE); } // We always needs to recreate the mask since the DOM element may have been re-created if (maskEl) { maskEl.destroy(); } maskEl = Ext.DomHelper.append(dom, { role: 'presentation', cls : Ext.baseCSSPrefix + "mask " + Ext.baseCSSPrefix + "border-box", children: { role: 'presentation', cls : msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG, cn : { tag: 'div', role: 'presentation', cls: Ext.baseCSSPrefix + 'mask-msg-inner', cn: { tag: 'div', role: 'presentation', cls: Ext.baseCSSPrefix + 'mask-msg-text', html: msg || '' } } } }, true); maskMsg = Ext.get(maskEl.dom.firstChild); data.maskEl = maskEl; me.addCls(XMASKED); maskEl.setDisplayed(true); if (typeof msg === 'string') { maskMsg.setDisplayed(true); maskMsg.center(me); } else { maskMsg.setDisplayed(false); } // When masking the body, don't touch its tabbable state if (dom === DOC.body) { maskEl.addCls(Ext.baseCSSPrefix + 'mask-fixed'); } else { me.saveTabbableState(); } me.saveChildrenTabbableState(); // ie will not expand full height automatically if (Ext.isIE9m && dom !== DOC.body && me.isStyle('height', 'auto')) { maskEl.setSize(undefined, elHeight || me.getHeight()); } return maskEl; }, /** * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if * the mouse was not moved back into the Element within the delay. If the mouse *was* moved * back in, the function is not called. * @param {Number} delay The delay **in milliseconds** to wait for possible mouse re-entry before calling the handler function. * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time. * @param {Object} [scope] The scope (`this` reference) in which the handler function executes. Defaults to this Element. * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage: * * // Hide the menu if the mouse moves out for 250ms or more * this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this); * * ... * // Remove mouseleave monitor on menu destroy * this.menuEl.un(this.mouseLeaveMonitor); * */ monitorMouseLeave: function(delay, handler, scope) { var me = this, timer, listeners = { mouseleave: function(e) { // if (Ext.isIE9m) { e.enableIEAsync(); } // timer = Ext.defer(handler, delay, scope || me, [e]); }, mouseenter: function() { clearTimeout(timer); } }; me.on(listeners); return listeners; }, /** * Fades the element out while slowly expanding it in all directions. When the effect is completed, the element will * be hidden (visibility = 'hidden') but block elements will still take up space in the document. Usage: * * // default * el.puff(); * * // common config options shown with default values * el.puff({ * easing: 'easeOut', * duration: 500, * useDisplay: false * }); * * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ puff: function(obj) { var me = this, dom = me.dom, beforeAnim, box = me.getBox(), originalStyles = me.getStyle(['width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index', 'font-size', 'opacity'], true); obj = Ext.applyIf(obj || {}, { easing: 'ease-out', duration: 500, useDisplay: false }); beforeAnim = function() { var el = Ext.fly(dom, '_anim'); el.clearOpacity(); el.show(); this.to = { width: box.width * 2, height: box.height * 2, x: box.x - (box.width / 2), y: box.y - (box.height /2), opacity: 0, fontSize: '200%' }; this.on('afteranimate',function() { var el = Ext.fly(dom, '_anim'); if (el) { if (obj.useDisplay) { el.setDisplayed(false); } else { el.hide(); } el.setStyle(originalStyles); Ext.callback(obj.callback, obj.scope); } }); }; me.animate({ duration: obj.duration, easing: obj.easing, listeners: { beforeanimate: { fn: beforeAnim } } }); return me; }, /** * Enable text selection for this element (normalized across browsers) * @return {Ext.dom.Element} this */ selectable: function() { var me = this; // We clear this property for all browsers, not just Opera. This is so that rendering templates don't need to // condition on Opera when making elements unselectable. me.dom.unselectable = ''; me.removeCls(Element.unselectableCls); me.addCls(Element.selectableCls); return me; }, // // private // used to ensure the mouseup event is captured if it occurs outside of the // window in IE9m. The only reason this method exists, (vs just calling // el.dom.setCapture() directly) is so that we can override it to emptyFn // during testing because setCapture() can wreak havoc on emulated mouse events // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646262(v=vs.85).aspx setCapture: function() { var dom = this.dom; if (Ext.isIE9m && dom.setCapture) { dom.setCapture(); } }, // /** * Sets the CSS display property. Uses originalDisplay if the specified value is a * boolean true. * @param {Boolean/String} value Boolean value to display the element using its * default display, or a string to set the display directly. * @return {Ext.dom.Element} this */ setDisplayed: function(value) { var me = this; if (typeof value === "boolean"){ value = value ? getDisplay(me) : NONE; } me.setStyle(DISPLAY, value); if (me.shadow || me.shim) { me.setUnderlaysVisible(value !== NONE); } return me; }, /** * Set the height of this Element. * * // change the height to 200px and animate with default configuration * Ext.fly('elementId').setHeight(200, true); * * // change the height to 150px and animate with a custom configuration * Ext.fly('elId').setHeight(150, { * duration : 500, // animation will have a duration of .5 seconds * // will change the content to "finished" * callback: function(){ this.{@link #setHtml}("finished"); } * }); * * @param {Number/String} height The new height. This may be one of: * * - A Number specifying the new height in pixels. * - A String used to set the CSS height style. Animation may **not** be used. * * @param {Boolean/Object} [animate] a standard Element animation config object or `true` for * the default animation (`{duration: 350, easing: 'ease-in'}`) * @return {Ext.dom.Element} this */ setHeight: function(height, animate) { var me = this; if (!animate || !me.anim) { me.callParent(arguments); } else { if (!Ext.isObject(animate)) { animate = {}; } me.animate(Ext.applyIf({ to: { height: height } }, animate)); } return me; }, /** * Removes "vertical" state from this element (reverses everything done * by {@link #setVertical}). * @private */ setHorizontal: function() { var me = this, cls = me.verticalCls; delete me.vertical; if (cls) { delete me.verticalCls; me.removeCls(cls); } // delete the inverted methods and revert to inheriting from the prototype delete me.setWidth; delete me.setHeight; if (!Ext.isIE8) { delete me.getWidth; delete me.getHeight; } // revert to inheriting styleHooks from the prototype delete me.styleHooks; }, /** * Updates the *text* value of this element. * Replaces the content of this element with a *single text node* containing the passed text. * @param {String} text The text to display in this Element. */ updateText: function(text) { var me = this, dom, textNode; if (dom) { textNode = dom.firstChild; if (!textNode || (textNode.nodeType !== 3 || textNode.nextSibling)) { textNode = DOC.createTextNode(); me.empty(); dom.appendChild(textNode); } if (text) { textNode.data = text; } } }, /** * Updates the innerHTML of this element, optionally searching for and processing scripts. * @param {String} html The new HTML * @param {Boolean} [loadScripts] True to look for and process scripts (defaults to false) * @param {Function} [callback] For async script loading you can be notified when the update completes * @return {Ext.dom.Element} this */ setHtml: function(html, loadScripts, callback) { var me = this, id, dom, interval; if (!me.dom) { return me; } html = html || ''; dom = me.dom; if (loadScripts !== true) { dom.innerHTML = html; Ext.callback(callback, me); return me; } id = Ext.id(); html += ''; interval = Ext.interval(function() { var hd, match, attrs, srcMatch, typeMatch, el, s; if (!(el = DOC.getElementById(id))) { return false; } clearInterval(interval); Ext.removeNode(el); hd = Ext.getHead().dom; while ((match = scriptTagRe.exec(html))) { attrs = match[1]; srcMatch = attrs ? attrs.match(srcRe) : false; if (srcMatch && srcMatch[2]) { s = DOC.createElement("script"); s.src = srcMatch[2]; typeMatch = attrs.match(typeRe); if (typeMatch && typeMatch[2]) { s.type = typeMatch[2]; } hd.appendChild(s); } else if (match[2] && match[2].length > 0) { (WIN.execScript || WIN.eval)(match[2]); // jshint ignore:line } } Ext.callback(callback, me); }, 20); dom.innerHTML = html.replace(replaceScriptTagRe, ''); return me; }, /** * Set the opacity of the element * @param {Number} opacity The new opacity. 0 = transparent, .5 = 50% visible, 1 = fully visible, etc * @param {Boolean/Object} [animate] a standard Element animation config object or `true` for * the default animation (`{duration: 350, easing: 'ease-in'}`) * @return {Ext.dom.Element} this */ setOpacity: function(opacity, animate) { var me = this; if (!me.dom) { return me; } if (!animate || !me.anim) { me.setStyle('opacity', opacity); } else { if (typeof animate != 'object') { animate = { duration: 350, easing: 'ease-in' }; } me.animate(Ext.applyIf({ to: { opacity: opacity } }, animate)); } return me; }, /** * Set positioning with an object returned by #getPositioning. * @param {Object} posCfg * @return {Ext.dom.Element} this */ setPositioning: function(pc) { return this.setStyle(pc); }, /** * Changes this Element's state to "vertical" (rotated 90 or 270 degrees). * This involves inverting the getters and setters for height and width, * and applying hooks for rotating getters and setters for border/margin/padding. * (getWidth becomes getHeight and vice versa), setStyle and getStyle will * also return the inverse when height or width are being operated on. * * @param {Number} angle the angle of rotation - either 90 or 270 * @param {String} cls an optional css class that contains the required * styles for switching the element to vertical orientation. Omit this if * the element already contains vertical styling. If cls is provided, * it will be removed from the element when {@link #setHorizontal} is called. * @private */ setVertical: function(angle, cls) { var me = this, proto = Element.prototype; me.vertical = true; if (cls) { me.addCls(me.verticalCls = cls); } me.setWidth = proto.setHeight; me.setHeight = proto.setWidth; if (!Ext.isIE8) { // In browsers that use CSS3 transforms we must invert getHeight and // get Width. In IE8 no adjustment is needed because we use // a BasicImage filter to rotate the element and the element's // offsetWidth and offsetHeight are automatically inverted. me.getWidth = proto.getHeight; me.getHeight = proto.getWidth; } // Switch to using the appropriate vertical style hooks me.styleHooks = (angle === 270) ? proto.verticalStyleHooks270 : proto.verticalStyleHooks90; }, /** * Set the size of this Element. If animation is true, both width and height will be animated concurrently. * @param {Number/String} width The new width. This may be one of: * * - A Number specifying the new width in pixels. * - A String used to set the CSS width style. Animation may **not** be used. * - A size object in the format `{width: widthValue, height: heightValue}`. * * @param {Number/String} height The new height. This may be one of: * * - A Number specifying the new height in pixels. * - A String used to set the CSS height style. Animation may **not** be used. * * @param {Boolean/Object} [animate] a standard Element animation config object or `true` for * the default animation (`{duration: 350, easing: 'ease-in'}`) * * @return {Ext.dom.Element} this */ setSize: function(width, height, animate) { var me = this; if (Ext.isObject(width)) { // in case of object from getSize() animate = height; height = width.height; width = width.width; } if (!animate || !me.anim) { me.dom.style.width = Element.addUnits(width); me.dom.style.height = Element.addUnits(height); if (me.shadow || me.shim) { me.syncUnderlays(); } } else { if (animate === true) { animate = {}; } me.animate(Ext.applyIf({ to: { width: width, height: height } }, animate)); } return me; }, /** * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property. * @param {Boolean} visible Whether the element is visible * @param {Boolean/Object} [animate] True for the default animation, or a standard Element animation config object * @return {Ext.dom.Element} this */ setVisible: function(visible, animate) { var me = this, dom = me.dom, visMode = getVisMode(me); // hideMode string override if (typeof animate === 'string') { switch (animate) { case DISPLAY: visMode = Element.DISPLAY; break; case VISIBILITY: visMode = Element.VISIBILITY; break; case OFFSETS: visMode = Element.OFFSETS; break; } me.setVisibilityMode(visMode); animate = false; } if (!animate || !me.anim) { if (visMode === Element.DISPLAY) { return me.setDisplayed(visible); } else if (visMode === Element.OFFSETS) { me[visible?'removeCls':'addCls'](OFFSETCLASS); } else if (visMode === Element.VISIBILITY) { me.fixDisplay(); // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting dom.style.visibility = visible ? '' : HIDDEN; } } else { // closure for composites if (visible) { me.setOpacity(0.01); me.setVisible(true); } if (!Ext.isObject(animate)) { animate = { duration: 350, easing: 'ease-in' }; } me.animate(Ext.applyIf({ callback: function() { if (!visible) { // Grab the dom again, since the reference may have changed if we use fly Ext.fly(dom).setVisible(false).setOpacity(1); } }, to: { opacity: (visible) ? 1 : 0 } }, animate)); } me.getData()[ISVISIBLE] = visible; if (me.shadow || me.shim) { me.setUnderlaysVisible(visible); } return me; }, /** * Set the width of this Element. * * // change the width to 200px and animate with default configuration * Ext.fly('elementId').setWidth(200, true); * * // change the width to 150px and animate with a custom configuration * Ext.fly('elId').setWidth(150, { * duration : 500, // animation will have a duration of .5 seconds * // will change the content to "finished" * callback: function(){ this.{@link #setHtml}("finished"); } * }); * * @param {Number/String} width The new width. This may be one of: * * - A Number specifying the new width in pixels. * - A String used to set the CSS width style. Animation may **not** be used. * * @param {Boolean/Object} [animate] a standard Element animation config object or `true` for * the default animation (`{duration: 350, easing: 'ease-in'}`) * @return {Ext.dom.Element} this */ setWidth: function(width, animate) { var me = this; if (!animate || !me.anim) { me.callParent(arguments); } else { if (!Ext.isObject(animate)) { animate = {}; } me.animate(Ext.applyIf({ to: { width: width } }, animate)); } return me; }, setX: function(x, animate) { return this.setXY([x, this.getY()], animate); }, setXY: function(xy, animate) { var me = this; if (!animate || !me.anim) { me.callParent([xy]); } else { if (!Ext.isObject(animate)) { animate = {}; } me.animate(Ext.applyIf({ to: { x: xy[0], y: xy[1] } }, animate)); } return this; }, setY: function(y, animate) { return this.setXY([this.getX(), y], animate); }, /** * Show this element - Uses display mode to determine whether to use "display", * "visibility", or "offsets". See {@link #setVisible}. * @param {Boolean/Object} [animate] true for the default animation or a standard * Element animation config object * @return {Ext.dom.Element} this */ show: function(animate){ // hideMode override if (typeof animate === 'string'){ this.setVisible(true, animate); return this; } this.setVisible(true, this.anim(animate)); return this; }, /** * Slides the element into view. An anchor point can be optionally passed to set the point of origin for the slide * effect. This function automatically handles wrapping the element with a fixed-size container if needed. See the * {@link Ext.fx.Anim} class overview for valid anchor point options. Usage: * * // default: slide the element in from the top * el.slideIn(); * * // custom: slide the element in from the right with a 2-second duration * el.slideIn('r', { duration: 2000 }); * * // common config options shown with default values * el.slideIn('t', { * easing: 'easeOut', * duration: 500 * }); * * @param {String} anchor (optional) One of the valid {@link Ext.fx.Anim} anchor positions (defaults to top: 't') * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @param {Boolean} options.preserveScroll Set to true if preservation of any descendant elements' * `scrollTop` values is required. By default the DOM wrapping operation performed by `slideIn` and * `slideOut` causes the browser to lose all scroll positions. * @return {Ext.dom.Element} The Element */ slideIn: function(anchor, obj, slideOut) { var me = this, dom = me.dom, elStyle = dom.style, beforeAnim, wrapAnim, restoreScroll, wrapDomParentNode; anchor = anchor || "t"; obj = obj || {}; beforeAnim = function() { var animScope = this, listeners = obj.listeners, el = Ext.fly(dom, '_anim'), box, originalStyles, anim, wrap; if (!slideOut) { el.fixDisplay(); } box = el.getBox(); if ((anchor == 't' || anchor == 'b') && box.height === 0) { box.height = dom.scrollHeight; } else if ((anchor == 'l' || anchor == 'r') && box.width === 0) { box.width = dom.scrollWidth; } originalStyles = el.getStyle(['width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index'], true); el.setSize(box.width, box.height); // Cache all descendants' scrollTop & scrollLeft values if configured to preserve scroll. if (obj.preserveScroll) { restoreScroll = el.cacheScrollValues(); } wrap = el.wrap({ role: 'presentation', id: Ext.id() + '-anim-wrap-for-' + el.dom.id, style: { visibility: slideOut ? 'visible' : 'hidden' } }); wrapDomParentNode = wrap.dom.parentNode; wrap.setPositioning(el.getPositioning()); if (wrap.isStyle('position', 'static')) { wrap.position('relative'); } el.clearPositioning('auto'); wrap.clip(); // The wrap will have reset all descendant scrollTops. Restore them if we cached them. if (restoreScroll) { restoreScroll(); } // This element is temporarily positioned absolute within its wrapper. // Restore to its default, CSS-inherited visibility setting. // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap. el.setStyle({ visibility: '', position: 'absolute' }); if (slideOut) { wrap.setSize(box.width, box.height); } switch (anchor) { case 't': anim = { from: { width: box.width + 'px', height: '0px' }, to: { width: box.width + 'px', height: box.height + 'px' } }; elStyle.bottom = '0px'; break; case 'l': anim = { from: { width: '0px', height: box.height + 'px' }, to: { width: box.width + 'px', height: box.height + 'px' } }; me.anchorAnimX(anchor); break; case 'r': anim = { from: { x: box.x + box.width, width: '0px', height: box.height + 'px' }, to: { x: box.x, width: box.width + 'px', height: box.height + 'px' } }; me.anchorAnimX(anchor); break; case 'b': anim = { from: { y: box.y + box.height, width: box.width + 'px', height: '0px' }, to: { y: box.y, width: box.width + 'px', height: box.height + 'px' } }; break; case 'tl': anim = { from: { x: box.x, y: box.y, width: '0px', height: '0px' }, to: { width: box.width + 'px', height: box.height + 'px' } }; elStyle.bottom = '0px'; me.anchorAnimX('l'); break; case 'bl': anim = { from: { y: box.y + box.height, width: '0px', height: '0px' }, to: { y: box.y, width: box.width + 'px', height: box.height + 'px' } }; me.anchorAnimX('l'); break; case 'br': anim = { from: { x: box.x + box.width, y: box.y + box.height, width: '0px', height: '0px' }, to: { x: box.x, y: box.y, width: box.width + 'px', height: box.height + 'px' } }; me.anchorAnimX('r'); break; case 'tr': anim = { from: { x: box.x + box.width, width: '0px', height: '0px' }, to: { x: box.x, width: box.width + 'px', height: box.height + 'px' } }; elStyle.bottom = '0px'; me.anchorAnimX('r'); break; } wrap.show(); wrapAnim = Ext.apply({}, obj); delete wrapAnim.listeners; wrapAnim = new Ext.fx.Anim(Ext.applyIf(wrapAnim, { target: wrap, duration: 500, easing: 'ease-out', from: slideOut ? anim.to : anim.from, to: slideOut ? anim.from : anim.to })); // In the absence of a callback, this listener MUST be added first wrapAnim.on('afteranimate', function() { var el = Ext.fly(dom, '_anim'); el.setStyle(originalStyles); if (slideOut) { if (obj.useDisplay) { el.setDisplayed(false); } else { el.hide(); } } if (wrap.dom) { if (wrap.dom.parentNode) { wrap.dom.parentNode.insertBefore(el.dom, wrap.dom); } else { wrapDomParentNode.appendChild(el.dom); } wrap.destroy(); } // The unwrap will have reset all descendant scrollTops. Restore them if we cached them. if (restoreScroll) { restoreScroll(); } // kill the no-op element animation created below animScope.end(); }); // Add configured listeners after if (listeners) { wrapAnim.on(listeners); } }; me.animate({ // See "A Note About Wrapped Animations" at the top of this class: duration: obj.duration ? Math.max(obj.duration, 500) * 2 : 1000, listeners: { beforeanimate: beforeAnim // kick off the wrap animation } }); return me; }, /** * Slides the element out of view. An anchor point can be optionally passed to set the end point for the slide * effect. When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will * still take up space in the document. The element must be removed from the DOM using the 'remove' config option if * desired. This function automatically handles wrapping the element with a fixed-size container if needed. See the * {@link Ext.fx.Anim} class overview for valid anchor point options. Usage: * * // default: slide the element out to the top * el.slideOut(); * * // custom: slide the element out to the right with a 2-second duration * el.slideOut('r', { duration: 2000 }); * * // common config options shown with default values * el.slideOut('t', { * easing: 'easeOut', * duration: 500, * remove: false, * useDisplay: false * }); * * @param {String} anchor (optional) One of the valid {@link Ext.fx.Anim} anchor positions (defaults to top: 't') * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ slideOut: function(anchor, o) { return this.slideIn(anchor, o, true); }, /** * Stops the specified event(s) from bubbling and optionally prevents the default action * * var store = Ext.create('Ext.data.Store', { * fields: ['name', 'email'], * data: [{ * 'name': 'Finn', * "email": "finn@adventuretime.com" * }] * }); * * Ext.create('Ext.grid.Panel', { * title: 'Land of Ooo', * store: store, * columns: [{ * text: 'Name', * dataIndex: 'name' * }, { * text: 'Email ', * dataIndex: 'email', * flex: 1, * listeners: { * render: function(col) { * // Swallow the click event when the click occurs on the * // help icon - preventing the sorting of data by that * // column and instead performing an action specific to * // the help icon * var img = col.getEl().down('img'); * img.swallowEvent(['click', 'mousedown'], true); * col.on('click', function() { * // logic to show a help dialog * console.log('image click handler'); * }, col); * } * } * }], * height: 200, * width: 400, * renderTo: document.body * }); * * @param {String/String[]} eventName an event / array of events to stop from bubbling * @param {Boolean} [preventDefault] true to prevent the default action too * @return {Ext.dom.Element} this */ swallowEvent: function(eventName, preventDefault) { var me = this, e, eLen, fn = function(e) { e.stopPropagation(); if (preventDefault) { e.preventDefault(); } }; if (Ext.isArray(eventName)) { eLen = eventName.length; for (e = 0; e < eLen; e++) { me.on(eventName[e], fn); } return me; } me.on(eventName, fn); return me; }, /** * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television). * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still * take up space in the document. The element must be removed from the DOM using the 'remove' config option if * desired. Usage: * * // default * el.switchOff(); * * // all config options shown with default values * el.switchOff({ * easing: 'easeIn', * duration: .3, * remove: false, * useDisplay: false * }); * * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ switchOff: function(obj) { var me = this, dom = me.dom, beforeAnim; obj = Ext.applyIf(obj || {}, { easing: 'ease-in', duration: 500, remove: false, useDisplay: false }); beforeAnim = function() { var el = Ext.fly(dom, '_anim'), animScope = this, size = el.getSize(), xy = el.getXY(), keyframe, position; el.clearOpacity(); el.clip(); position = el.getPositioning(); keyframe = new Ext.fx.Animator({ target: dom, duration: obj.duration, easing: obj.easing, keyframes: { 33: { opacity: 0.3 }, 66: { height: 1, y: xy[1] + size.height / 2 }, 100: { width: 1, x: xy[0] + size.width / 2 } } }); keyframe.on('afteranimate', function() { var el = Ext.fly(dom, '_anim'); if (obj.useDisplay) { el.setDisplayed(false); } else { el.hide(); } el.clearOpacity(); el.setPositioning(position); el.setSize(size); // kill the no-op element animation created below animScope.end(); }); }; me.animate({ // See "A Note About Wrapped Animations" at the top of this class: duration: (Math.max(obj.duration, 500) * 2), listeners: { beforeanimate: { fn: beforeAnim } }, callback: obj.callback, scope: obj.scope }); return me; }, /** * @private. * Currently used for updating grid cells without modifying DOM structure * * Synchronizes content of this Element with the content of the passed element. * * Style and CSS class are copied from source into this Element, and contents are synced * recursively. If a child node is a text node, the textual data is copied. */ syncContent: function(source) { source = Ext.getDom(source); var sourceNodes = source.childNodes, sourceLen = sourceNodes.length, dest = this.dom, destNodes = dest.childNodes, destLen = destNodes.length, i, destNode, sourceNode, nodeType, newAttrs, attLen, attName, elData = dest._extData; // Copy top node's attributes across. Use IE-specific method if possible. // In IE10, there is a problem where the className will not get updated // in the view, even though the className on the dom element is correct. // See EXTJSIV-9462 if (Ext.isIE9m && dest.mergeAttributes) { dest.mergeAttributes(source, true); // EXTJSIV-6803. IE's mergeAttributes appears not to make the source's "src" value available until after the image is ready. // So programmatically copy any src attribute. dest.src = source.src; } else { newAttrs = source.attributes; attLen = newAttrs.length; for (i = 0; i < attLen; i++) { attName = newAttrs[i].name; if (attName !== 'id') { dest.setAttribute(attName, newAttrs[i].value); } } } // The element's data is no longer synchronized. We just overwrite it in the DOM if (elData) { elData.isSynchronized = false; } // If the number of child nodes does not match, fall back to replacing innerHTML if (sourceLen !== destLen) { dest.innerHTML = source.innerHTML; return; } // Loop through source nodes. // If there are fewer, we must remove excess for (i = 0; i < sourceLen; i++) { sourceNode = sourceNodes[i]; destNode = destNodes[i]; nodeType = sourceNode.nodeType; // If node structure is out of sync, just drop innerHTML in and return if (nodeType !== destNode.nodeType || (nodeType === 1 && sourceNode.tagName !== destNode.tagName)) { dest.innerHTML = source.innerHTML; return; } // Update text node if (nodeType === 3) { destNode.data = sourceNode.data; } // Sync element content else { if (sourceNode.id && destNode.id !== sourceNode.id) { destNode.id = sourceNode.id; } destNode.style.cssText = sourceNode.style.cssText; destNode.className = sourceNode.className; Ext.fly(destNode, '_syncContent').syncContent(sourceNode); } } }, /** * Toggles the element's visibility, depending on visibility mode. * @param {Boolean/Object} [animate] True for the default animation, or a standard Element animation config object * @return {Ext.dom.Element} this */ toggle: function(animate){ var me = this; me.setVisible(!me.isVisible(), me.anim(animate)); return me; }, /** * Hides a previously applied mask. */ unmask: function() { var me = this, data = me.getData(), maskEl = data.maskEl, style; if (maskEl) { style = maskEl.dom.style; // Remove resource-intensive CSS expressions as soon as they are not required. if (style.clearExpression) { style.clearExpression('width'); style.clearExpression('height'); } if (maskEl) { maskEl.destroy(); delete data.maskEl; } me.removeCls([XMASKED, XMASKEDRELATIVE]); } me.restoreChildrenTabbableState(); if (me.dom !== DOC.body) { me.restoreTabbableState(); } }, /** * Return clipping (overflow) to original clipping before {@link #clip} was called * @return {Ext.dom.Element} this */ unclip: function() { var me = this, data = me.getData(), clip; if (data[ISCLIPPED]) { data[ISCLIPPED] = false; clip = data[ORIGINALCLIP]; if (clip.o) { me.setStyle(OVERFLOW, clip.o); } if (clip.x) { me.setStyle(OVERFLOWX, clip.x); } if (clip.y) { me.setStyle(OVERFLOWY, clip.y); } } return me; }, translate: function(x, y, z) { if (Ext.supports.CssTransforms && !Ext.isIE9m) { this.callParent(arguments); } else { if (x != null) { this.dom.style.left = x + 'px'; } if (y != null) { this.dom.style.top = y + 'px'; } } }, /** * Disables text selection for this element (normalized across browsers) * @return {Ext.dom.Element} this */ unselectable: function() { // The approach used to disable text selection combines CSS, HTML attributes and DOM events. Importantly the // strategy is designed to be expressible in markup, so that elements can be rendered unselectable without // needing modifications post-render. e.g.: // //
// // Changes to this method may need to be reflected elsewhere, e.g. ProtoElement. var me = this; // The unselectable property (or similar) is supported by various browsers but Opera is the only browser that // doesn't support any of the other techniques. The problem with it is that it isn't inherited by child // elements. Theoretically we could add it to all children but the performance would be terrible. In certain // key locations (e.g. panel headers) we add unselectable="on" to extra elements during rendering just for // Opera's benefit. if (Ext.isOpera) { me.dom.unselectable = 'on'; } // In Mozilla and WebKit the CSS properties -moz-user-select and -webkit-user-select prevent a selection // originating in an element. These are inherited, which is what we want. // // In IE we rely on a listener for the selectstart event instead. We don't need to register a listener on the // individual element, instead we use a single listener and rely on event propagation to listen for the event at // the document level. That listener will walk up the DOM looking for nodes that have either of the classes // x-selectable or x-unselectable. This simulates the CSS inheritance approach. // // IE 10 is expected to support -ms-user-select so the listener may not be required. me.removeCls(Element.selectableCls); me.addCls(Element.unselectableCls); return me; }, privates: { /** * Returns true if this element needs an explicit tabIndex to make it focusable. * Input fields, text areas, buttons, anchor elements **with an href** etc * do not need a tabIndex, but structural elements do. * See http://www.w3.org/TR/html5/editing.html#specially-focusable * @private */ needsTabIndex: function() { var dom = this.dom, nodeName, isFocusable; if (dom) { nodeName = dom.nodeName; // Note that the code below is identical to isFocusable(); // it is intentionally duplicated for performance reasons. isFocusable = !!Ext.Element.naturallyFocusableTags[nodeName] || ((nodeName === 'A' || nodeName === 'LINK') && !!dom.href) || dom.getAttribute('tabindex') != null || dom.contentEditable === 'true'; // The result we need is the opposite to what we got return !isFocusable; } }, /** * @private * The difference between findTabbableElements and selectTabbableElements * is that find() will include `this` element itself if it is tabbable, while * select() will only find tabbable children following querySelectorAll() logic. */ findTabbableElements: function(asDom, selector, /* private */ limit, backward) { asDom = asDom != undefined ? asDom : true; var me = this, selection; selection = me.selectTabbableElements(asDom, selector, limit, backward); if (me.isTabbable()) { selection.unshift(asDom ? me.dom : me); } return selection; }, /** * @private */ selectTabbableElements: function(asDom, selector, /* private */ limit, backward) { var selection = [], nodes, node, el, i, len, to, step, tabIndex; asDom = asDom != undefined ? asDom : true; nodes = this.dom.querySelectorAll(selector || Ext.Element.tabbableSelector); len = nodes.length; if (!len) { return selection; } if (backward) { i = len - 1; to = 0; step = -1; } else { i = 0; to = len - 1; step = 1; } // We're only interested in the elements that an user can *tab into*, // not all programmatically focusable elements. So we have to filter // these out. for (;; i += step) { if ((step > 0 && i > to) || (step < 0 && i < to)) { break; } node = nodes[i]; // A node with tabIndex < 0 absolutely can't be tabbable // so we can save a function call if that is the case. // Note that we can't use node.tabIndex here because IE // will return 0 for elements that have no tabindex // attribute defined, regardless of whether they are // tabbable or not. tabIndex = node.getAttribute('tabindex') - 0; // quicker than parseInt // tabIndex value may be null for nodes with no tabIndex defined; // most of those may be naturally tabbable. We don't want to // check this here, that's isTabbable()'s job and it's not trivial. // We explicitly check that tabIndex is not negative; the expression // below is purposeful. if (!(tabIndex < 0)) { el = asDom ? Ext.fly(node) : Ext.get(node); if (el.isTabbable()) { selection.push(asDom ? node : el); } } if (selection.length >= limit) { return selection; } } return selection; }, /** * @private */ selectFirstTabbableElement: function(asDom, selector) { var els = this.selectTabbableElements(asDom, selector, 1, false); return els[0]; }, /** * @private */ selectLastTabbableElement: function(asDom, selector) { var el = this.selectTabbableElements(true, selector, 1, true)[0]; return (asDom !== false) ? el : Ext.get(el); }, /** * @private */ saveTabbableState: function(attribute) { var tabbableSavedFlagAttribute = Ext.Element.tabbableSavedFlagAttribute, dom = this.dom; // Prevent tabIndex from being munged more than once if (dom.hasAttribute(tabbableSavedFlagAttribute)) { return; } attribute = attribute || Ext.Element.tabbableSavedAttribute; // tabIndex could be set on both naturally tabbable and generic elements. // Either way we need to save it to restore later. if (dom.hasAttribute('tabindex')) { dom.setAttribute(attribute, dom.getAttribute('tabindex')); } // When no tabIndex is specified, that means a naturally tabbable element. else { dom.setAttribute(attribute, 'none'); } // We disable the tabbable state by setting tabIndex to -1. // The element can still be focused programmatically though. dom.setAttribute('tabindex', -1); dom.setAttribute(tabbableSavedFlagAttribute, true); return this; }, /** * @private */ restoreTabbableState: function(attribute) { var tabbableSavedFlagAttribute = Ext.Element.tabbableSavedFlagAttribute, dom = this.dom, idx; attribute = attribute || Ext.Element.tabbableSavedAttribute; if (!dom.hasAttribute(tabbableSavedFlagAttribute) || !dom.hasAttribute(attribute)) { return; } idx = dom.getAttribute(attribute); // That is a naturally tabbable element if (idx === 'none') { dom.removeAttribute('tabindex'); } else { dom.setAttribute('tabindex', idx); } dom.removeAttribute(attribute); dom.removeAttribute(tabbableSavedFlagAttribute); return this; }, /** * @private */ saveChildrenTabbableState: function(attribute) { var children, child, i, len; if (this.dom) { children = this.selectTabbableElements(); for (i = 0, len = children.length; i < len; i++) { child = Ext.fly(children[i]); child.saveTabbableState(attribute); } } return children; }, /** * @private */ restoreChildrenTabbableState: function(attribute, children) { var child, i, len; if (this.dom) { attribute = attribute || Ext.Element.tabbableSavedAttribute; children = children || this.dom.querySelectorAll('[' + attribute + ']'); for (i = 0, len = children.length; i < len; i++) { child = Ext.fly(children[i]); child.restoreTabbableState(attribute); } } return children; } }, deprecated: { '4.0': { methods: { /** * Creates a pause before any subsequent queued effects begin. If there are no effects queued after the pause it will * have no effect. Usage: * * el.pause(1); * * @deprecated 4.0 Use the `delay` config to {@link #animate} instead. * @param {Number} seconds The length of time to pause (in seconds) * @return {Ext.dom.Element} The Element */ pause: function(ms) { var me = this; Ext.fx.Manager.setFxDefaults(me.id, { delay: ms }); return me; }, /** * Animates the transition of an element's dimensions from a starting height/width to an ending height/width. This * method is a convenience implementation of {@link #shift}. Usage: * * // change height and width to 100x100 pixels * el.scale(100, 100); * * // common config options shown with default values. The height and width will default to * // the element's existing values if passed as null. * el.scale( * [element's width], * [element's height], { * easing: 'easeOut', * duration: 350 * } * ); * * @deprecated 4.0 Just use {@link #animate} instead. * @param {Number} width The new width (pass undefined to keep the original width) * @param {Number} height The new height (pass undefined to keep the original height) * @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ scale: function(w, h, o) { this.animate(Ext.apply({}, o, { width: w, height: h })); return this; }, /** * Animates the transition of any combination of an element's dimensions, xy position and/or opacity. Any of these * properties not specified in the config object will not be changed. This effect requires that at least one new * dimension, position or opacity setting must be passed in on the config object in order for the function to have * any effect. Usage: * * // slide the element horizontally to x position 200 while changing the height and opacity * el.shift({ x: 200, height: 50, opacity: .8 }); * * // common config options shown with default values. * el.shift({ * width: [element's width], * height: [element's height], * x: [element's x position], * y: [element's y position], * opacity: [element's opacity], * easing: 'easeOut', * duration: 350 * }); * * @deprecated 4.0 Just use {@link #animate} instead. * @param {Object} options Object literal with any of the {@link Ext.fx.Anim} config options * @return {Ext.dom.Element} The Element */ shift: function(config) { this.animate(config); return this; } } }, '4.2': { methods: { /** * Sets the position of the element in page coordinates. * @param {Number} x X value for new position (coordinates are page-based) * @param {Number} y Y value for new position (coordinates are page-based) * @param {Boolean/Object} [animate] True for the default animation, or a standard * Element animation config object * @return {Ext.dom.Element} this * @deprecated 4.2.0 Use {@link #setXY} instead. */ moveTo: function(x, y, animate) { return this.setXY([x, y], animate); }, /** * Sets the element's position and size in one shot. If animation is true then * width, height, x and y will be animated concurrently. * * @param {Number} x X value for new position (coordinates are page-based) * @param {Number} y Y value for new position (coordinates are page-based) * @param {Number/String} width The new width. This may be one of: * * - A Number specifying the new width in pixels * - A String used to set the CSS width style. Animation may **not** be used. * * @param {Number/String} height The new height. This may be one of: * * - A Number specifying the new height in pixels * - A String used to set the CSS height style. Animation may **not** be used. * * @param {Boolean/Object} [animate] true for the default animation or * a standard Element animation config object * * @return {Ext.dom.Element} this * @deprecated 4.2.0 Use {@link Ext.util.Positionable#setBox} instead. */ setBounds: function(x, y, width, height, animate) { return this.setBox({ x: x, y: y, width: width, height: height }, animate); }, /** * Sets the element's left and top positions directly using CSS style * @param {Number/String} left Number of pixels or CSS string value to * set as the left CSS property value * @param {Number/String} top Number of pixels or CSS string value to * set as the top CSS property value * @return {Ext.dom.Element} this * @deprecated 4.2.0 Use {@link #setLocalXY} instead */ setLeftTop: function(left, top) { var me = this, style = me.dom.style; style.left = Element.addUnits(left); style.top = Element.addUnits(top); if (me.shadow || me.shim) { me.syncUnderlays(); } return me; }, /** * Sets the position of the element in page coordinates. * @param {Number} x X value for new position * @param {Number} y Y value for new position * @param {Boolean/Object} [animate] True for the default animation, or a standard * Element animation config object * @return {Ext.dom.Element} this * @deprecated 4.2.0 Use {@link #setXY} instead. */ setLocation: function(x, y, animate) { return this.setXY([x, y], animate); } } }, '5.0': { methods: { /** * Returns the value of a namespaced attribute from the element's underlying DOM node. * @param {String} namespace The namespace in which to look for the attribute * @param {String} name The attribute name * @return {String} The attribute value * @deprecated 5.0.0 Please use {@link #getAttribute} instead. */ getAttributeNS: function(namespace, name) { return this.getAttribute(name, namespace); }, /** * Calculates the x, y to center this element on the screen * @return {Number[]} The x, y values [x, y] * @deprecated 5.0.0 Use {@link #getAlignToXY} instead. * el.getAlignToXY(document, 'c-c'); */ getCenterXY: function(){ return this.getAlignToXY(DOC, 'c-c'); }, /** * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements * if a height has not been set using CSS. * @return {Number} * @deprecated 5.0.0 use {@link #getHeight} instead */ getComputedHeight: function() { return Math.max(this.dom.offsetHeight, this.dom.clientHeight) || parseFloat(this.getStyle(HEIGHT)) || 0; }, /** * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements * if a width has not been set using CSS. * @return {Number} * @deprecated 5.0.0 use {@link #getWidth} instead. */ getComputedWidth: function() { return Math.max(this.dom.offsetWidth, this.dom.clientWidth) || parseFloat(this.getStyle(WIDTH)) || 0; }, /** * Returns the dimensions of the element available to lay content out in. * * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and * offsetWidth/clientWidth. To obtain the size excluding scrollbars, use getViewSize. * * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc. * * @return {Object} Object describing width and height. * @return {Number} return.width * @return {Number} return.height * @deprecated 5.0.0 Use {@link #getSize} instead. */ getStyleSize: function() { var me = this, d = this.dom, isDoc = (d === DOC || d === DOC.body), s, w, h; // If the body, use static methods if (isDoc) { return { width : Element.getViewportWidth(), height : Element.getViewportHeight() }; } s = me.getStyle(['height', 'width'], true); //seek inline // Use Styles if they are set if (s.width && s.width !== 'auto') { w = parseFloat(s.width); } // Use Styles if they are set if (s.height && s.height !== 'auto') { h = parseFloat(s.height); } // Use getWidth/getHeight if style not set. return {width: w || me.getWidth(true), height: h || me.getHeight(true)}; }, /** * Returns true if this element uses the border-box-sizing model. This method is * deprecated as of version 5.0 because border-box sizing is forced upon all elements * via a style sheet rule, and the browsers that do not support border-box (IE6/7 strict * mode) are no longer supported. * @deprecated 5.0.0 * @return {Boolean} */ isBorderBox: function() { return true; }, /** * Returns true if display is not "none" * @return {Boolean} * @deprecated 5.0.0 use element.isStyle('display', 'none'); */ isDisplayed: function() { return !this.isStyle('display', 'none'); }, /** * Checks whether this element can be focused. * @return {Boolean} True if the element is focusable * @deprecated 5.0.0 use {@link #isFocusable} instead */ focusable: 'isFocusable' } } } }; })(), function() { var Element = Ext.dom.Element, proto = Element.prototype, useDocForId = !Ext.isIE8, DOC = document, view = DOC.defaultView, opacityRe = /alpha\(opacity=(.*)\)/i, trimRe = /^\s+|\s+$/g, styleHooks = proto.styleHooks, supports = Ext.supports, verticalStyleHooks90, verticalStyleHooks270, edges, k, edge, borderWidth, getBorderWidth; proto._init(Element); delete proto._init; Ext.plainTableCls = Ext.baseCSSPrefix + 'table-plain'; Ext.plainListCls = Ext.baseCSSPrefix + 'list-plain'; // ensure that any methods added by this override are also added to Ext.CompositeElementLite if (Ext.CompositeElementLite) { Ext.CompositeElementLite.importElementMethods(); } styleHooks.opacity = { name: 'opacity', afterSet: function(dom, value, el) { var shadow = el.shadow; if (shadow) { shadow.setOpacity(value); } } }; if (!supports.Opacity && Ext.isIE) { Ext.apply(styleHooks.opacity, { get: function (dom) { var filter = dom.style.filter, match, opacity; if (filter.match) { match = filter.match(opacityRe); if (match) { opacity = parseFloat(match[1]); if (!isNaN(opacity)) { return opacity ? opacity / 100 : 0; } } } return 1; }, set: function (dom, value) { var style = dom.style, val = style.filter.replace(opacityRe, '').replace(trimRe, ''); style.zoom = 1; // ensure dom.hasLayout // value can be a number or '' or null... so treat falsey as no opacity if (typeof(value) === 'number' && value >= 0 && value < 1) { value *= 100; style.filter = val + (val.length ? ' ' : '') + 'alpha(opacity='+value+')'; } else { style.filter = val; } } }); } if (!supports.matchesSelector) { // Match basic tagName.ClassName selector syntax for is implementation var simpleSelectorRe = /^([a-z]+|\*)?(?:\.([a-z][a-z\-_0-9]*))?$/i, dashRe = /\-/g, fragment, classMatcher = function (tag, cls) { var classRe = new RegExp('(?:^|\\s+)' +cls.replace(dashRe, '\\-') + '(?:\\s+|$)'); if (tag && tag !== '*') { tag = tag.toUpperCase(); return function (el) { return el.tagName === tag && classRe.test(el.className); }; } return function (el) { return classRe.test(el.className); }; }, tagMatcher = function (tag) { tag = tag.toUpperCase(); return function (el) { return el.tagName === tag; }; }, cache = {}; proto.matcherCache = cache; proto.is = function(selector) { // Empty selector always matches if (!selector) { return true; } var dom = this.dom, cls, match, testFn, root, isOrphan, is, tag; // Only Element node types can be matched. if (dom.nodeType !== 1) { return false; } if (!(testFn = Ext.isFunction(selector) ? selector : cache[selector])) { if (!(match = selector.match(simpleSelectorRe))) { // Not a simple tagName.className selector, do it the hard way root = dom.parentNode; if (!root) { isOrphan = true; root = fragment || (fragment = DOC.createDocumentFragment()); fragment.appendChild(dom); } is = Ext.Array.indexOf(Ext.fly(root, '_is').query(selector), dom) !== -1; if (isOrphan) { fragment.removeChild(dom); } return is; } tag = match[1]; cls = match[2]; cache[selector] = testFn = cls ? classMatcher(tag, cls) : tagMatcher(tag); } return testFn(dom); }; } // IE8 needs its own implementation of getStyle because it doesn't support getComputedStyle if (!view || !view.getComputedStyle) { proto.getStyle = function (property, inline) { var me = this, dom = me.dom, multiple = typeof property !== 'string', prop = property, props = prop, len = 1, isInline = inline, styleHooks = me.styleHooks, camel, domStyle, values, hook, out, style, i; if (multiple) { values = {}; prop = props[0]; i = 0; if (!(len = props.length)) { return values; } } if (!dom || dom.documentElement) { return values || ''; } domStyle = dom.style; if (inline) { style = domStyle; } else { style = dom.currentStyle; // fallback to inline style if rendering context not available if (!style) { isInline = true; style = domStyle; } } do { hook = styleHooks[prop]; if (!hook) { styleHooks[prop] = hook = { name: Element.normalize(prop) }; } if (hook.get) { out = hook.get(dom, me, isInline, style); } else { camel = hook.name; out = style[camel]; } if (!multiple) { return out; } values[prop] = out; prop = props[++i]; } while (i < len); return values; }; } // override getStyle for border-*-width if (Ext.isIE8) { getBorderWidth = function (dom, el, inline, style) { if (style[this.styleName] === 'none') { return '0px'; } return style[this.name]; }; edges = ['Top','Right','Bottom','Left']; k = edges.length; while (k--) { edge = edges[k]; borderWidth = 'border' + edge + 'Width'; styleHooks['border-'+edge.toLowerCase()+'-width'] = styleHooks[borderWidth] = { name: borderWidth, styleName: 'border' + edge + 'Style', get: getBorderWidth }; } } Ext.apply(Ext, { /** * `true` to automatically uncache orphaned Ext.Elements periodically. If set to * `false`, the application will be required to clean up orphaned Ext.Elements and * it's listeners as to not cause memory leakage. * @member Ext */ enableGarbageCollector: true, // In sencha v5 isBorderBox is no longer needed since all supported browsers // support border-box, but it is hard coded to true for backward compatibility isBorderBox: true, /** * @property {Boolean} useShims * @member Ext * Set to `true` to use a {@link Ext.util.Floating#shim shim} on all floating Components * and {@link Ext.LoadMask LoadMasks} */ useShims: false, /** * @private * Returns an HTML div element into which {@link Ext.container.Container#method-remove removed} components * are placed so that their DOM elements are not garbage collected as detached Dom trees. * @return {Ext.dom.Element} * @member Ext */ getDetachedBody: function () { var detachedEl = Ext.detachedBodyEl; if (!detachedEl) { detachedEl = DOC.createElement('div'); Ext.detachedBodyEl = detachedEl = new Ext.dom.Fly(detachedEl); detachedEl.isDetachedBody = true; } return detachedEl; }, getElementById: function (id) { var el = DOC.getElementById(id), detachedBodyEl; if (!el && (detachedBodyEl = Ext.detachedBodyEl)) { el = detachedBodyEl.dom.querySelector(Ext.makeIdSelector(id)); } return el; }, /** * Applies event listeners to elements by selectors when the document is ready. * The event name is specified with an `@` suffix. * * Ext.addBehaviors({ * // add a listener for click on all anchors in element with id foo * '#foo a@click': function(e, t){ * // do something * }, * * // add the same listener to multiple selectors (separated by comma BEFORE the @) * '#foo a, #bar span.some-class@mouseover': function(){ * // do something * } * }); * * @param {Object} obj The list of behaviors to apply * @member Ext */ addBehaviors: function(o){ if(!Ext.isReady){ Ext.onInternalReady(function(){ Ext.addBehaviors(o); }); } else { var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times parts, b, s; for (b in o) { if ((parts = b.split('@'))[1]) { // for Object prototype breakers s = parts[0]; if(!cache[s]){ cache[s] = Ext.fly(document).select(s, true); } cache[s].on(parts[1], o[b]); } } cache = null; } } }); if (Ext.isIE9m) { Ext.getElementById = function (id) { var el = DOC.getElementById(id), detachedBodyEl; if (!el && (detachedBodyEl = Ext.detachedBodyEl)) { el = detachedBodyEl.dom.all[id]; } return el; }; proto.getById = function (id, asDom) { var dom = this.dom, ret = null, entry, el; if (dom) { // for normal elements getElementById is the best solution, but if the el is // not part of the document.body, we need to use all[] el = (useDocForId && DOC.getElementById(id)) || dom.all[id]; if (el) { if (asDom) { ret = el; } else { // calling Element.get here is a real hit (2x slower) because it has to // redetermine that we are giving it a dom el. entry = Ext.cache[id]; if (entry) { if (entry.skipGarbageCollection || !Ext.isGarbage(entry.dom)) { ret = entry; } else { // Ext.Error.raise("Stale Element with id '" + el.id + "' found in Element cache. " + "Make sure to clean up Element instances using destroy()" ); // entry.destroy(); } } ret = ret || new Ext.Element(el); } } } return ret; }; } else if (!DOC.querySelector) { Ext.getDetachedBody = Ext.getBody; Ext.getElementById = function (id) { return DOC.getElementById(id); }; proto.getById = function (id, asDom) { var dom = DOC.getElementById(id); return asDom ? dom : (dom ? Ext.get(dom) : null); }; } if (Ext.isIE && !(Ext.isIE9p && DOC.documentMode >= 9)) { // Essentially all web browsers (Firefox, Internet Explorer, recent versions of Opera, Safari, Konqueror, and iCab, // as a non-exhaustive list) return null when the specified attribute does not exist on the specified element. // The DOM specification says that the correct return value in this case is actually the empty string, and some // DOM implementations implement this behavior. The implementation of getAttribute in XUL (Gecko) actually follows // the specification and returns an empty string. Consequently, you should use hasAttribute to check for an attribute's // existence prior to calling getAttribute() if it is possible that the requested attribute does not exist on the specified element. // // https://developer.mozilla.org/en-US/docs/DOM/element.getAttribute // http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-745549614 proto.getAttribute = function(name, ns) { var d = this.dom, type; if (ns) { type = typeof d[ns + ":" + name]; if (type !== 'undefined' && type !== 'unknown') { return d[ns + ":" + name] || null; } return null; } if (name === "for") { name = "htmlFor"; } return d[name] || null; }; } Ext.onInternalReady(function () { var transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i, bodyCls = [], //htmlCls = [], origSetWidth = proto.setWidth, origSetHeight = proto.setHeight, origSetSize = proto.setSize, pxRe = /^\d+(?:\.\d*)?px$/i, colorStyles, i, name, camel; if (supports.FixedTableWidthBug) { // EXTJSIV-12665 // https://bugs.webkit.org/show_bug.cgi?id=130239 // Webkit browsers fail to layout correctly when a form field's width is less // than the min-width of the body element. The only way to fix it seems to be // to toggle the display style of the field's element before and after setting // the width. Note: once the bug has been corrected by toggling the element's // display, successive calls to setWidth will work without the hack. It's only // when going from naturally widthed to having an explicit width that the bug // occurs. styleHooks.width = { name: 'width', set: function(dom, value, el) { var style = dom.style, needsFix = el._needsTableWidthFix, origDisplay = style.display; if (needsFix) { style.display = 'none'; } style.width = value; if (needsFix) { // repaint dom.scrollWidth; // jshint ignore:line style.display = origDisplay; } } }; proto.setWidth = function(width, animate) { var me = this, dom = me.dom, style = dom.style, needsFix = me._needsTableWidthFix, origDisplay = style.display; if (needsFix && !animate) { style.display = 'none'; } origSetWidth.call(me, width, animate); if (needsFix && !animate) { // repaint dom.scrollWidth; // jshint ignore:line style.display = origDisplay; } return me; }; proto.setSize = function(width, height, animate) { var me = this, dom = me.dom, style = dom.style, needsFix = me._needsTableWidthFix, origDisplay = style.display; if (needsFix && !animate) { style.display = 'none'; } origSetSize.call(me, width, height, animate); if (needsFix && !animate) { // repaint dom.scrollWidth; // jshint ignore:line style.display = origDisplay; } return me; }; } // if (Ext.isIE8) { styleHooks.height = { name: 'height', set: function(dom, value, el) { var component = el.component, frameInfo, frameBodyStyle; if (component && component._syncFrameHeight && this === component.el) { frameBodyStyle = component.frameBody.dom.style; if (pxRe.test(value)) { frameInfo = component.getFrameInfo(); if (frameInfo) { frameBodyStyle.height = (parseInt(value, 10) - frameInfo.height) + 'px'; } } else if (!value || value === 'auto') { frameBodyStyle.height = ''; } } dom.style.height = value; } }; proto.setHeight = function(height, animate) { var component = this.component, frameInfo, frameBodyStyle; if (component && component._syncFrameHeight && this === component.el) { frameBodyStyle = component.frameBody.dom.style; if (!height || height === 'auto') { frameBodyStyle.height = ''; } else { frameInfo = component.getFrameInfo(); if (frameInfo) { frameBodyStyle.height = (height - frameInfo.height) + 'px'; } } } return origSetHeight.call(this, height, animate); }; proto.setSize = function(width, height, animate) { var component = this.component, frameInfo, frameBodyStyle; if (component && component._syncFrameHeight && this === component.el) { frameBodyStyle = component.frameBody.dom.style; if (!height || height === 'auto') { frameBodyStyle.height = ''; } else { frameInfo = component.getFrameInfo(); if (frameInfo) { frameBodyStyle.height = (height - frameInfo.height) + 'px'; } } } return origSetSize.call(this, width, height, animate); }; } // // Element.unselectable relies on this listener to prevent selection in IE. Some other browsers support the event too // but it is only strictly required for IE. In WebKit this listener causes subtle differences to how the browser handles // the non-selection, e.g. whether or not the mouse cursor changes when attempting to select text. Ext.getDoc().on('selectstart', function(ev, dom) { var selectableCls = Element.selectableCls, unselectableCls = Element.unselectableCls, tagName = dom && dom.tagName; tagName = tagName && tagName.toLowerCase(); // Element.unselectable is not really intended to handle selection within text fields and it is important that // fields inside menus or panel headers don't inherit the unselectability. In most browsers this is automatic but in // IE 9 the selectstart event can bubble up from text fields so we have to explicitly handle that case. if (tagName === 'input' || tagName === 'textarea') { return; } // Walk up the DOM checking the nodes. This may be 'slow' but selectstart events don't fire very often while (dom && dom.nodeType === 1 && dom !== DOC.documentElement) { var el = Ext.fly(dom); // If the node has the class x-selectable then stop looking, the text selection is allowed if (el.hasCls(selectableCls)) { return; } // If the node has class x-unselectable then the text selection needs to be stopped if (el.hasCls(unselectableCls)) { ev.stopEvent(); return; } dom = dom.parentNode; } }); function fixTransparent (dom, el, inline, style) { var value = style[this.name] || ''; return transparentRe.test(value) ? 'transparent' : value; } /* * Helper function to create the function that will restore the selection. */ function makeSelectionRestoreFn (activeEl, start, end) { return function () { activeEl.selectionStart = start; activeEl.selectionEnd = end; }; } /** * Creates a function to call to clean up problems with the work-around for the * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to * the element before calling getComputedStyle and then to restore its original * display value. The problem with this is that it corrupts the selection of an * INPUT or TEXTAREA element (as in the "I-beam" goes away but the focus remains). * To cleanup after this, we need to capture the selection of any such element and * then restore it after we have restored the display style. * * @param {HTMLElement} target The top-most element being adjusted. * @private */ function getRightMarginFixCleaner(target) { var hasInputBug = supports.DisplayChangeInputSelectionBug, hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug, activeEl, tag, start, end; if (hasInputBug || hasTextAreaBug) { activeEl = Element.getActiveElement(); tag = activeEl && activeEl.tagName; if ((hasTextAreaBug && tag === 'TEXTAREA') || (hasInputBug && tag === 'INPUT' && activeEl.type === 'text')) { if (Ext.fly(target).isAncestor(activeEl)) { start = activeEl.selectionStart; end = activeEl.selectionEnd; if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe... // We don't create the raw closure here inline because that // will be costly even if we don't want to return it (nested // function decls and exprs are often instantiated on entry // regardless of whether execution ever reaches them): return makeSelectionRestoreFn(activeEl, start, end); } } } } return Ext.emptyFn; // avoid special cases, just return a nop } function fixRightMargin (dom, el, inline, style) { var result = style.marginRight, domStyle, display; // Ignore cases when the margin is correctly reported as 0, the bug only shows // numbers larger. if (result !== '0px') { domStyle = dom.style; display = domStyle.display; domStyle.display = 'inline-block'; result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, null)).marginRight; domStyle.display = display; } return result; } function fixRightMarginAndInputFocus (dom, el, inline, style) { var result = style.marginRight, domStyle, cleaner, display; if (result !== '0px') { domStyle = dom.style; cleaner = getRightMarginFixCleaner(dom); display = domStyle.display; domStyle.display = 'inline-block'; result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, '')).marginRight; domStyle.display = display; cleaner(); } return result; } // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343 if (!supports.RightMargin) { styleHooks.marginRight = styleHooks['margin-right'] = { name: 'marginRight', // TODO - Touch should use conditional compilation here or ensure that the // underlying Ext.supports flags are set correctly... get: (supports.DisplayChangeInputSelectionBug || supports.DisplayChangeTextAreaSelectionBug) ? fixRightMarginAndInputFocus : fixRightMargin }; } if (!supports.TransparentColor) { colorStyles = ['background-color', 'border-color', 'color', 'outline-color']; for (i = colorStyles.length; i--; ) { name = colorStyles[i]; camel = Element.normalize(name); styleHooks[name] = styleHooks[camel] = { name: camel, get: fixTransparent }; } } // When elements are rotated 80 or 270 degrees, their border, margin and padding hooks // need to be rotated as well. proto.verticalStyleHooks90 = verticalStyleHooks90 = Ext.Object.chain(styleHooks); proto.verticalStyleHooks270 = verticalStyleHooks270 = Ext.Object.chain(styleHooks); verticalStyleHooks90.width = styleHooks.height || { name: 'height' }; verticalStyleHooks90.height = styleHooks.width || { name: 'width' }; verticalStyleHooks90['margin-top'] = { name: 'marginLeft' }; verticalStyleHooks90['margin-right'] = { name: 'marginTop' }; verticalStyleHooks90['margin-bottom'] = { name: 'marginRight' }; verticalStyleHooks90['margin-left'] = { name: 'marginBottom' }; verticalStyleHooks90['padding-top'] = { name: 'paddingLeft' }; verticalStyleHooks90['padding-right'] = { name: 'paddingTop' }; verticalStyleHooks90['padding-bottom'] = { name: 'paddingRight' }; verticalStyleHooks90['padding-left'] = { name: 'paddingBottom' }; verticalStyleHooks90['border-top'] = { name: 'borderLeft' }; verticalStyleHooks90['border-right'] = { name: 'borderTop' }; verticalStyleHooks90['border-bottom'] = { name: 'borderRight' }; verticalStyleHooks90['border-left'] = { name: 'borderBottom' }; verticalStyleHooks270.width = styleHooks.height || { name: 'height' }; verticalStyleHooks270.height = styleHooks.width || { name: 'width' }; verticalStyleHooks270['margin-top'] = { name: 'marginRight' }; verticalStyleHooks270['margin-right'] = { name: 'marginBottom' }; verticalStyleHooks270['margin-bottom'] = { name: 'marginLeft' }; verticalStyleHooks270['margin-left'] = { name: 'marginTop' }; verticalStyleHooks270['padding-top'] = { name: 'paddingRight' }; verticalStyleHooks270['padding-right'] = { name: 'paddingBottom' }; verticalStyleHooks270['padding-bottom'] = { name: 'paddingLeft' }; verticalStyleHooks270['padding-left'] = { name: 'paddingTop' }; verticalStyleHooks270['border-top'] = { name: 'borderRight' }; verticalStyleHooks270['border-right'] = { name: 'borderBottom' }; verticalStyleHooks270['border-bottom'] = { name: 'borderLeft' }; verticalStyleHooks270['border-left'] = { name: 'borderTop' }; /** * @property {Boolean} scopeCss * @member Ext * Set this to true before onReady to prevent any styling from being added to * the body element. By default a few styles such as font-family, and color * are added to the body element via a "x-body" class. When this is set to * `true` the "x-body" class is not added to the body element, but is added * to the elements of root-level containers instead. */ if (!Ext.scopeCss) { bodyCls.push(Ext.baseCSSPrefix + 'body'); } if (supports.Touch) { bodyCls.push(Ext.baseCSSPrefix + 'touch'); } if (Ext.isIE && Ext.isIE9m) { bodyCls.push(Ext.baseCSSPrefix + 'ie', Ext.baseCSSPrefix + 'ie9m'); // very often CSS needs to do checks like "IE7+" or "IE6 or 7". To help // reduce the clutter (since CSS/SCSS cannot do these tests), we add some // additional classes: // // x-ie7p : IE7+ : 7 <= ieVer // x-ie7m : IE7- : ieVer <= 7 // x-ie8p : IE8+ : 8 <= ieVer // x-ie8m : IE8- : ieVer <= 8 // x-ie9p : IE9+ : 9 <= ieVer // x-ie78 : IE7 or 8 : 7 <= ieVer <= 8 // bodyCls.push(Ext.baseCSSPrefix + 'ie8p'); if (Ext.isIE8) { bodyCls.push(Ext.baseCSSPrefix + 'ie8'); } else { bodyCls.push(Ext.baseCSSPrefix + 'ie9', Ext.baseCSSPrefix + 'ie9p'); } if (Ext.isIE8m) { bodyCls.push(Ext.baseCSSPrefix + 'ie8m'); } } if (Ext.isIE10) { bodyCls.push(Ext.baseCSSPrefix + 'ie10'); } if (Ext.isIE11) { bodyCls.push(Ext.baseCSSPrefix + 'ie11'); } if (Ext.isGecko) { bodyCls.push(Ext.baseCSSPrefix + 'gecko'); } if (Ext.isOpera) { bodyCls.push(Ext.baseCSSPrefix + 'opera'); } if (Ext.isOpera12m) { bodyCls.push(Ext.baseCSSPrefix + 'opera12m'); } if (Ext.isWebKit) { bodyCls.push(Ext.baseCSSPrefix + 'webkit'); } if (Ext.isSafari) { bodyCls.push(Ext.baseCSSPrefix + 'safari'); } if (Ext.isChrome) { bodyCls.push(Ext.baseCSSPrefix + 'chrome'); } if (Ext.isMac) { bodyCls.push(Ext.baseCSSPrefix + 'mac'); } if (Ext.isLinux) { bodyCls.push(Ext.baseCSSPrefix + 'linux'); } if (!supports.CSS3BorderRadius) { bodyCls.push(Ext.baseCSSPrefix + 'nbr'); } if (!supports.CSS3LinearGradient) { bodyCls.push(Ext.baseCSSPrefix + 'nlg'); } if (supports.Touch) { bodyCls.push(Ext.baseCSSPrefix + 'touch'); } //Ext.fly(document.documentElement).addCls(htmlCls); Ext.getBody().addCls(bodyCls); }, null, { priority: 1500 }); // onReady });