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