/** * @class Ext.dom.Element * @override 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', CLIP = 'clip', ORIGINALDISPLAY = 'originalDisplay', VISMODE = 'visibilityMode', ISVISIBLE = 'isVisible', OFFSETCLASS = Ext.baseCSSPrefix + 'hidden-offsets', CLIPCLASS = Ext.baseCSSPrefix + 'hidden-clip', boxMarkup = [ '<div class="{0}-tl" role="presentation">', '<div class="{0}-tr" role="presentation">', '<div class="{0}-tc" role="presentation"></div>', '</div>', '</div>', '<div class="{0}-ml" role="presentation">', '<div class="{0}-mr" role="presentation">', '<div class="{0}-mc" role="presentation"></div>', '</div>', '</div>', '<div class="{0}-bl" role="presentation">', '<div class="{0}-br" role="presentation">', '<div class="{0}-bc" role="presentation"></div>', '</div>', '</div>' ].join(''), scriptTagRe = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig, replaceScriptTagRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, srcRe = /\ssrc=([\'\"])(.*?)\1/i, nonSpaceRe = /\S/, typeRe = /\stype=([\'\"])(.*?)\1/i, adjustDirect2DTableRe = /table-row|table-.*-group/, 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 = {}, getVisMode = function(el){ var data = el.getData(), visMode = data[VISMODE]; if (visMode === undefined) { data[VISMODE] = visMode = Element.VISIBILITY; } return visMode; }, syncContentFly, emptyRange = DOC.createRange ? DOC.createRange() : null; //<feature legacyBrowser> if (Ext.isIE8) { var 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, clearGarbageFn = function() { var len = destroyQueue.length, i; for (i = 0; i < len; i++) { garbageBin.appendChild(destroyQueue[i]); } garbageBin.innerHTML = ''; destroyQueue.length = 0; }; //<debug> clearGarbageFn.$skipTimerCheck = true; //</debug> clearGarbage = Ext.Function.createBuffered(clearGarbageFn, 10); } //</feature> 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 // We want to expose destroyQueue on the prototype for testing purposes //<debug> if (WIN.__UNIT_TESTING__) { E.destroyQueue = destroyQueue; } //</debug> }, statics: { 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)); } }, /** * 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 * Returns the fractional portion of this element's measurement in the given dimension. * (IE9+ only) * @return {Number} */ adjustDirect2DDimension: function(dimension) { var me = this, dom = me.dom, display = me.getStyle('display'), inlineDisplay = dom.style.display, inlinePosition = dom.style.position, originIndex = dimension === WIDTH ? 0 : 1, currentStyle = dom.currentStyle, floating; if (display === 'inline') { dom.style.display = 'inline-block'; } dom.style.position = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static'; // floating will contain digits that appears after the decimal point // if height or width are set to auto we fallback to msTransformOrigin calculation // Use currentStyle here instead of getStyle. In some difficult to reproduce // instances it resets the scrollWidth of the element floating = (parseFloat(currentStyle[dimension]) || parseFloat(currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1; dom.style.position = inlinePosition; if (display === 'inline') { dom.style.display = inlineDisplay; } return floating; }, /** * @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), userConfig: 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; }, /** * Calls `{@link #addAnimation}` and returns this Element (for call chaining). For * details, see `{@link #addAnimation}`. * * @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) { this.addAnimation(config); return this; }, /** * Starts a 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 (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.fx.Anim} The new animation. */ addAnimation: 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 anim; }, /** * @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: * * <div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div> * <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div> * <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div> * * 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", "<div class='" + cls + "' role='presentation'>" + Ext.String.format(boxMarkup, cls) + "</div>")); 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; }, /** * @method * 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.peekData(), maskEl, maskMsg; if (dom) { if (me.isAnimate) { me.stopAnimation(true); } me.removeAnchor(); } if (me.deferredFocusTimer) { clearTimeout(me.deferredFocusTimer); me.deferredFocusTimer = null; } me.callParent(); //<feature legacyBrowser> // 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(); } //</feature> 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, animFly = new Ext.dom.Fly(); me.animate(Ext.apply({}, o, { opacity: 1, internalListeners: { beforeanimate: function(anim) { // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); // restore any visibility/display that may have // been applied by a fadeout animation if (animFly.isStyle('display', 'none')) { animFly.setDisplayed(''); } else { animFly.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, animFly = new Ext.dom.Fly(); o = Ext.apply({ opacity: 0, internalListeners: { afteranimate: function(anim) { if (anim.to.opacity === 0) { // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); if (o.useDisplay) { animFly.setDisplayed(false); } else { animFly.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, me._getDisplay()); // 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, animFly = new Ext.dom.Fly(), beforeAnim; color = color || '#C3DAF9'; count = count || 1; obj = obj || {}; beforeAnim = function() { var animScope = this, box, proxy, proxyAnim; // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); animFly.show(); box = animFly.getBox(); proxy = Ext.getBody().createChild({ role: 'presentation', id: animFly.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<len; i++) { h = parseInt(v[i], 10); color += (h < 16 ? '0' : '') + h.toString(16); } } else { v = v.replace('#', ''); color += v.length === 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v; } return(color.length > 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 * `setPositioning` 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, animFly = new Ext.dom.Fly(), beforeAnim; anchor = anchor || "b"; beforeAnim = function() { // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); var width = animFly.getWidth(), height = animFly.getHeight(), xy = animFly.getXY(), position = animFly.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 () { // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); if (animFly) { animFly.hide(); animFly.clearOpacity(); animFly.setPositioning(position); } }); }; me.animate(Ext.applyIf(obj || {}, { duration: 500, easing: 'ease-out', listeners: { beforeanimate: beforeAnim } })); return me; }, //<feature legacyBrowser> getTextSelection: function () { var ret = this.callParent(); if (typeof ret[0] !== 'number') { var dom = this.dom; var doc = dom.ownerDocument; var range = doc.selection.createRange(); var textRange = dom.createTextRange(); textRange.setEndPoint('EndToStart', range); ret[0] = textRange.text.length; ret[1] = ret[0] + range.text.length; } return ret; }, //</feature> /** * @override * Hide this element - Uses display mode to determine whether to use "display", * "visibility", "offsets", or "clip". 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 = {}, animFly = new Ext.dom.Fly(), 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() { // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); restore = dom.style[attr]; animFly.clearOpacity(); animFly.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; }, /** * 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); }, /** * 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; }, /** * 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.fly(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); } if (dom === DOC.body) { maskEl.addCls(Ext.baseCSSPrefix + 'mask-fixed'); } // When masking the body, don't touch its tabbable state me.saveTabbableState({ skipSelf: dom === DOC.body }); // 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; }, /** * Measures and returns the size of this element. When `dimension` is `null` (or * not specified), this will be an object with `width` and `height` properties. * * If `dimension` is `'w'` the value returned will be this element's width. If * `dimension` is `'h'` the returned value will be this element's height. * * Unlike `getWidth` and `getHeight` this method only returns "precise" (sub-pixel) * sizes based on the `getBoundingClientRect` API. * * @param {'w'/'h'} [dimension] Specifies which dimension is desired. If omitted * then an object with `width` and `height` properties is returned. * @return {Number/Object} This element's width, height or both as a readonly * object. This object may be the direct result of `getBoundingClientRect` and * hence immutable on some browsers. * @private * @since 6.5.0 */ measure: function (dimension) { var me = this, dom = me.dom, includeWidth = dimension !== 'h', includeHeight = dimension !== 'w', height, rect, width; // Use the viewport height if they are asking for body height if (dom.nodeName === 'BODY') { height = includeHeight && Element.getViewportHeight(); width = includeWidth && Element.getViewportWidth(); rect = dimension ? null : { width: width, height: height }; } else { rect = dom.getBoundingClientRect(); if (Ext.isIE8) { // IE8 does not provide width/height *and* the rect is readonly rect = { width: rect.right - rect.left, height: rect.bottom - rect.top }; } height = rect.height; width = rect.width; // IE9/10 Direct2D dimension rounding bug if (Ext.supports.Direct2DBug) { if (includeHeight) { height += me.adjustDirect2DDimension(HEIGHT); } if (includeWidth) { width += me.adjustDirect2DDimension(WIDTH); } rect = dimension ? null : { width: width, height: height }; } } // NOTE: The modern override ignores all these IE8/9/10 issues return dimension ? (includeWidth ? width : height) : rect; }, /** * 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, animFly = new Ext.dom.Fly(), 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() { // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); animFly.clearOpacity(); animFly.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() { // Reattach to the DOM in case the caller animated a Fly // in which case the dom reference will have changed by now. animFly.attach(dom); if (obj.useDisplay) { animFly.setDisplayed(false); } else { animFly.hide(); } animFly.setStyle(originalStyles); Ext.callback(obj.callback, obj.scope); }); }; me.animate({ duration: obj.duration, easing: obj.easing, listeners: { beforeanimate: { fn: beforeAnim } } }); return me; }, //<feature legacyBrowser> // 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(); } }, //</feature> /** * 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.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] Pass `true` to look for and process scripts. * @param {Function} [callback] For async script loading you can be notified when the update completes. * @param {Object} [scope=`this`] The scope (`this` reference) in which to execute the callback. * * Also used as the scope for any *inline* script source if the `loadScripts` parameter is `true`. * Scripts with a `src` attribute cannot be executed in this scope. * * Defaults to this Element. * @return {Ext.dom.Element} this */ setHtml: function(html, loadScripts, callback, scope) { 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 += '<span id="' + id + '" role="presentation"></span>'; 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) { if (scope) { Ext.functionFactory(match[2]).call(scope); } else { Ext.globalEval(match[2]); } } } Ext.callback(callback, scope || 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, animFly, 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; case CLIP: visMode = Element.CLIP; 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.CLIP) { me[visible ? 'removeCls' : 'addCls'](CLIPCLASS); } 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' }; } animFly = new Ext.dom.Fly(), me.animate(Ext.applyIf({ callback: function() { if (!visible) {