/*!
 * jQuery UI Tooltip - v1.10.4
 * http://jqueryui.com
 *
 * Copyright 2014 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/tooltip/
 */

define([
    'jquery',
    'jquery-ui-modules/core',
    'jquery-ui-modules/widget',
    'jquery-ui-modules/position'
], 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: "1.10.4",
        options: {
            content: function () {
                // support: IE<9, Opera in jQuery <1.7
                // .text() can't accept undefined, so coerce to a string
                var title = $(this).attr("title") || "";
                // Escape title, since we're going from an attribute to raw HTML
                return $("<a>").text(title).html();
            },
            hide: true,
            // Disabled elements have inconsistent behavior across browsers (#8661)
            items: "[title]:not([disabled])",
            position: {
                my: "left top+15",
                at: "left bottom",
                collision: "flipfit flip"
            },
            show: true,
            tooltipClass: null,
            track: false,

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

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

            // IDs of generated tooltips, needed for destroy
            this.tooltips = {};
            // IDs of parent tooltips where we removed the title attribute
            this.parents = {};

            if (this.options.disabled) {
                this._disable();
            }
        },

        _setOption: function (key, value) {
            var that = this;

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

            this._super(key, value);

            if (key === "content") {
                $.each(this.tooltips, function (id, element) {
                    that._updateContent(element);
                });
            }
        },

        _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).addBack().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).addBack().each(function () {
                var element = $(this);
                if (element.data("ui-tooltip-title")) {
                    element.attr("title", element.data("ui-tooltip-title"));
                }
            });
        },

        open: function (event) {
            var that = this,
                target = $(event ? event.target : this.element)
                // we need closest here due to mouseover bubbling,
                // but always pointing at the same event target
                    .closest(this.options.items);

            // No element to show a tooltip for or 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("ui-tooltip-open", true);

            // kill parent tooltips, custom or native, for hover
            if (event && event.type === "mouseover") {
                target.parents().each(function () {
                    var parent = $(this),
                        blurEvent;
                    if (parent.data("ui-tooltip-open")) {
                        blurEvent = $.Event("blur");
                        blurEvent.target = blurEvent.currentTarget = this;
                        that.close(blurEvent, true);
                    }
                    if (parent.attr("title")) {
                        parent.uniqueId();
                        that.parents[this.id] = {
                            element: this,
                            title: parent.attr("title")
                        };
                        parent.attr("title", "");
                    }
                });
            }

            this._updateContent(target, event);
        },

        _updateContent: function (target, event) {
            var content,
                contentOption = this.options.content,
                that = this,
                eventType = event ? event.type : null;

            if (typeof contentOption === "string") {
                return this._open(event, target, contentOption);
            }

            content = contentOption.call(target[0], function (response) {
                // ignore async response if tooltip was closed already
                if (!target.data("ui-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
                that._delay(function () {
                    // jQuery creates a special event for focusin when it doesn't
                    // exist natively. To improve performance, the native event
                    // object is reused and the type is changed. Therefore, we can't
                    // rely on the type being correct after the event finished
                    // bubbling, so we set it back to the previous value. (#8740)
                    if (event) {
                        event.type = eventType;
                    }
                    this._open(event, target, response);
                });
            });
            if (content) {
                this._open(event, target, content);
            }
        },

        _open: function (event, target, content) {
            var tooltip, events, delayedShow,
                positionOption = $.extend({}, this.options.position);

            if (!content) {
                return;
            }

            // Content can be updated multiple times. If the tooltip already
            // exists, then just update the content and bail.
            tooltip = this._find(target);
            if (tooltip.length) {
                tooltip.find(".ui-tooltip-content").html(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");
                }
            }

            tooltip = this._tooltip(target);
            addDescribedBy(target, tooltip.attr("id"));
            tooltip.find(".ui-tooltip-content").html(content);

            function position(event) {
                positionOption.of = event;
                if (tooltip.is(":hidden")) {
                    return;
                }
                tooltip.position(positionOption);
            }

            if (this.options.track && event && /^mouse/.test(event.type)) {
                this._on(this.document, {
                    mousemove: position
                });
                // trigger once to override element-relative positioning
                position(event);
            } else {
                tooltip.position($.extend({
                    of: target
                }, this.options.position));
            }

            tooltip.hide();

            this._show(tooltip, this.options.show);
            // Handle tracking tooltips that are shown with a delay (#8644). As soon
            // as the tooltip is visible, position the tooltip using the most recent
            // event.
            if (this.options.show && this.options.show.delay) {
                delayedShow = this.delayedShow = setInterval(function () {
                    if (tooltip.is(":visible")) {
                        position(positionOption.of);
                        clearInterval(delayedShow);
                    }
                }, $.fx.interval);
            }

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

            events = {
                keyup: function (event) {
                    if (event.keyCode === $.ui.keyCode.ESCAPE) {
                        var fakeEvent = $.Event(event);
                        fakeEvent.currentTarget = target[0];
                        this.close(fakeEvent, true);
                    }
                },
                remove: function () {
                    this._removeTooltip(tooltip);
                }
            };
            if (!event || event.type === "mouseover") {
                events.mouseleave = "close";
            }
            if (!event || event.type === "focusin") {
                events.focusout = "close";
            }
            this._on(true, target, events);
        },

        close: function (event) {
            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;
            }

            // Clear the interval for delayed tracking tooltips
            clearInterval(this.delayedShow);

            // 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 () {
                that._removeTooltip($(this));
            });

            target.removeData("ui-tooltip-open");
            this._off(target, "mouseleave focusout keyup");
            // Remove 'remove' binding only on delegated targets
            if (target[0] !== this.element[0]) {
                this._off(target, "remove");
            }
            this._off(this.document, "mousemove");

            if (event && event.type === "mouseleave") {
                $.each(this.parents, function (id, parent) {
                    $(parent.element).attr("title", parent.title);
                    delete that.parents[id];
                });
            }

            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);
            this.tooltips[id] = element;
            return tooltip;
        },

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

        _removeTooltip: function (tooltip) {
            tooltip.remove();
            delete this.tooltips[tooltip.attr("id")];
        },

        _destroy: function () {
            var that = this;

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

                // Remove immediately; destroying an open tooltip doesn't use the
                // hide animation
                $("#" + id).remove();

                // Restore the title
                if (element.data("ui-tooltip-title")) {
                    element.attr("title", element.data("ui-tooltip-title"));
                    element.removeData("ui-tooltip-title");
                }
            });
        }
    });

});
