adobe/brackets

View on GitHub
src/extensions/default/JavaScriptQuickEdit/unittest-files/jquery-ui/ui/jquery.ui.tooltip.js

Summary

Maintainability
A
1 hr
Test Coverage
/*!
 * jQuery UI Tooltip @VERSION
 *
 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Depends:
 *    jquery.ui.core.js
 *    jquery.ui.widget.js
 *    jquery.ui.position.js
 */
(function( $ ) {

var increments = 0;

function addDescribedBy( elem, id ) {
    var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
    describedby.push( id );
    elem
        .data( "ui-tooltip-id", id )
        .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
}

function removeDescribedBy( elem ) {
    var id = elem.data( "ui-tooltip-id" ),
        describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
        index = $.inArray( id, describedby );
    if ( index !== -1 ) {
        describedby.splice( index, 1 );
    }

    elem.removeData( "ui-tooltip-id" );
    describedby = $.trim( describedby.join( " " ) );
    if ( describedby ) {
        elem.attr( "aria-describedby", describedby );
    } else {
        elem.removeAttr( "aria-describedby" );
    }
}

$.widget( "ui.tooltip", {
    version: "@VERSION",
    options: {
        content: function() {
            return $( this ).attr( "title" );
        },
        hide: true,
        items: "[title]",
        position: {
            my: "left+15 center",
            at: "right center",
            collision: "flipfit flipfit"
        },
        show: true,
        tooltipClass: null,

        // callbacks
        close: null,
        open: null
    },

    _create: function() {
        this._bind({
            mouseover: "open",
            focusin: "open"
        });

        // IDs of generated tooltips, needed for destroy
        this.tooltips = {};
    },

    _setOption: function( key, value ) {
        if ( key === "disabled" ) {
            this[ value ? "_disable" : "_enable" ]();
            this.options[ key ] = value;
            // disable element style changes
            return;
        }
        this._super( key, value );
    },

    _disable: function() {
        var that = this;

        // close open tooltips
        $.each( this.tooltips, function( id, element ) {
            var event = $.Event( "blur" );
            event.target = event.currentTarget = element[0];
            that.close( event, true );
        });

        // remove title attributes to prevent native tooltips
        this.element.find( this.options.items ).andSelf().each(function() {
            var element = $( this );
            if ( element.is( "[title]" ) ) {
                element
                    .data( "ui-tooltip-title", element.attr( "title" ) )
                    .attr( "title", "" );
            }
        });
    },

    _enable: function() {
        // restore title attributes
        this.element.find( this.options.items ).andSelf().each(function() {
            var element = $( this );
            if ( element.data( "ui-tooltip-title" ) ) {
                element.attr( "title", element.data( "ui-tooltip-title" ) );
            }
        });
    },

    open: function( event ) {
        var content,
            that = this,
            target = $( event ? event.target : this.element )
                .closest( this.options.items );

        // if ui-tooltip-id exists, then the tooltip is already open
        if ( !target.length || target.data( "ui-tooltip-id" ) ) {
            return;
        }

        if ( target.attr( "title" ) ) {
            target.data( "ui-tooltip-title", target.attr( "title" ) );
        }

        target.data( "tooltip-open", true );

        content = this.options.content.call( target[0], function( response ) {
            // ignore async response if tooltip was closed already
            if ( !target.data( "tooltip-open" ) ) {
                return;
            }
            // IE may instantly serve a cached response for ajax requests
            // delay this call to _open so the other call to _open runs first
            setTimeout(function() {
                that._open( event, target, response );
            }, 1 );
        });
        if ( content ) {
            that._open( event, target, content );
        }
    },

    _open: function( event, target, content ) {
        if ( !content ) {
            return;
        }

        // if we have a title, clear it to prevent the native tooltip
        // we have to check first to avoid defining a title if none exists
        // (we don't want to cause an element to start matching [title])
        //
        // We use removeAttr only for key events, to allow IE to export the correct
        // accessible attributes. For mouse events, set to empty string to avoid
        // native tooltip showing up (happens only when removing inside mouseover).
        if ( target.is( "[title]" ) ) {
            if ( event && event.type === "mouseover" ) {
                target.attr( "title", "" );
            } else {
                target.removeAttr( "title" );
            }
        }

        // ajaxy tooltip can update an existing one
        var tooltip = this._find( target );
        if ( !tooltip.length ) {
            tooltip = this._tooltip( target );
            addDescribedBy( target, tooltip.attr( "id" ) );
        }
        tooltip.find( ".ui-tooltip-content" ).html( content );
        tooltip
            .stop( true )
            .position( $.extend({
                of: target
            }, this.options.position ) )
            .hide();

        this._show( tooltip, this.options.show );

        this._trigger( "open", event, { tooltip: tooltip } );

        this._bind( target, {
            mouseleave: "close",
            focusout: "close",
            keyup: function( event ) {
                if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
                    var fakeEvent = $.Event(event);
                    fakeEvent.currentTarget = target[0];
                    this.close( fakeEvent, true );
                }
            }
        });
    },

    close: function( event, force ) {
        var that = this,
            target = $( event ? event.currentTarget : this.element ),
            tooltip = this._find( target );

        // disabling closes the tooltip, so we need to track when we're closing
        // to avoid an infinite loop in case the tooltip becomes disabled on close
        if ( this.closing ) {
            return;
        }

        // don't close if the element has focus
        // this prevents the tooltip from closing if you hover while focused
        //
        // we have to check the event type because tabbing out of the document
        // may leave the element as the activeElement
        if ( !force && event && event.type !== "focusout" &&
                this.document[0].activeElement === target[0] ) {
            return;
        }

        // only set title if we had one before (see comment in _open())
        if ( target.data( "ui-tooltip-title" ) ) {
            target.attr( "title", target.data( "ui-tooltip-title" ) );
        }

        removeDescribedBy( target );

        tooltip.stop( true );
        this._hide( tooltip, this.options.hide, function() {
            $( this ).remove();
            delete that.tooltips[ this.id ];
        });

        target.removeData( "tooltip-open" );
        target.unbind( "mouseleave.tooltip focusout.tooltip keyup.tooltip" );

        this.closing = true;
        this._trigger( "close", event, { tooltip: tooltip } );
        this.closing = false;
    },

    _tooltip: function( element ) {
        var id = "ui-tooltip-" + increments++,
            tooltip = $( "<div>" )
                .attr({
                    id: id,
                    role: "tooltip"
                })
                .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
                    ( this.options.tooltipClass || "" ) );
        $( "<div>" )
            .addClass( "ui-tooltip-content" )
            .appendTo( tooltip );
        tooltip.appendTo( this.document[0].body );
        if ( $.fn.bgiframe ) {
            tooltip.bgiframe();
        }
        this.tooltips[ id ] = element;
        return tooltip;
    },

    _find: function( target ) {
        var id = target.data( "ui-tooltip-id" );
        return id ? $( "#" + id ) : $();
    },

    _destroy: function() {
        $.each( this.tooltips, function( id ) {
            $( "#" + id ).remove();
        });
    }
});

}( jQuery ) );