| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-tip-ToolTip'>/**</span> * ToolTip is a {@link Ext.tip.Tip} implementation that handles the common case of displaying a * tooltip when hovering over a certain element or elements on the page. It allows fine-grained * control over the tooltip's alignment relative to the target element or mouse, and the timing * of when it is automatically shown and hidden. * * This implementation does **not** have a built-in method of automatically populating the tooltip's * text based on the target element; you must either configure a fixed {@link #html} value for each * ToolTip instance, or implement custom logic (e.g. in a {@link #beforeshow} event listener) to * generate the appropriate tooltip content on the fly. See {@link Ext.tip.QuickTip} for a more * convenient way of automatically populating and configuring a tooltip based on specific DOM * attributes of each target element. * * # Basic Example * *     var tip = Ext.create('Ext.tip.ToolTip', { *         target: 'clearButton', *         html: 'Press this button to clear the form' *     }); * * {@img Ext.tip.ToolTip/Ext.tip.ToolTip1.png Basic Ext.tip.ToolTip} * * # Delegation * * In addition to attaching a ToolTip to a single element, you can also use delegation to attach * one ToolTip to many elements under a common parent. This is more efficient than creating many * ToolTip instances. To do this, point the {@link #target} config to a common ancestor of all the * elements, and then set the {@link #delegate} config to a CSS selector that will select all the * appropriate sub-elements. * * When using delegation, it is likely that you will want to programmatically change the content * of the ToolTip based on each delegate element; you can do this by implementing a custom * listener for the {@link #beforeshow} event. Example: * *     var store = Ext.create('Ext.data.ArrayStore', { *         fields: ['company', 'price', 'change'], *         data: [ *             ['3m Co',                               71.72, 0.02], *             ['Alcoa Inc',                           29.01, 0.42], *             ['Altria Group Inc',                    83.81, 0.28], *             ['American Express Company',            52.55, 0.01], *             ['American International Group, Inc.',  64.13, 0.31], *             ['AT&T Inc.',                           31.61, -0.48] *         ] *     }); * *     var grid = Ext.create('Ext.grid.Panel', { *         title: 'Array Grid', *         store: store, *         columns: [ *             {text: 'Company', flex: 1, dataIndex: 'company'}, *             {text: 'Price', width: 75, dataIndex: 'price'}, *             {text: 'Change', width: 75, dataIndex: 'change'} *         ], *         height: 200, *         width: 400, *         renderTo: Ext.getBody() *     }); * *     grid.getView().on('render', function(view) { *         view.tip = Ext.create('Ext.tip.ToolTip', { *             // The overall target element. *             target: view.el, *             // Each grid row causes its own separate show and hide. *             delegate: view.itemSelector, *             // Moving within the row should not hide the tip. *             trackMouse: true, *             // Render immediately so that tip.body can be referenced prior to the first show. *             renderTo: Ext.getBody(), *             listeners: { *                 // Change content dynamically depending on which element triggered the show. *                 beforeshow: function updateTipBody(tip) { *                     tip.update('Over company "' + view.getRecord(tip.triggerElement).get('company') + '"'); *                 } *             } *         }); *     }); * * {@img Ext.tip.ToolTip/Ext.tip.ToolTip2.png Ext.tip.ToolTip with delegation} * * # Alignment * * The following configuration properties allow control over how the ToolTip is aligned relative to * the target element and/or mouse pointer: * * - {@link #anchor} * - {@link #anchorToTarget} * - {@link #anchorOffset} * - {@link #trackMouse} * - {@link #mouseOffset} * * # Showing/Hiding * * The following configuration properties allow control over how and when the ToolTip is automatically * shown and hidden: * * - {@link #autoHide} * - {@link #showDelay} * - {@link #hideDelay} * - {@link #dismissDelay} * * @docauthor Jason Johnston <jason@sencha.com> */Ext.define('Ext.tip.ToolTip', {    extend: 'Ext.tip.Tip',    alias: 'widget.tooltip',    alternateClassName: 'Ext.ToolTip',<span id='Ext-tip-ToolTip-property-triggerElement'>    /**</span>     * @property {HTMLElement} triggerElement     * When a ToolTip is configured with the `{@link #delegate}`     * option to cause selected child elements of the `{@link #target}`     * Element to each trigger a separate show event, this property is set to     * the DOM element which triggered the show.     */<span id='Ext-tip-ToolTip-cfg-target'>    /**</span>     * @cfg {HTMLElement/Ext.Element/String} target     * The target element or string id to monitor for mouseover events to trigger     * showing this ToolTip.     */<span id='Ext-tip-ToolTip-cfg-autoHide'>    /**</span>     * @cfg {Boolean} [autoHide=true]     * True to automatically hide the tooltip after the     * mouse exits the target element or after the `{@link #dismissDelay}`     * has expired if set.  If `{@link #closable} = true`     * a close tool button will be rendered into the tooltip header.     */    autoHide: true,    <span id='Ext-tip-ToolTip-cfg-showDelay'>    /**</span>     * @cfg {Number} showDelay     * Delay in milliseconds before the tooltip displays after the mouse enters the target element.     */    showDelay: 500,<span id='Ext-tip-ToolTip-cfg-hideDelay'>    /**</span>     * @cfg {Number} hideDelay     * Delay in milliseconds after the mouse exits the target element but before the tooltip actually hides.     * Set to 0 for the tooltip to hide immediately.     */    hideDelay: 200,<span id='Ext-tip-ToolTip-cfg-dismissDelay'>    /**</span>     * @cfg {Number} dismissDelay     * Delay in milliseconds before the tooltip automatically hides. To disable automatic hiding, set     * dismissDelay = 0.     */    dismissDelay: 5000,<span id='Ext-tip-ToolTip-cfg-mouseOffset'>    /**</span>     * @cfg {Number[]} [mouseOffset=[15,18]]     * An XY offset from the mouse position where the tooltip should be shown.     */<span id='Ext-tip-ToolTip-cfg-trackMouse'>    /**</span>     * @cfg {Boolean} trackMouse     * True to have the tooltip follow the mouse as it moves over the target element.     */    trackMouse: false,<span id='Ext-tip-ToolTip-cfg-anchor'>    /**</span>     * @cfg {String} anchor     * If specified, indicates that the tip should be anchored to a     * particular side of the target element or mouse pointer ("top", "right", "bottom",     * or "left"), with an arrow pointing back at the target or mouse pointer. If     * {@link #constrainPosition} is enabled, this will be used as a preferred value     * only and may be flipped as needed.     */<span id='Ext-tip-ToolTip-cfg-anchorToTarget'>    /**</span>     * @cfg {Boolean} anchorToTarget     * True to anchor the tooltip to the target element, false to anchor it relative to the mouse coordinates.     * When `anchorToTarget` is true, use `{@link #defaultAlign}` to control tooltip alignment to the     * target element.  When `anchorToTarget` is false, use `{@link #anchor}` instead to control alignment.     */    anchorToTarget: true,<span id='Ext-tip-ToolTip-cfg-anchorOffset'>    /**</span>     * @cfg {Number} anchorOffset     * A numeric pixel value used to offset the default position of the anchor arrow.  When the anchor     * position is on the top or bottom of the tooltip, `anchorOffset` will be used as a horizontal offset.     * Likewise, when the anchor position is on the left or right side, `anchorOffset` will be used as     * a vertical offset.     */    anchorOffset: 0,<span id='Ext-tip-ToolTip-cfg-delegate'>    /**</span>     * @cfg {String} delegate     *     * A {@link Ext.DomQuery DomQuery} selector which allows selection of individual elements within the     * `{@link #target}` element to trigger showing and hiding the ToolTip as the mouse moves within the     * target.     *     * When specified, the child element of the target which caused a show event is placed into the     * `{@link #triggerElement}` property before the ToolTip is shown.     *     * This may be useful when a Component has regular, repeating elements in it, each of which need a     * ToolTip which contains information specific to that element.     *     * See the delegate example in class documentation of {@link Ext.tip.ToolTip}.     */    // private    targetCounter: 0,    quickShowInterval: 250,    // private    initComponent: function() {        var me = this;        me.callParent(arguments);        me.lastActive = new Date();        me.setTarget(me.target);        me.origAnchor = me.anchor;    },    // private    onRender: function(ct, position) {        var me = this;        me.callParent(arguments);        me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();        me.anchorEl = me.el.createChild({            cls: Ext.baseCSSPrefix + 'tip-anchor ' + me.anchorCls        });    },<span id='Ext-tip-ToolTip-method-setTarget'>    /**</span>     * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.     * @param {String/HTMLElement/Ext.Element} t The Element, HtmlElement, or ID of an element to bind to     */    setTarget: function(target) {        var me = this,            t = Ext.get(target),            tg;        if (me.target) {            tg = Ext.get(me.target);            me.mun(tg, 'mouseover', me.onTargetOver, me);            me.mun(tg, 'mouseout', me.onTargetOut, me);            me.mun(tg, 'mousemove', me.onMouseMove, me);        }        me.target = t;        if (t) {            me.mon(t, {                // TODO - investigate why IE6/7 seem to fire recursive resize in e.getXY                // breaking QuickTip#onTargetOver (EXTJSIV-1608)                freezeEvent: true,                mouseover: me.onTargetOver,                mouseout: me.onTargetOut,                mousemove: me.onMouseMove,                scope: me            });        }        if (me.anchor) {            me.anchorTarget = me.target;        }    },    // private    onMouseMove: function(e) {        var me = this,            t = me.delegate ? e.getTarget(me.delegate) : me.triggerElement = true,            xy;        if (t) {            me.targetXY = e.getXY();            if (t === me.triggerElement) {                if (!me.hidden && me.trackMouse) {                    xy = me.getTargetXY();                    if (me.constrainPosition) {                        xy = me.el.adjustForConstraints(xy, me.el.getScopeParent());                    }                    me.setPagePosition(xy);                }            } else {                me.hide();                me.lastActive = new Date(0);                me.onTargetOver(e);            }        } else if ((!me.closable && me.isVisible()) && me.autoHide !== false) {            me.hide();        }    },    // private    getTargetXY: function() {        var me = this,            mouseOffset,            offsets, xy, dw, dh, de, bd, scrollX, scrollY, axy, sz, constrainPosition;        if (me.delegate) {            me.anchorTarget = me.triggerElement;        }        if (me.anchor) {            me.targetCounter++;            offsets = me.getOffsets();            xy = (me.anchorToTarget && !me.trackMouse) ? me.el.getAlignToXY(me.anchorTarget, me.getAnchorAlign()) : me.targetXY;            dw = Ext.Element.getViewWidth() - 5;            dh = Ext.Element.getViewHeight() - 5;            de = document.documentElement;            bd = document.body;            scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5;            scrollY = (de.scrollTop || bd.scrollTop || 0) + 5;            axy = [xy[0] + offsets[0], xy[1] + offsets[1]];            sz = me.getSize();            constrainPosition = me.constrainPosition;            me.anchorEl.removeCls(me.anchorCls);            if (me.targetCounter < 2 && constrainPosition) {                if (axy[0] < scrollX) {                    if (me.anchorToTarget) {                        me.defaultAlign = 'l-r';                        if (me.mouseOffset) {                            me.mouseOffset[0] *= -1;                        }                    }                    me.anchor = 'left';                    return me.getTargetXY();                }                if (axy[0] + sz.width > dw) {                    if (me.anchorToTarget) {                        me.defaultAlign = 'r-l';                        if (me.mouseOffset) {                            me.mouseOffset[0] *= -1;                        }                    }                    me.anchor = 'right';                    return me.getTargetXY();                }                if (axy[1] < scrollY) {                    if (me.anchorToTarget) {                        me.defaultAlign = 't-b';                        if (me.mouseOffset) {                            me.mouseOffset[1] *= -1;                        }                    }                    me.anchor = 'top';                    return me.getTargetXY();                }                if (axy[1] + sz.height > dh) {                    if (me.anchorToTarget) {                        me.defaultAlign = 'b-t';                        if (me.mouseOffset) {                            me.mouseOffset[1] *= -1;                        }                    }                    me.anchor = 'bottom';                    return me.getTargetXY();                }            }            me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();            me.anchorEl.addCls(me.anchorCls);            me.targetCounter = 0;            return axy;        } else {            mouseOffset = me.getMouseOffset();            return (me.targetXY) ? [me.targetXY[0] + mouseOffset[0], me.targetXY[1] + mouseOffset[1]] : mouseOffset;        }    },    getMouseOffset: function() {        var me = this,        offset = me.anchor ? [0, 0] : [15, 18];        if (me.mouseOffset) {            offset[0] += me.mouseOffset[0];            offset[1] += me.mouseOffset[1];        }        return offset;    },    // private    getAnchorPosition: function() {        var me = this,            m;        if (me.anchor) {            me.tipAnchor = me.anchor.charAt(0);        } else {            m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);            //<debug>            if (!m) {                Ext.Error.raise('The AnchorTip.defaultAlign value "' + me.defaultAlign + '" is invalid.');            }            //</debug>            me.tipAnchor = m[1].charAt(0);        }        switch (me.tipAnchor) {        case 't':            return 'top';        case 'b':            return 'bottom';        case 'r':            return 'right';        }        return 'left';    },    // private    getAnchorAlign: function() {        switch (this.anchor) {        case 'top':            return 'tl-bl';        case 'left':            return 'tl-tr';        case 'right':            return 'tr-tl';        default:            return 'bl-tl';        }    },    // private    getOffsets: function() {        var me = this,            mouseOffset,            offsets,            ap = me.getAnchorPosition().charAt(0);        if (me.anchorToTarget && !me.trackMouse) {            switch (ap) {            case 't':                offsets = [0, 9];                break;            case 'b':                offsets = [0, -13];                break;            case 'r':                offsets = [ - 13, 0];                break;            default:                offsets = [9, 0];                break;            }        } else {            switch (ap) {            case 't':                offsets = [ - 15 - me.anchorOffset, 30];                break;            case 'b':                offsets = [ - 19 - me.anchorOffset, -13 - me.el.dom.offsetHeight];                break;            case 'r':                offsets = [ - 15 - me.el.dom.offsetWidth, -13 - me.anchorOffset];                break;            default:                offsets = [25, -13 - me.anchorOffset];                break;            }        }        mouseOffset = me.getMouseOffset();        offsets[0] += mouseOffset[0];        offsets[1] += mouseOffset[1];        return offsets;    },    // private    onTargetOver: function(e) {        var me = this,            t;        if (me.disabled || e.within(me.target.dom, true)) {            return;        }        t = e.getTarget(me.delegate);        if (t) {            me.triggerElement = t;            me.clearTimer('hide');            me.targetXY = e.getXY();            me.delayShow();        }    },    // private    delayShow: function() {        var me = this;        if (me.hidden && !me.showTimer) {            if (Ext.Date.getElapsed(me.lastActive) < me.quickShowInterval) {                me.show();            } else {                me.showTimer = Ext.defer(me.show, me.showDelay, me);            }        }        else if (!me.hidden && me.autoHide !== false) {            me.show();        }    },        onShowVeto: function(){        this.callParent();        this.clearTimer('show');    },    // private    onTargetOut: function(e) {        var me = this;        // If disabled, moving within the current target, ignore the mouseout        // EventObject.within is the only correct way to determine this.        if (me.disabled || e.within(me.target.dom, true)) {            return;        }        me.clearTimer('show');        if (me.autoHide !== false) {            me.delayHide();        }    },    // private    delayHide: function() {        var me = this;        if (!me.hidden && !me.hideTimer) {            me.hideTimer = Ext.defer(me.hide, me.hideDelay, me);        }    },<span id='Ext-tip-ToolTip-method-hide'>    /**</span>     * Hides this tooltip if visible.     */    hide: function() {        var me = this;        me.clearTimer('dismiss');        me.lastActive = new Date();        if (me.anchorEl) {            me.anchorEl.hide();        }        me.callParent(arguments);        delete me.triggerElement;    },<span id='Ext-tip-ToolTip-method-show'>    /**</span>     * Shows this tooltip at the current event target XY position.     */    show: function() {        var me = this;        // Show this Component first, so that sizing can be calculated        // pre-show it off screen so that the el will have dimensions        this.callParent();        if (this.hidden === false) {            me.setPagePosition(-10000, -10000);            if (me.anchor) {                me.anchor = me.origAnchor;            }                        if (!me.calledFromShowAt) {                me.showAt(me.getTargetXY());            }            if (me.anchor) {                me.syncAnchor();                me.anchorEl.show();            } else {                me.anchorEl.hide();            }        }    },    // inherit docs    showAt: function(xy) {        var me = this;        me.lastActive = new Date();        me.clearTimers();        me.calledFromShowAt = true;        // Only call if this is hidden. May have been called from show above.        if (!me.isVisible()) {            this.callParent(arguments);        }        // Show may have been vetoed.        if (me.isVisible()) {            me.setPagePosition(xy[0], xy[1]);            if (me.constrainPosition || me.constrain) {                me.doConstrain();            }            me.toFront(true);            me.el.sync(true);            if (me.dismissDelay && me.autoHide !== false) {                me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me);            }            if (me.anchor) {                me.syncAnchor();                if (!me.anchorEl.isVisible()) {                    me.anchorEl.show();                }            } else {                me.anchorEl.hide();            }        }        delete me.calledFromShowAt;    },    // private    syncAnchor: function() {        var me = this,            anchorPos,            targetPos,            offset;        switch (me.tipAnchor.charAt(0)) {        case 't':            anchorPos = 'b';            targetPos = 'tl';            offset = [20 + me.anchorOffset, 1];            break;        case 'r':            anchorPos = 'l';            targetPos = 'tr';            offset = [ - 1, 12 + me.anchorOffset];            break;        case 'b':            anchorPos = 't';            targetPos = 'bl';            offset = [20 + me.anchorOffset, -1];            break;        default:            anchorPos = 'r';            targetPos = 'tl';            offset = [1, 12 + me.anchorOffset];            break;        }        me.anchorEl.alignTo(me.el, anchorPos + '-' + targetPos, offset);        me.anchorEl.setStyle('z-index', parseInt(me.el.getZIndex(), 10) || 0 + 1).setVisibilityMode(Ext.Element.DISPLAY);    },    // private    setPagePosition: function(x, y) {        var me = this;        me.callParent(arguments);        if (me.anchor) {            me.syncAnchor();        }    },    // private    clearTimer: function(name) {        name = name + 'Timer';        clearTimeout(this[name]);        delete this[name];    },    // private    clearTimers: function() {        var me = this;        me.clearTimer('show');        me.clearTimer('dismiss');        me.clearTimer('hide');    },    // private    onShow: function() {        var me = this;        me.callParent();        me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);    },    // private    onHide: function() {        var me = this;        me.callParent();        me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);    },    // private    onDocMouseDown: function(e) {        var me = this;        if (!me.closable && !e.within(me.el.dom)) {            me.disable();            Ext.defer(me.doEnable, 100, me);        }    },    // private    doEnable: function() {        if (!this.isDestroyed) {            this.enable();        }    },    // private    onDisable: function() {        this.callParent();        this.clearTimers();        this.hide();    },    beforeDestroy: function() {        var me = this;        me.clearTimers();        Ext.destroy(me.anchorEl);        delete me.anchorEl;        delete me.target;        delete me.anchorTarget;        delete me.triggerElement;        me.callParent();    },    // private    onDestroy: function() {        Ext.getDoc().un('mousedown', this.onDocMouseDown, this);        this.callParent();    }});</pre></body></html>
 |