Subversion Repositories Sites.obs-saisons.fr

Rev

Blame | Last modification | View Log | RSS feed

/*
 * jQuery UI Autocomplete 1.8.5
 *
 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Autocomplete
 *
 * Depends:
 *      jquery.ui.core.js
 *      jquery.ui.widget.js
 *      jquery.ui.position.js
 */
(function( $, undefined ) {

$.widget( "ui.autocomplete", {
        options: {
                appendTo: "body",
                delay: 300,
                minLength: 1,
                position: {
                        my: "left top",
                        at: "left bottom",
                        collision: "none"
                },
                source: null
        },
        _create: function() {
                var self = this,
                        doc = this.element[ 0 ].ownerDocument;
                this.element
                        .addClass( "ui-autocomplete-input" )
                        .attr( "autocomplete", "off" )
                        // TODO verify these actually work as intended
                        .attr({
                                role: "textbox",
                                "aria-autocomplete": "list",
                                "aria-haspopup": "true"
                        })
                        .bind( "keydown.autocomplete", function( event ) {
                                if ( self.options.disabled ) {
                                        return;
                                }

                                var keyCode = $.ui.keyCode;
                                switch( event.keyCode ) {
                                case keyCode.PAGE_UP:
                                        self._move( "previousPage", event );
                                        break;
                                case keyCode.PAGE_DOWN:
                                        self._move( "nextPage", event );
                                        break;
                                case keyCode.UP:
                                        self._move( "previous", event );
                                        // prevent moving cursor to beginning of text field in some browsers
                                        event.preventDefault();
                                        break;
                                case keyCode.DOWN:
                                        self._move( "next", event );
                                        // prevent moving cursor to end of text field in some browsers
                                        event.preventDefault();
                                        break;
                                case keyCode.ENTER:
                                case keyCode.NUMPAD_ENTER:
                                        // when menu is open or has focus
                                        if ( self.menu.element.is( ":visible" ) ) {
                                                event.preventDefault();
                                        }
                                        //passthrough - ENTER and TAB both select the current element
                                case keyCode.TAB:
                                        if ( !self.menu.active ) {
                                                return;
                                        }
                                        self.menu.select( event );
                                        break;
                                case keyCode.ESCAPE:
                                        self.element.val( self.term );
                                        self.close( event );
                                        break;
                                default:
                                        // keypress is triggered before the input value is changed
                                        clearTimeout( self.searching );
                                        self.searching = setTimeout(function() {
                                                // only search if the value has changed
                                                if ( self.term != self.element.val() ) {
                                                        self.selectedItem = null;
                                                        self.search( null, event );
                                                }
                                        }, self.options.delay );
                                        break;
                                }
                        })
                        .bind( "focus.autocomplete", function() {
                                if ( self.options.disabled ) {
                                        return;
                                }

                                self.selectedItem = null;
                                self.previous = self.element.val();
                        })
                        .bind( "blur.autocomplete", function( event ) {
                                if ( self.options.disabled ) {
                                        return;
                                }

                                clearTimeout( self.searching );
                                // clicks on the menu (or a button to trigger a search) will cause a blur event
                                self.closing = setTimeout(function() {
                                        self.close( event );
                                        self._change( event );
                                }, 150 );
                        });
                this._initSource();
                this.response = function() {
                        return self._response.apply( self, arguments );
                };
                this.menu = $( "<ul></ul>" )
                        .addClass( "ui-autocomplete" )
                        .appendTo( $( this.options.appendTo || "body", doc )[0] )
                        // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
                        .mousedown(function( event ) {
                                // 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 = self.menu.element[ 0 ];
                                if ( event.target === menuElement ) {
                                        setTimeout(function() {
                                                $( document ).one( 'mousedown', function( event ) {
                                                        if ( event.target !== self.element[ 0 ] &&
                                                                event.target !== menuElement &&
                                                                !$.ui.contains( menuElement, event.target ) ) {
                                                                self.close();
                                                        }
                                                });
                                        }, 1 );
                                }

                                // use another timeout to make sure the blur-event-handler on the input was already triggered
                                setTimeout(function() {
                                        clearTimeout( self.closing );
                                }, 13);
                        })
                        .menu({
                                focus: function( event, ui ) {
                                        var item = ui.item.data( "item.autocomplete" );
                                        if ( false !== self._trigger( "focus", null, { item: item } ) ) {
                                                // use value to match what will end up in the input, if it was a key event
                                                if ( /^key/.test(event.originalEvent.type) ) {
                                                        self.element.val( item.value );
                                                }
                                        }
                                },
                                selected: function( event, ui ) {
                                        var item = ui.item.data( "item.autocomplete" ),
                                                previous = self.previous;

                                        // only trigger when focus was lost (click on menu)
                                        if ( self.element[0] !== doc.activeElement ) {
                                                self.element.focus();
                                                self.previous = previous;
                                        }

                                        if ( false !== self._trigger( "select", event, { item: item } ) ) {
                                                self.term = item.value;
                                                self.element.val( item.value );
                                        }

                                        self.close( event );
                                        self.selectedItem = item;
                                },
                                blur: function( event, ui ) {
                                        // don't set the value of the text field if it's already correct
                                        // this prevents moving the cursor unnecessarily
                                        if ( self.menu.element.is(":visible") &&
                                                ( self.element.val() !== self.term ) ) {
                                                self.element.val( self.term );
                                        }
                                }
                        })
                        .zIndex( this.element.zIndex() + 1 )
                        // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
                        .css({ top: 0, left: 0 })
                        .hide()
                        .data( "menu" );
                if ( $.fn.bgiframe ) {
                         this.menu.element.bgiframe();
                }
        },

        destroy: function() {
                this.element
                        .removeClass( "ui-autocomplete-input" )
                        .removeAttr( "autocomplete" )
                        .removeAttr( "role" )
                        .removeAttr( "aria-autocomplete" )
                        .removeAttr( "aria-haspopup" );
                this.menu.element.remove();
                $.Widget.prototype.destroy.call( this );
        },

        _setOption: function( key, value ) {
                $.Widget.prototype._setOption.apply( this, arguments );
                if ( key === "source" ) {
                        this._initSource();
                }
                if ( key === "appendTo" ) {
                        this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
                }
        },

        _initSource: function() {
                var self = this,
                        array,
                        url;
                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 (self.xhr) {
                                        self.xhr.abort();
                                }
                                self.xhr = $.getJSON( url, request, function( data, status, xhr ) {
                                        if ( xhr === self.xhr ) {
                                                response( data );
                                        }
                                        self.xhr = null;
                                });
                        };
                } else {
                        this.source = this.options.source;
                }
        },

        search: function( value, event ) {
                value = value != null ? value : this.element.val();

                // always save the actual value, not the one passed as an argument
                this.term = this.element.val();

                if ( value.length < this.options.minLength ) {
                        return this.close( event );
                }

                clearTimeout( this.closing );
                if ( this._trigger("search") === false ) {
                        return;
                }

                return this._search( value );
        },

        _search: function( value ) {
                this.element.addClass( "ui-autocomplete-loading" );

                this.source( { term: value }, this.response );
        },

        _response: function( content ) {
                if ( content.length ) {
                        content = this._normalize( content );
                        this._suggest( content );
                        this._trigger( "open" );
                } else {
                        this.close();
                }
                this.element.removeClass( "ui-autocomplete-loading" );
        },

        close: function( event ) {
                clearTimeout( this.closing );
                if ( this.menu.element.is(":visible") ) {
                        this._trigger( "close", event );
                        this.menu.element.hide();
                        this.menu.deactivate();
                }
        },
        
        _change: function( event ) {
                if ( this.previous !== this.element.val() ) {
                        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()
                                .zIndex( this.element.zIndex() + 1 ),
                        menuWidth,
                        textWidth;
                this._renderMenu( ul, items );
                // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
                this.menu.deactivate();
                this.menu.refresh();
                this.menu.element.show().position( $.extend({
                        of: this.element
                }, this.options.position ));

                menuWidth = ul.width( "" ).outerWidth();
                textWidth = this.element.outerWidth();
                ul.outerWidth( Math.max( menuWidth, textWidth ) );
        },

        _renderMenu: function( ul, items ) {
                var self = this;
                $.each( items, function( index, item ) {
                        self._renderItem( ul, item );
                });
        },

        _renderItem: function( ul, item) {
                return $( "<li></li>" )
                        .data( "item.autocomplete", item )
                        .append( $( "<a></a>" ).text( item.label ) )
                        .appendTo( ul );
        },

        _move: function( direction, event ) {
                if ( !this.menu.element.is(":visible") ) {
                        this.search( null, event );
                        return;
                }
                if ( this.menu.first() && /^previous/.test(direction) ||
                                this.menu.last() && /^next/.test(direction) ) {
                        this.element.val( this.term );
                        this.menu.deactivate();
                        return;
                }
                this.menu[ direction ]( event );
        },

        widget: function() {
                return this.menu.element;
        }
});

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

}( jQuery ));

