All Downloads are FREE. Search and download functionalities are using the official Maven repository.

template.js.base.jquery.ui.autocomplete.js Maven / Gradle / Ivy

There is a newer version: 5.0.3
Show newest version
/*!
 * jQuery UI Autocomplete 1.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/autocomplete/
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function ($, undefined) {

    $.widget("ui.autocomplete", {
        version: "1.10.4",
        defaultElement: "",
        options: {
            appendTo: null,
            autoFocus: false,
            delay: 300,
            minLength: 1,
            position: {
                my: "left top",
                at: "left bottom",
                collision: "none"
            },
            source: null,

            // callbacks
            change: null,
            close: null,
            focus: null,
            open: null,
            response: null,
            search: null,
            select: null
        },

        requestIndex: 0,
        pending: 0,

        _create: function () {
            // Some browsers only repeat keydown events, not keypress events,
            // so we use the suppressKeyPress flag to determine if we've already
            // handled the keydown event. #7269
            // Unfortunately the code for & in keypress is the same as the up arrow,
            // so we use the suppressKeyPressRepeat flag to avoid handling keypress
            // events when we know the keydown event was used to modify the
            // search term. #7799
            var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
                nodeName = this.element[0].nodeName.toLowerCase(),
                isTextarea = nodeName === "textarea",
                isInput = nodeName === "input";

            this.isMultiLine =
                // Textareas are always multi-line
                isTextarea ? true :
                    // Inputs are always single-line, even if inside a contentEditable element
                    // IE also treats inputs as contentEditable
                    isInput ? false :
                        // All other element types are determined by whether or not they're contentEditable
                        this.element.prop("isContentEditable");

            this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
            this.isNewMenu = true;

            this.element
                .addClass("ui-autocomplete-input")
                .attr("autocomplete", "off");

            this._on(this.element, {
                keydown: function (event) {
                    if (this.element.prop("readOnly")) {
                        suppressKeyPress = true;
                        suppressInput = true;
                        suppressKeyPressRepeat = true;
                        return;
                    }

                    suppressKeyPress = false;
                    suppressInput = false;
                    suppressKeyPressRepeat = false;
                    var keyCode = $.ui.keyCode;
                    switch (event.keyCode) {
                        case keyCode.PAGE_UP:
                            suppressKeyPress = true;
                            this._move("previousPage", event);
                            break;
                        case keyCode.PAGE_DOWN:
                            suppressKeyPress = true;
                            this._move("nextPage", event);
                            break;
                        case keyCode.UP:
                            suppressKeyPress = true;
                            this._keyEvent("previous", event);
                            break;
                        case keyCode.DOWN:
                            suppressKeyPress = true;
                            this._keyEvent("next", event);
                            break;
                        case keyCode.ENTER:
                        case keyCode.NUMPAD_ENTER:
                            // when menu is open and has focus
                            if (this.menu.active) {
                                // #6055 - Opera still allows the keypress to occur
                                // which causes forms to submit
                                suppressKeyPress = true;
                                event.preventDefault();
                                this.menu.select(event);
                            }
                            break;
                        case keyCode.TAB:
                            if (this.menu.active) {
                                this.menu.select(event);
                            }
                            break;
                        case keyCode.ESCAPE:
                            if (this.menu.element.is(":visible")) {
                                this._value(this.term);
                                this.close(event);
                                // Different browsers have different default behavior for escape
                                // Single press can mean undo or clear
                                // Double press in IE means clear the whole form
                                event.preventDefault();
                            }
                            break;
                        default:
                            suppressKeyPressRepeat = true;
                            // search timeout should be triggered before the input value is changed
                            this._searchTimeout(event);
                            break;
                    }
                },
                keypress: function (event) {
                    if (suppressKeyPress) {
                        suppressKeyPress = false;
                        if (!this.isMultiLine || this.menu.element.is(":visible")) {
                            event.preventDefault();
                        }
                        return;
                    }
                    if (suppressKeyPressRepeat) {
                        return;
                    }

                    // replicate some key handlers to allow them to repeat in Firefox and Opera
                    var keyCode = $.ui.keyCode;
                    switch (event.keyCode) {
                        case keyCode.PAGE_UP:
                            this._move("previousPage", event);
                            break;
                        case keyCode.PAGE_DOWN:
                            this._move("nextPage", event);
                            break;
                        case keyCode.UP:
                            this._keyEvent("previous", event);
                            break;
                        case keyCode.DOWN:
                            this._keyEvent("next", event);
                            break;
                    }
                },
                input: function (event) {
                    if (suppressInput) {
                        suppressInput = false;
                        event.preventDefault();
                        return;
                    }
                    this._searchTimeout(event);
                },
                focus: function () {
                    this.selectedItem = null;
                    this.previous = this._value();
                },
                blur: function (event) {
                    if (this.cancelBlur) {
                        delete this.cancelBlur;
                        return;
                    }

                    clearTimeout(this.searching);
                    this.close(event);
                    this._change(event);
                }
            });

            this._initSource();
            this.menu = $("
    ") .addClass("ui-autocomplete ui-front") .appendTo(this._appendTo()) .menu({ // disable ARIA support, the live region takes care of that role: null }) .hide() .data("ui-menu"); this._on(this.menu.element, { mousedown: function (event) { // prevent moving focus out of the text field event.preventDefault(); // IE doesn't prevent moving focus even with event.preventDefault() // so we set a flag to know when we should ignore the blur event this.cancelBlur = true; this._delay(function () { delete this.cancelBlur; }); // clicking on the scrollbar causes focus to shift to the body // but we can't detect a mouseup or a click immediately afterward // so we have to track the next mousedown and close the menu if // the user clicks somewhere outside of the autocomplete var menuElement = this.menu.element[ 0 ]; if (!$(event.target).closest(".ui-menu-item").length) { this._delay(function () { var that = this; this.document.one("mousedown", function (event) { if (event.target !== that.element[ 0 ] && event.target !== menuElement && !$.contains(menuElement, event.target)) { that.close(); } }); }); } }, menufocus: function (event, ui) { // support: Firefox // Prevent accidental activation of menu items in Firefox (#7024 #9118) if (this.isNewMenu) { this.isNewMenu = false; if (event.originalEvent && /^mouse/.test(event.originalEvent.type)) { this.menu.blur(); this.document.one("mousemove", function () { $(event.target).trigger(event.originalEvent); }); return; } } var item = ui.item.data("ui-autocomplete-item"); if (false !== this._trigger("focus", event, { item: item })) { // use value to match what will end up in the input, if it was a key event if (event.originalEvent && /^key/.test(event.originalEvent.type)) { this._value(item.value); } } else { // Normally the input is populated with the item's value as the // menu is navigated, causing screen readers to notice a change and // announce the item. Since the focus event was canceled, this doesn't // happen, so we update the live region so that screen readers can // still notice the change and announce it. this.liveRegion.text(item.value); } }, menuselect: function (event, ui) { var item = ui.item.data("ui-autocomplete-item"), previous = this.previous; // only trigger when focus was lost (click on menu) if (this.element[0] !== this.document[0].activeElement) { this.element.focus(); this.previous = previous; // #6109 - IE triggers two focus events and the second // is asynchronous, so we need to reset the previous // term synchronously and asynchronously :-( this._delay(function () { this.previous = previous; this.selectedItem = item; }); } if (false !== this._trigger("select", event, { item: item })) { this._value(item.value); } // reset the term after the select event // this allows custom select handling to work properly this.term = this._value(); this.close(event); this.selectedItem = item; } }); this.liveRegion = $("", { role: "status", "aria-live": "polite" }) .addClass("ui-helper-hidden-accessible") .insertBefore(this.element); // turning off autocomplete prevents the browser from remembering the // value when navigating through history, so we re-enable autocomplete // if the page is unloaded before the widget is destroyed. #7790 this._on(this.window, { beforeunload: function () { this.element.removeAttr("autocomplete"); } }); }, _destroy: function () { clearTimeout(this.searching); this.element .removeClass("ui-autocomplete-input") .removeAttr("autocomplete"); this.menu.element.remove(); this.liveRegion.remove(); }, _setOption: function (key, value) { this._super(key, value); if (key === "source") { this._initSource(); } if (key === "appendTo") { this.menu.element.appendTo(this._appendTo()); } if (key === "disabled" && value && this.xhr) { this.xhr.abort(); } }, _appendTo: function () { var element = this.options.appendTo; if (element) { element = element.jquery || element.nodeType ? $(element) : this.document.find(element).eq(0); } if (!element) { element = this.element.closest(".ui-front"); } if (!element.length) { element = this.document[0].body; } return element; }, _initSource: function () { var array, url, that = this; if ($.isArray(this.options.source)) { array = this.options.source; this.source = function (request, response) { response($.ui.autocomplete.filter(array, request.term)); }; } else if (typeof this.options.source === "string") { url = this.options.source; this.source = function (request, response) { if (that.xhr) { that.xhr.abort(); } that.xhr = $.ajax({ url: url, data: request, dataType: "json", success: function (data) { response(data); }, error: function () { response([]); } }); }; } else { this.source = this.options.source; } }, _searchTimeout: function (event) { clearTimeout(this.searching); this.searching = this._delay(function () { // only search if the value has changed if (this.term !== this._value()) { this.selectedItem = null; this.search(null, event); } }, this.options.delay); }, search: function (value, event) { value = value != null ? value : this._value(); // always save the actual value, not the one passed as an argument this.term = this._value(); if (value.length < this.options.minLength) { return this.close(event); } if (this._trigger("search", event) === false) { return; } return this._search(value); }, _search: function (value) { this.pending++; this.element.addClass("ui-autocomplete-loading"); this.cancelSearch = false; this.source({ term: value }, this._response()); }, _response: function () { var index = ++this.requestIndex; return $.proxy(function (content) { if (index === this.requestIndex) { this.__response(content); } this.pending--; if (!this.pending) { this.element.removeClass("ui-autocomplete-loading"); } }, this); }, __response: function (content) { if (content) { content = this._normalize(content); } this._trigger("response", null, { content: content }); if (!this.options.disabled && content && content.length && !this.cancelSearch) { this._suggest(content); this._trigger("open"); } else { // use ._close() instead of .close() so we don't cancel future searches this._close(); } }, close: function (event) { this.cancelSearch = true; this._close(event); }, _close: function (event) { if (this.menu.element.is(":visible")) { this.menu.element.hide(); this.menu.blur(); this.isNewMenu = true; this._trigger("close", event); } }, _change: function (event) { if (this.previous !== this._value()) { this._trigger("change", event, { item: this.selectedItem }); } }, _normalize: function (items) { // assume all items have the right format when the first item is complete if (items.length && items[0].label && items[0].value) { return items; } return $.map(items, function (item) { if (typeof item === "string") { return { label: item, value: item }; } return $.extend({ label: item.label || item.value, value: item.value || item.label }, item); }); }, _suggest: function (items) { var ul = this.menu.element.empty(); this._renderMenu(ul, items); this.isNewMenu = true; this.menu.refresh(); // size and position menu ul.show(); this._resizeMenu(); ul.position($.extend({ of: this.element }, this.options.position)); if (this.options.autoFocus) { this.menu.next(); } }, _resizeMenu: function () { var ul = this.menu.element; ul.outerWidth(Math.max( // Firefox wraps long text (possibly a rounding bug) // so we add 1px to avoid the wrapping (#7513) ul.width("").outerWidth() + 1, this.element.outerWidth() )); }, _renderMenu: function (ul, items) { var that = this; $.each(items, function (index, item) { that._renderItemData(ul, item); }); }, _renderItemData: function (ul, item) { return this._renderItem(ul, item).data("ui-autocomplete-item", item); }, _renderItem: function (ul, item) { return $("
  • ") .append($("").text(item.label)) .appendTo(ul); }, _move: function (direction, event) { if (!this.menu.element.is(":visible")) { this.search(null, event); return; } if (this.menu.isFirstItem() && /^previous/.test(direction) || this.menu.isLastItem() && /^next/.test(direction)) { this._value(this.term); this.menu.blur(); return; } this.menu[ direction ](event); }, widget: function () { return this.menu.element; }, _value: function () { return this.valueMethod.apply(this.element, arguments); }, _keyEvent: function (keyEvent, event) { if (!this.isMultiLine || this.menu.element.is(":visible")) { this._move(keyEvent, event); // prevents moving cursor to beginning/end of the text field in some browsers event.preventDefault(); } } }); $.extend($.ui.autocomplete, { escapeRegex: function (value) { return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); }, filter: function (array, term) { var matcher = new RegExp($.ui.autocomplete.escapeRegex(term), "i"); return $.grep(array, function (value) { return matcher.test(value.label || value.value || value); }); } }); // live region extension, adding a `messages` option // NOTE: This is an experimental API. We are still investigating // a full solution for string manipulation and internationalization. $.widget("ui.autocomplete", $.ui.autocomplete, { options: { messages: { noResults: "No search results.", results: function (amount) { return amount + ( amount > 1 ? " results are" : " result is" ) + " available, use up and down arrow keys to navigate."; } } }, __response: function (content) { var message; this._superApply(arguments); if (this.options.disabled || this.cancelSearch) { return; } if (content && content.length) { message = this.options.messages.results(content.length); } else { message = this.options.messages.noResults; } this.liveRegion.text(message); } }); }(jQuery));




© 2015 - 2024 Weber Informatics LLC | Privacy Policy