corrade-http-templates – Rev 42

Subversion Repositories:
Rev:
/*!
 * jQuery UI Autocomplete 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Autocomplete
//>>group: Widgets
//>>description: Lists suggested words as the user is typing.
//>>docs: http://api.jqueryui.com/autocomplete/
//>>demos: http://jqueryui.com/autocomplete/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/autocomplete.css
//>>css.theme: ../../themes/base/theme.css

( function( factory ) {
        if ( typeof define === "function" && define.amd ) {

                // AMD. Register as an anonymous module.
                define( [
                        "jquery",
                        "./menu",
                        "../keycode",
                        "../position",
                        "../safe-active-element",
                        "../version",
                        "../widget"
                ], factory );
        } else {

                // Browser globals
                factory( jQuery );
        }
}( function( $ ) {

$.widget( "ui.autocomplete", {
        version: "1.12.1",
        defaultElement: "<input>",
        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";

                // Textareas are always multi-line
                // Inputs are always single-line, even if inside a contentEditable element
                // IE also treats inputs as contentEditable
                // All other element types are determined by whether or not they're contentEditable
                this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );

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

                this._addClass( "ui-autocomplete-input" );
                this.element.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:

                                        // 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" ) ) {
                                                if ( !this.isMultiLine ) {
                                                        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 = $( "<ul>" )
                        .appendTo( this._appendTo() )
                        .menu( {

                                // disable ARIA support, the live region takes care of that
                                role: null
                        } )
                        .hide()
                        .menu( "instance" );

                this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
                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;

                                        // Support: IE 8 only
                                        // Right clicking a menu item or selecting text from the menu items will
                                        // result in focus moving out of the input. However, we've already received
                                        // and ignored the blur event because of the cancelBlur flag set above. So
                                        // we restore focus to ensure that the menu closes properly based on the user's
                                        // next actions.
                                        if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
                                                this.element.trigger( "focus" );
                                        }
                                } );
                        },
                        menufocus: function( event, ui ) {
                                var label, item;

                                // 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;
                                        }
                                }

                                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 );
                                        }
                                }

                                // Announce the value in the liveRegion
                                label = ui.item.attr( "aria-label" ) || item.value;
                                if ( label && $.trim( label ).length ) {
                                        this.liveRegion.children().hide();
                                        $( "<div>" ).text( label ).appendTo( this.liveRegion );
                                }
                        },
                        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 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
                                        this.element.trigger( "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 = $( "<div>", {
                        role: "status",
                        "aria-live": "assertive",
                        "aria-relevant": "additions"
                } )
                        .appendTo( this.document[ 0 ].body );

                this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );

                // 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.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();
                }
        },

        _isEventTargetInWidget: function( event ) {
                var menuElement = this.menu.element[ 0 ];

                return event.target === this.element[ 0 ] ||
                        event.target === menuElement ||
                        $.contains( menuElement, event.target );
        },

        _closeOnClickOutside: function( event ) {
                if ( !this._isEventTargetInWidget( event ) ) {
                        this.close();
                }
        },

        _appendTo: function() {
                var element = this.options.appendTo;

                if ( element ) {
                        element = element.jquery || element.nodeType ?
                                $( element ) :
                                this.document.find( element ).eq( 0 );
                }

                if ( !element || !element[ 0 ] ) {
                        element = this.element.closest( ".ui-front, dialog" );
                }

                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() {

                        // Search if the value has changed, or if the user retypes the same value (see #7434)
                        var equalValues = this.term === this._value(),
                                menuVisible = this.menu.element.is( ":visible" ),
                                modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;

                        if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
                                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._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._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 ) {

                // Remove the handler that closes the menu on outside clicks
                this._off( this.document, "mousedown" );

                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( {}, item, {
                                label: item.label || item.value,
                                value: item.value || item.label
                        } );
                } );
        },

        _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();
                }

                // Listen for interactions outside of the widget (#6642)
                this._on( this.document, {
                        mousedown: "_closeOnClickOutside"
                } );
        },

        _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 $( "<li>" )
                        .append( $( "<div>" ).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 ) ) {

                        if ( !this.isMultiLine ) {
                                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();
                }
        },

        // Support: Chrome <=50
        // We should be able to just use this.element.prop( "isContentEditable" )
        // but hidden elements always report false in Chrome.
        // https://code.google.com/p/chromium/issues/detail?id=313082
        _isContentEditable: function( element ) {
                if ( !element.length ) {
                        return false;
                }

                var editable = element.prop( "contentEditable" );

                if ( editable === "inherit" ) {
                  return this._isContentEditable( element.parent() );
                }

                return editable === "true";
        }
} );

$.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.children().hide();
                $( "<div>" ).text( message ).appendTo( this.liveRegion );
        }
} );

return $.ui.autocomplete;

} ) );