/*
 * jQuery UI Menu (not officially released)
 * 
 * This widget isn't yet finished and the API is subject to change. We plan to finish
 * it for the next release. You're welcome to give it a try anyway and give us feedback,
 * as long as you're okay with migrating your code later on. We can help with that, too.
 *
 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Menu
 *
 * Depends:
 *      jquery.ui.core.js
 *  jquery.ui.widget.js
 */
(function($) {

$.widget("ui.menu", {
        _create: function() {
                var self = this;
                this.element
                        .addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
                        .attr({
                                role: "listbox",
                                "aria-activedescendant": "ui-active-menuitem"
                        })
                        .click(function( event ) {
                                if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
                                        return;
                                }
                                // temporary
                                event.preventDefault();
                                self.select( event );
                        });
                this.refresh();
        },
        
        refresh: function() {
                var self = this;

                // don't refresh list items that are already adapted
                var items = this.element.children("li:not(.ui-menu-item):has(a)")
                        .addClass("ui-menu-item")
                        .attr("role", "menuitem");
                
                items.children("a")
                        .addClass("ui-corner-all")
                        .attr("tabindex", -1)
                        // mouseenter doesn't work with event delegation
                        .mouseenter(function( event ) {
                                self.activate( event, $(this).parent() );
                        })
                        .mouseleave(function() {
                                self.deactivate();
                        });
        },

        activate: function( event, item ) {
                this.deactivate();
                if (this.hasScroll()) {
                        var offset = item.offset().top - this.element.offset().top,
                                scroll = this.element.attr("scrollTop"),
                                elementHeight = this.element.height();
                        if (offset < 0) {
                                this.element.attr("scrollTop", scroll + offset);
                        } else if (offset >= elementHeight) {
                                this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());
                        }
                }
                this.active = item.eq(0)
                        .children("a")
                                .addClass("ui-state-hover")
                                .attr("id", "ui-active-menuitem")
                        .end();
                this._trigger("focus", event, { item: item });
        },

        deactivate: function() {
                if (!this.active) { return; }

                this.active.children("a")
                        .removeClass("ui-state-hover")
                        .removeAttr("id");
                this._trigger("blur");
                this.active = null;
        },

        next: function(event) {
                this.move("next", ".ui-menu-item:first", event);
        },

        previous: function(event) {
                this.move("prev", ".ui-menu-item:last", event);
        },

        first: function() {
                return this.active && !this.active.prevAll(".ui-menu-item").length;
        },

        last: function() {
                return this.active && !this.active.nextAll(".ui-menu-item").length;
        },

        move: function(direction, edge, event) {
                if (!this.active) {
                        this.activate(event, this.element.children(edge));
                        return;
                }
                var next = this.active[direction + "All"](".ui-menu-item").eq(0);
                if (next.length) {
                        this.activate(event, next);
                } else {
                        this.activate(event, this.element.children(edge));
                }
        },

        // TODO merge with previousPage
        nextPage: function(event) {
                if (this.hasScroll()) {
                        // TODO merge with no-scroll-else
                        if (!this.active || this.last()) {
                                this.activate(event, this.element.children(":first"));
                                return;
                        }
                        var base = this.active.offset().top,
                                height = this.element.height(),
                                result = this.element.children("li").filter(function() {
                                        var close = $(this).offset().top - base - height + $(this).height();
                                        // TODO improve approximation
                                        return close < 10 && close > -10;
                                });

                        // TODO try to catch this earlier when scrollTop indicates the last page anyway
                        if (!result.length) {
                                result = this.element.children(":last");
                        }
                        this.activate(event, result);
                } else {
                        this.activate(event, this.element.children(!this.active || this.last() ? ":first" : ":last"));
                }
        },

        // TODO merge with nextPage
        previousPage: function(event) {
                if (this.hasScroll()) {
                        // TODO merge with no-scroll-else
                        if (!this.active || this.first()) {
                                this.activate(event, this.element.children(":last"));
                                return;
                        }

                        var base = this.active.offset().top,
                                height = this.element.height();
                                result = this.element.children("li").filter(function() {
                                        var close = $(this).offset().top - base + height - $(this).height();
                                        // TODO improve approximation
                                        return close < 10 && close > -10;
                                });

                        // TODO try to catch this earlier when scrollTop indicates the last page anyway
                        if (!result.length) {
                                result = this.element.children(":first");
                        }
                        this.activate(event, result);
                } else {
                        this.activate(event, this.element.children(!this.active || this.first() ? ":last" : ":first"));
                }
        },

        hasScroll: function() {
                return this.element.height() < this.element.attr("scrollHeight");
        },

        select: function( event ) {
                this._trigger("selected", event, { item: this.active });
        }
});

}(jQuery));