UNPKG

alpaca

Version:

Alpaca provides the easiest and fastest way to generate interactive forms for the web and mobile devices. It runs simply as HTML5 or more elaborately using Bootstrap, jQuery Mobile or jQuery UI. Alpaca uses Handlebars to process JSON schema and provide

617 lines (509 loc) 13.8 kB
/*! * jQuery UI Selectmenu 1.11.2 * http://jqueryui.com * * Copyright 2014 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/selectmenu */ (function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define([ "jquery", "./core", "./widget", "./position", "./menu" ], factory ); } else { // Browser globals factory( jQuery ); } }(function( $ ) { return $.widget( "ui.selectmenu", { version: "1.11.2", defaultElement: "<select>", options: { appendTo: null, disabled: null, icons: { button: "ui-icon-triangle-1-s" }, position: { my: "left top", at: "left bottom", collision: "none" }, width: null, // callbacks change: null, close: null, focus: null, open: null, select: null }, _create: function() { var selectmenuId = this.element.uniqueId().attr( "id" ); this.ids = { element: selectmenuId, button: selectmenuId + "-button", menu: selectmenuId + "-menu" }; this._drawButton(); this._drawMenu(); if ( this.options.disabled ) { this.disable(); } }, _drawButton: function() { var that = this, tabindex = this.element.attr( "tabindex" ); // Associate existing label with the new button this.label = $( "label[for='" + this.ids.element + "']" ).attr( "for", this.ids.button ); this._on( this.label, { click: function( event ) { this.button.focus(); event.preventDefault(); } }); // Hide original select element this.element.hide(); // Create button this.button = $( "<span>", { "class": "ui-selectmenu-button ui-widget ui-state-default ui-corner-all", tabindex: tabindex || this.options.disabled ? -1 : 0, id: this.ids.button, role: "combobox", "aria-expanded": "false", "aria-autocomplete": "list", "aria-owns": this.ids.menu, "aria-haspopup": "true" }) .insertAfter( this.element ); $( "<span>", { "class": "ui-icon " + this.options.icons.button }) .prependTo( this.button ); this.buttonText = $( "<span>", { "class": "ui-selectmenu-text" }) .appendTo( this.button ); this._setText( this.buttonText, this.element.find( "option:selected" ).text() ); this._resizeButton(); this._on( this.button, this._buttonEvents ); this.button.one( "focusin", function() { // Delay rendering the menu items until the button receives focus. // The menu may have already been rendered via a programmatic open. if ( !that.menuItems ) { that._refreshMenu(); } }); this._hoverable( this.button ); this._focusable( this.button ); }, _drawMenu: function() { var that = this; // Create menu this.menu = $( "<ul>", { "aria-hidden": "true", "aria-labelledby": this.ids.button, id: this.ids.menu }); // Wrap menu this.menuWrap = $( "<div>", { "class": "ui-selectmenu-menu ui-front" }) .append( this.menu ) .appendTo( this._appendTo() ); // Initialize menu widget this.menuInstance = this.menu .menu({ role: "listbox", select: function( event, ui ) { event.preventDefault(); // support: IE8 // If the item was selected via a click, the text selection // will be destroyed in IE that._setSelection(); that._select( ui.item.data( "ui-selectmenu-item" ), event ); }, focus: function( event, ui ) { var item = ui.item.data( "ui-selectmenu-item" ); // Prevent inital focus from firing and check if its a newly focused item if ( that.focusIndex != null && item.index !== that.focusIndex ) { that._trigger( "focus", event, { item: item } ); if ( !that.isOpen ) { that._select( item, event ); } } that.focusIndex = item.index; that.button.attr( "aria-activedescendant", that.menuItems.eq( item.index ).attr( "id" ) ); } }) .menu( "instance" ); // Adjust menu styles to dropdown this.menu .addClass( "ui-corner-bottom" ) .removeClass( "ui-corner-all" ); // Don't close the menu on mouseleave this.menuInstance._off( this.menu, "mouseleave" ); // Cancel the menu's collapseAll on document click this.menuInstance._closeOnDocumentClick = function() { return false; }; // Selects often contain empty items, but never contain dividers this.menuInstance._isDivider = function() { return false; }; }, refresh: function() { this._refreshMenu(); this._setText( this.buttonText, this._getSelectedItem().text() ); if ( !this.options.width ) { this._resizeButton(); } }, _refreshMenu: function() { this.menu.empty(); var item, options = this.element.find( "option" ); if ( !options.length ) { return; } this._parseOptions( options ); this._renderMenu( this.menu, this.items ); this.menuInstance.refresh(); this.menuItems = this.menu.find( "li" ).not( ".ui-selectmenu-optgroup" ); item = this._getSelectedItem(); // Update the menu to have the correct item focused this.menuInstance.focus( null, item ); this._setAria( item.data( "ui-selectmenu-item" ) ); // Set disabled state this._setOption( "disabled", this.element.prop( "disabled" ) ); }, open: function( event ) { if ( this.options.disabled ) { return; } // If this is the first time the menu is being opened, render the items if ( !this.menuItems ) { this._refreshMenu(); } else { // Menu clears focus on close, reset focus to selected item this.menu.find( ".ui-state-focus" ).removeClass( "ui-state-focus" ); this.menuInstance.focus( null, this._getSelectedItem() ); } this.isOpen = true; this._toggleAttr(); this._resizeMenu(); this._position(); this._on( this.document, this._documentClick ); this._trigger( "open", event ); }, _position: function() { this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) ); }, close: function( event ) { if ( !this.isOpen ) { return; } this.isOpen = false; this._toggleAttr(); this.range = null; this._off( this.document ); this._trigger( "close", event ); }, widget: function() { return this.button; }, menuWidget: function() { return this.menu; }, _renderMenu: function( ul, items ) { var that = this, currentOptgroup = ""; $.each( items, function( index, item ) { if ( item.optgroup !== currentOptgroup ) { $( "<li>", { "class": "ui-selectmenu-optgroup ui-menu-divider" + ( item.element.parent( "optgroup" ).prop( "disabled" ) ? " ui-state-disabled" : "" ), text: item.optgroup }) .appendTo( ul ); currentOptgroup = item.optgroup; } that._renderItemData( ul, item ); }); }, _renderItemData: function( ul, item ) { return this._renderItem( ul, item ).data( "ui-selectmenu-item", item ); }, _renderItem: function( ul, item ) { var li = $( "<li>" ); if ( item.disabled ) { li.addClass( "ui-state-disabled" ); } this._setText( li, item.label ); return li.appendTo( ul ); }, _setText: function( element, value ) { if ( value ) { element.text( value ); } else { element.html( "&#160;" ); } }, _move: function( direction, event ) { var item, next, filter = ".ui-menu-item"; if ( this.isOpen ) { item = this.menuItems.eq( this.focusIndex ); } else { item = this.menuItems.eq( this.element[ 0 ].selectedIndex ); filter += ":not(.ui-state-disabled)"; } if ( direction === "first" || direction === "last" ) { next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 ); } else { next = item[ direction + "All" ]( filter ).eq( 0 ); } if ( next.length ) { this.menuInstance.focus( event, next ); } }, _getSelectedItem: function() { return this.menuItems.eq( this.element[ 0 ].selectedIndex ); }, _toggle: function( event ) { this[ this.isOpen ? "close" : "open" ]( event ); }, _setSelection: function() { var selection; if ( !this.range ) { return; } if ( window.getSelection ) { selection = window.getSelection(); selection.removeAllRanges(); selection.addRange( this.range ); // support: IE8 } else { this.range.select(); } // support: IE // Setting the text selection kills the button focus in IE, but // restoring the focus doesn't kill the selection. this.button.focus(); }, _documentClick: { mousedown: function( event ) { if ( !this.isOpen ) { return; } if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" + this.ids.button ).length ) { this.close( event ); } } }, _buttonEvents: { // Prevent text selection from being reset when interacting with the selectmenu (#10144) mousedown: function() { var selection; if ( window.getSelection ) { selection = window.getSelection(); if ( selection.rangeCount ) { this.range = selection.getRangeAt( 0 ); } // support: IE8 } else { this.range = document.selection.createRange(); } }, click: function( event ) { this._setSelection(); this._toggle( event ); }, keydown: function( event ) { var preventDefault = true; switch ( event.keyCode ) { case $.ui.keyCode.TAB: case $.ui.keyCode.ESCAPE: this.close( event ); preventDefault = false; break; case $.ui.keyCode.ENTER: if ( this.isOpen ) { this._selectFocusedItem( event ); } break; case $.ui.keyCode.UP: if ( event.altKey ) { this._toggle( event ); } else { this._move( "prev", event ); } break; case $.ui.keyCode.DOWN: if ( event.altKey ) { this._toggle( event ); } else { this._move( "next", event ); } break; case $.ui.keyCode.SPACE: if ( this.isOpen ) { this._selectFocusedItem( event ); } else { this._toggle( event ); } break; case $.ui.keyCode.LEFT: this._move( "prev", event ); break; case $.ui.keyCode.RIGHT: this._move( "next", event ); break; case $.ui.keyCode.HOME: case $.ui.keyCode.PAGE_UP: this._move( "first", event ); break; case $.ui.keyCode.END: case $.ui.keyCode.PAGE_DOWN: this._move( "last", event ); break; default: this.menu.trigger( event ); preventDefault = false; } if ( preventDefault ) { event.preventDefault(); } } }, _selectFocusedItem: function( event ) { var item = this.menuItems.eq( this.focusIndex ); if ( !item.hasClass( "ui-state-disabled" ) ) { this._select( item.data( "ui-selectmenu-item" ), event ); } }, _select: function( item, event ) { var oldIndex = this.element[ 0 ].selectedIndex; // Change native select element this.element[ 0 ].selectedIndex = item.index; this._setText( this.buttonText, item.label ); this._setAria( item ); this._trigger( "select", event, { item: item } ); if ( item.index !== oldIndex ) { this._trigger( "change", event, { item: item } ); } this.close( event ); }, _setAria: function( item ) { var id = this.menuItems.eq( item.index ).attr( "id" ); this.button.attr({ "aria-labelledby": id, "aria-activedescendant": id }); this.menu.attr( "aria-activedescendant", id ); }, _setOption: function( key, value ) { if ( key === "icons" ) { this.button.find( "span.ui-icon" ) .removeClass( this.options.icons.button ) .addClass( value.button ); } this._super( key, value ); if ( key === "appendTo" ) { this.menuWrap.appendTo( this._appendTo() ); } if ( key === "disabled" ) { this.menuInstance.option( "disabled", value ); this.button .toggleClass( "ui-state-disabled", value ) .attr( "aria-disabled", value ); this.element.prop( "disabled", value ); if ( value ) { this.button.attr( "tabindex", -1 ); this.close(); } else { this.button.attr( "tabindex", 0 ); } } if ( key === "width" ) { this._resizeButton(); } }, _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" ); } if ( !element.length ) { element = this.document[ 0 ].body; } return element; }, _toggleAttr: function() { this.button .toggleClass( "ui-corner-top", this.isOpen ) .toggleClass( "ui-corner-all", !this.isOpen ) .attr( "aria-expanded", this.isOpen ); this.menuWrap.toggleClass( "ui-selectmenu-open", this.isOpen ); this.menu.attr( "aria-hidden", !this.isOpen ); }, _resizeButton: function() { var width = this.options.width; if ( !width ) { width = this.element.show().outerWidth(); this.element.hide(); } this.button.outerWidth( width ); }, _resizeMenu: function() { this.menu.outerWidth( Math.max( this.button.outerWidth(), // support: IE10 // IE10 wraps long text (possibly a rounding bug) // so we add 1px to avoid the wrapping this.menu.width( "" ).outerWidth() + 1 ) ); }, _getCreateOptions: function() { return { disabled: this.element.prop( "disabled" ) }; }, _parseOptions: function( options ) { var data = []; options.each(function( index, item ) { var option = $( item ), optgroup = option.parent( "optgroup" ); data.push({ element: option, index: index, value: option.attr( "value" ), label: option.text(), optgroup: optgroup.attr( "label" ) || "", disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" ) }); }); this.items = data; }, _destroy: function() { this.menuWrap.remove(); this.button.remove(); this.element.show(); this.element.removeUniqueId(); this.label.attr( "for", this.ids.element ); } }); }));