[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 /*! 2 * jQuery UI Selectmenu 1.13.1 3 * http://jqueryui.com 4 * 5 * Copyright jQuery Foundation and other contributors 6 * Released under the MIT license. 7 * http://jquery.org/license 8 */ 9 10 //>>label: Selectmenu 11 //>>group: Widgets 12 /* eslint-disable max-len */ 13 //>>description: Duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select. 14 /* eslint-enable max-len */ 15 //>>docs: http://api.jqueryui.com/selectmenu/ 16 //>>demos: http://jqueryui.com/selectmenu/ 17 //>>css.structure: ../../themes/base/core.css 18 //>>css.structure: ../../themes/base/selectmenu.css, ../../themes/base/button.css 19 //>>css.theme: ../../themes/base/theme.css 20 21 ( function( factory ) { 22 "use strict"; 23 24 if ( typeof define === "function" && define.amd ) { 25 26 // AMD. Register as an anonymous module. 27 define( [ 28 "jquery", 29 "./menu", 30 "./core" 31 ], factory ); 32 } else { 33 34 // Browser globals 35 factory( jQuery ); 36 } 37 } )( function( $ ) { 38 "use strict"; 39 40 return $.widget( "ui.selectmenu", [ $.ui.formResetMixin, { 41 version: "1.13.1", 42 defaultElement: "<select>", 43 options: { 44 appendTo: null, 45 classes: { 46 "ui-selectmenu-button-open": "ui-corner-top", 47 "ui-selectmenu-button-closed": "ui-corner-all" 48 }, 49 disabled: null, 50 icons: { 51 button: "ui-icon-triangle-1-s" 52 }, 53 position: { 54 my: "left top", 55 at: "left bottom", 56 collision: "none" 57 }, 58 width: false, 59 60 // Callbacks 61 change: null, 62 close: null, 63 focus: null, 64 open: null, 65 select: null 66 }, 67 68 _create: function() { 69 var selectmenuId = this.element.uniqueId().attr( "id" ); 70 this.ids = { 71 element: selectmenuId, 72 button: selectmenuId + "-button", 73 menu: selectmenuId + "-menu" 74 }; 75 76 this._drawButton(); 77 this._drawMenu(); 78 this._bindFormResetHandler(); 79 80 this._rendered = false; 81 this.menuItems = $(); 82 }, 83 84 _drawButton: function() { 85 var icon, 86 that = this, 87 item = this._parseOption( 88 this.element.find( "option:selected" ), 89 this.element[ 0 ].selectedIndex 90 ); 91 92 // Associate existing label with the new button 93 this.labels = this.element.labels().attr( "for", this.ids.button ); 94 this._on( this.labels, { 95 click: function( event ) { 96 this.button.trigger( "focus" ); 97 event.preventDefault(); 98 } 99 } ); 100 101 // Hide original select element 102 this.element.hide(); 103 104 // Create button 105 this.button = $( "<span>", { 106 tabindex: this.options.disabled ? -1 : 0, 107 id: this.ids.button, 108 role: "combobox", 109 "aria-expanded": "false", 110 "aria-autocomplete": "list", 111 "aria-owns": this.ids.menu, 112 "aria-haspopup": "true", 113 title: this.element.attr( "title" ) 114 } ) 115 .insertAfter( this.element ); 116 117 this._addClass( this.button, "ui-selectmenu-button ui-selectmenu-button-closed", 118 "ui-button ui-widget" ); 119 120 icon = $( "<span>" ).appendTo( this.button ); 121 this._addClass( icon, "ui-selectmenu-icon", "ui-icon " + this.options.icons.button ); 122 this.buttonItem = this._renderButtonItem( item ) 123 .appendTo( this.button ); 124 125 if ( this.options.width !== false ) { 126 this._resizeButton(); 127 } 128 129 this._on( this.button, this._buttonEvents ); 130 this.button.one( "focusin", function() { 131 132 // Delay rendering the menu items until the button receives focus. 133 // The menu may have already been rendered via a programmatic open. 134 if ( !that._rendered ) { 135 that._refreshMenu(); 136 } 137 } ); 138 }, 139 140 _drawMenu: function() { 141 var that = this; 142 143 // Create menu 144 this.menu = $( "<ul>", { 145 "aria-hidden": "true", 146 "aria-labelledby": this.ids.button, 147 id: this.ids.menu 148 } ); 149 150 // Wrap menu 151 this.menuWrap = $( "<div>" ).append( this.menu ); 152 this._addClass( this.menuWrap, "ui-selectmenu-menu", "ui-front" ); 153 this.menuWrap.appendTo( this._appendTo() ); 154 155 // Initialize menu widget 156 this.menuInstance = this.menu 157 .menu( { 158 classes: { 159 "ui-menu": "ui-corner-bottom" 160 }, 161 role: "listbox", 162 select: function( event, ui ) { 163 event.preventDefault(); 164 165 // Support: IE8 166 // If the item was selected via a click, the text selection 167 // will be destroyed in IE 168 that._setSelection(); 169 170 that._select( ui.item.data( "ui-selectmenu-item" ), event ); 171 }, 172 focus: function( event, ui ) { 173 var item = ui.item.data( "ui-selectmenu-item" ); 174 175 // Prevent inital focus from firing and check if its a newly focused item 176 if ( that.focusIndex != null && item.index !== that.focusIndex ) { 177 that._trigger( "focus", event, { item: item } ); 178 if ( !that.isOpen ) { 179 that._select( item, event ); 180 } 181 } 182 that.focusIndex = item.index; 183 184 that.button.attr( "aria-activedescendant", 185 that.menuItems.eq( item.index ).attr( "id" ) ); 186 } 187 } ) 188 .menu( "instance" ); 189 190 // Don't close the menu on mouseleave 191 this.menuInstance._off( this.menu, "mouseleave" ); 192 193 // Cancel the menu's collapseAll on document click 194 this.menuInstance._closeOnDocumentClick = function() { 195 return false; 196 }; 197 198 // Selects often contain empty items, but never contain dividers 199 this.menuInstance._isDivider = function() { 200 return false; 201 }; 202 }, 203 204 refresh: function() { 205 this._refreshMenu(); 206 this.buttonItem.replaceWith( 207 this.buttonItem = this._renderButtonItem( 208 209 // Fall back to an empty object in case there are no options 210 this._getSelectedItem().data( "ui-selectmenu-item" ) || {} 211 ) 212 ); 213 if ( this.options.width === null ) { 214 this._resizeButton(); 215 } 216 }, 217 218 _refreshMenu: function() { 219 var item, 220 options = this.element.find( "option" ); 221 222 this.menu.empty(); 223 224 this._parseOptions( options ); 225 this._renderMenu( this.menu, this.items ); 226 227 this.menuInstance.refresh(); 228 this.menuItems = this.menu.find( "li" ) 229 .not( ".ui-selectmenu-optgroup" ) 230 .find( ".ui-menu-item-wrapper" ); 231 232 this._rendered = true; 233 234 if ( !options.length ) { 235 return; 236 } 237 238 item = this._getSelectedItem(); 239 240 // Update the menu to have the correct item focused 241 this.menuInstance.focus( null, item ); 242 this._setAria( item.data( "ui-selectmenu-item" ) ); 243 244 // Set disabled state 245 this._setOption( "disabled", this.element.prop( "disabled" ) ); 246 }, 247 248 open: function( event ) { 249 if ( this.options.disabled ) { 250 return; 251 } 252 253 // If this is the first time the menu is being opened, render the items 254 if ( !this._rendered ) { 255 this._refreshMenu(); 256 } else { 257 258 // Menu clears focus on close, reset focus to selected item 259 this._removeClass( this.menu.find( ".ui-state-active" ), null, "ui-state-active" ); 260 this.menuInstance.focus( null, this._getSelectedItem() ); 261 } 262 263 // If there are no options, don't open the menu 264 if ( !this.menuItems.length ) { 265 return; 266 } 267 268 this.isOpen = true; 269 this._toggleAttr(); 270 this._resizeMenu(); 271 this._position(); 272 273 this._on( this.document, this._documentClick ); 274 275 this._trigger( "open", event ); 276 }, 277 278 _position: function() { 279 this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) ); 280 }, 281 282 close: function( event ) { 283 if ( !this.isOpen ) { 284 return; 285 } 286 287 this.isOpen = false; 288 this._toggleAttr(); 289 290 this.range = null; 291 this._off( this.document ); 292 293 this._trigger( "close", event ); 294 }, 295 296 widget: function() { 297 return this.button; 298 }, 299 300 menuWidget: function() { 301 return this.menu; 302 }, 303 304 _renderButtonItem: function( item ) { 305 var buttonItem = $( "<span>" ); 306 307 this._setText( buttonItem, item.label ); 308 this._addClass( buttonItem, "ui-selectmenu-text" ); 309 310 return buttonItem; 311 }, 312 313 _renderMenu: function( ul, items ) { 314 var that = this, 315 currentOptgroup = ""; 316 317 $.each( items, function( index, item ) { 318 var li; 319 320 if ( item.optgroup !== currentOptgroup ) { 321 li = $( "<li>", { 322 text: item.optgroup 323 } ); 324 that._addClass( li, "ui-selectmenu-optgroup", "ui-menu-divider" + 325 ( item.element.parent( "optgroup" ).prop( "disabled" ) ? 326 " ui-state-disabled" : 327 "" ) ); 328 329 li.appendTo( ul ); 330 331 currentOptgroup = item.optgroup; 332 } 333 334 that._renderItemData( ul, item ); 335 } ); 336 }, 337 338 _renderItemData: function( ul, item ) { 339 return this._renderItem( ul, item ).data( "ui-selectmenu-item", item ); 340 }, 341 342 _renderItem: function( ul, item ) { 343 var li = $( "<li>" ), 344 wrapper = $( "<div>", { 345 title: item.element.attr( "title" ) 346 } ); 347 348 if ( item.disabled ) { 349 this._addClass( li, null, "ui-state-disabled" ); 350 } 351 this._setText( wrapper, item.label ); 352 353 return li.append( wrapper ).appendTo( ul ); 354 }, 355 356 _setText: function( element, value ) { 357 if ( value ) { 358 element.text( value ); 359 } else { 360 element.html( " " ); 361 } 362 }, 363 364 _move: function( direction, event ) { 365 var item, next, 366 filter = ".ui-menu-item"; 367 368 if ( this.isOpen ) { 369 item = this.menuItems.eq( this.focusIndex ).parent( "li" ); 370 } else { 371 item = this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" ); 372 filter += ":not(.ui-state-disabled)"; 373 } 374 375 if ( direction === "first" || direction === "last" ) { 376 next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 ); 377 } else { 378 next = item[ direction + "All" ]( filter ).eq( 0 ); 379 } 380 381 if ( next.length ) { 382 this.menuInstance.focus( event, next ); 383 } 384 }, 385 386 _getSelectedItem: function() { 387 return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" ); 388 }, 389 390 _toggle: function( event ) { 391 this[ this.isOpen ? "close" : "open" ]( event ); 392 }, 393 394 _setSelection: function() { 395 var selection; 396 397 if ( !this.range ) { 398 return; 399 } 400 401 if ( window.getSelection ) { 402 selection = window.getSelection(); 403 selection.removeAllRanges(); 404 selection.addRange( this.range ); 405 406 // Support: IE8 407 } else { 408 this.range.select(); 409 } 410 411 // Support: IE 412 // Setting the text selection kills the button focus in IE, but 413 // restoring the focus doesn't kill the selection. 414 this.button.focus(); 415 }, 416 417 _documentClick: { 418 mousedown: function( event ) { 419 if ( !this.isOpen ) { 420 return; 421 } 422 423 if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" + 424 $.escapeSelector( this.ids.button ) ).length ) { 425 this.close( event ); 426 } 427 } 428 }, 429 430 _buttonEvents: { 431 432 // Prevent text selection from being reset when interacting with the selectmenu (#10144) 433 mousedown: function() { 434 var selection; 435 436 if ( window.getSelection ) { 437 selection = window.getSelection(); 438 if ( selection.rangeCount ) { 439 this.range = selection.getRangeAt( 0 ); 440 } 441 442 // Support: IE8 443 } else { 444 this.range = document.selection.createRange(); 445 } 446 }, 447 448 click: function( event ) { 449 this._setSelection(); 450 this._toggle( event ); 451 }, 452 453 keydown: function( event ) { 454 var preventDefault = true; 455 switch ( event.keyCode ) { 456 case $.ui.keyCode.TAB: 457 case $.ui.keyCode.ESCAPE: 458 this.close( event ); 459 preventDefault = false; 460 break; 461 case $.ui.keyCode.ENTER: 462 if ( this.isOpen ) { 463 this._selectFocusedItem( event ); 464 } 465 break; 466 case $.ui.keyCode.UP: 467 if ( event.altKey ) { 468 this._toggle( event ); 469 } else { 470 this._move( "prev", event ); 471 } 472 break; 473 case $.ui.keyCode.DOWN: 474 if ( event.altKey ) { 475 this._toggle( event ); 476 } else { 477 this._move( "next", event ); 478 } 479 break; 480 case $.ui.keyCode.SPACE: 481 if ( this.isOpen ) { 482 this._selectFocusedItem( event ); 483 } else { 484 this._toggle( event ); 485 } 486 break; 487 case $.ui.keyCode.LEFT: 488 this._move( "prev", event ); 489 break; 490 case $.ui.keyCode.RIGHT: 491 this._move( "next", event ); 492 break; 493 case $.ui.keyCode.HOME: 494 case $.ui.keyCode.PAGE_UP: 495 this._move( "first", event ); 496 break; 497 case $.ui.keyCode.END: 498 case $.ui.keyCode.PAGE_DOWN: 499 this._move( "last", event ); 500 break; 501 default: 502 this.menu.trigger( event ); 503 preventDefault = false; 504 } 505 506 if ( preventDefault ) { 507 event.preventDefault(); 508 } 509 } 510 }, 511 512 _selectFocusedItem: function( event ) { 513 var item = this.menuItems.eq( this.focusIndex ).parent( "li" ); 514 if ( !item.hasClass( "ui-state-disabled" ) ) { 515 this._select( item.data( "ui-selectmenu-item" ), event ); 516 } 517 }, 518 519 _select: function( item, event ) { 520 var oldIndex = this.element[ 0 ].selectedIndex; 521 522 // Change native select element 523 this.element[ 0 ].selectedIndex = item.index; 524 this.buttonItem.replaceWith( this.buttonItem = this._renderButtonItem( item ) ); 525 this._setAria( item ); 526 this._trigger( "select", event, { item: item } ); 527 528 if ( item.index !== oldIndex ) { 529 this._trigger( "change", event, { item: item } ); 530 } 531 532 this.close( event ); 533 }, 534 535 _setAria: function( item ) { 536 var id = this.menuItems.eq( item.index ).attr( "id" ); 537 538 this.button.attr( { 539 "aria-labelledby": id, 540 "aria-activedescendant": id 541 } ); 542 this.menu.attr( "aria-activedescendant", id ); 543 }, 544 545 _setOption: function( key, value ) { 546 if ( key === "icons" ) { 547 var icon = this.button.find( "span.ui-icon" ); 548 this._removeClass( icon, null, this.options.icons.button ) 549 ._addClass( icon, null, value.button ); 550 } 551 552 this._super( key, value ); 553 554 if ( key === "appendTo" ) { 555 this.menuWrap.appendTo( this._appendTo() ); 556 } 557 558 if ( key === "width" ) { 559 this._resizeButton(); 560 } 561 }, 562 563 _setOptionDisabled: function( value ) { 564 this._super( value ); 565 566 this.menuInstance.option( "disabled", value ); 567 this.button.attr( "aria-disabled", value ); 568 this._toggleClass( this.button, null, "ui-state-disabled", value ); 569 570 this.element.prop( "disabled", value ); 571 if ( value ) { 572 this.button.attr( "tabindex", -1 ); 573 this.close(); 574 } else { 575 this.button.attr( "tabindex", 0 ); 576 } 577 }, 578 579 _appendTo: function() { 580 var element = this.options.appendTo; 581 582 if ( element ) { 583 element = element.jquery || element.nodeType ? 584 $( element ) : 585 this.document.find( element ).eq( 0 ); 586 } 587 588 if ( !element || !element[ 0 ] ) { 589 element = this.element.closest( ".ui-front, dialog" ); 590 } 591 592 if ( !element.length ) { 593 element = this.document[ 0 ].body; 594 } 595 596 return element; 597 }, 598 599 _toggleAttr: function() { 600 this.button.attr( "aria-expanded", this.isOpen ); 601 602 // We can't use two _toggleClass() calls here, because we need to make sure 603 // we always remove classes first and add them second, otherwise if both classes have the 604 // same theme class, it will be removed after we add it. 605 this._removeClass( this.button, "ui-selectmenu-button-" + 606 ( this.isOpen ? "closed" : "open" ) ) 607 ._addClass( this.button, "ui-selectmenu-button-" + 608 ( this.isOpen ? "open" : "closed" ) ) 609 ._toggleClass( this.menuWrap, "ui-selectmenu-open", null, this.isOpen ); 610 611 this.menu.attr( "aria-hidden", !this.isOpen ); 612 }, 613 614 _resizeButton: function() { 615 var width = this.options.width; 616 617 // For `width: false`, just remove inline style and stop 618 if ( width === false ) { 619 this.button.css( "width", "" ); 620 return; 621 } 622 623 // For `width: null`, match the width of the original element 624 if ( width === null ) { 625 width = this.element.show().outerWidth(); 626 this.element.hide(); 627 } 628 629 this.button.outerWidth( width ); 630 }, 631 632 _resizeMenu: function() { 633 this.menu.outerWidth( Math.max( 634 this.button.outerWidth(), 635 636 // Support: IE10 637 // IE10 wraps long text (possibly a rounding bug) 638 // so we add 1px to avoid the wrapping 639 this.menu.width( "" ).outerWidth() + 1 640 ) ); 641 }, 642 643 _getCreateOptions: function() { 644 var options = this._super(); 645 646 options.disabled = this.element.prop( "disabled" ); 647 648 return options; 649 }, 650 651 _parseOptions: function( options ) { 652 var that = this, 653 data = []; 654 options.each( function( index, item ) { 655 if ( item.hidden ) { 656 return; 657 } 658 659 data.push( that._parseOption( $( item ), index ) ); 660 } ); 661 this.items = data; 662 }, 663 664 _parseOption: function( option, index ) { 665 var optgroup = option.parent( "optgroup" ); 666 667 return { 668 element: option, 669 index: index, 670 value: option.val(), 671 label: option.text(), 672 optgroup: optgroup.attr( "label" ) || "", 673 disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" ) 674 }; 675 }, 676 677 _destroy: function() { 678 this._unbindFormResetHandler(); 679 this.menuWrap.remove(); 680 this.button.remove(); 681 this.element.show(); 682 this.element.removeUniqueId(); 683 this.labels.attr( "for", this.ids.element ); 684 } 685 } ] ); 686 687 } );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |