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