Ticket #28709: 28709.wip.4.diff
File 28709.wip.4.diff, 30.5 KB (added by , 10 years ago) |
---|
-
src/wp-admin/customize.php
diff --git src/wp-admin/customize.php src/wp-admin/customize.php index 7828ee4..109bc07 100644
do_action( 'customize_controls_init' ); 53 53 wp_enqueue_script( 'customize-controls' ); 54 54 wp_enqueue_style( 'customize-controls' ); 55 55 56 wp_enqueue_script( 'accordion' );57 58 56 /** 59 57 * Enqueue Customizer control scripts. 60 58 * … … do_action( 'customize_controls_print_scripts' ); 160 158 <?php endif; ?> 161 159 </div> 162 160 163 <div id="customize-theme-controls"><ul> 164 <?php 165 foreach ( $wp_customize->containers() as $container ) { 166 $container->maybe_render(); 167 } 168 ?> 169 </ul></div> 161 <div id="customize-theme-controls"> 162 <ul><?php // Panels and sections are managed here via JavaScript ?></ul> 163 </div> 170 164 </div> 171 165 </div> 172 166 … … do_action( 'customize_controls_print_scripts' ); 249 243 ), 250 244 'settings' => array(), 251 245 'controls' => array(), 246 'panels' => array(), 247 'sections' => array(), 252 248 'nonce' => array( 253 249 'save' => wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() ), 254 250 'preview' => wp_create_nonce( 'preview-customize_' . $wp_customize->get_stylesheet() ) … … do_action( 'customize_controls_print_scripts' ); 263 259 ); 264 260 } 265 261 266 // Prepare Customize Control objects to pass to Java script.262 // Prepare Customize Control objects to pass to JavaScript. 267 263 foreach ( $wp_customize->controls() as $id => $control ) { 268 $control->to_json(); 269 $settings['controls'][ $id ] = $control->json; 264 $settings['controls'][ $id ] = $control->json(); 265 } 266 267 // Prepare Customize Section objects to pass to JavaScript. 268 foreach ( $wp_customize->sections() as $id => $section ) { 269 $settings['sections'][ $id ] = $section->json(); 270 } 271 272 // Prepare Customize Panel objects to pass to JavaScript. 273 foreach ( $wp_customize->panels() as $id => $panel ) { 274 $settings['panels'][ $id ] = $panel->json(); 275 foreach ( $panel->sections as $section_id => $section ) { 276 $settings['sections'][ $section_id ] = $section->json(); 277 } 270 278 } 271 279 272 280 ?> -
src/wp-admin/js/accordion.js
diff --git src/wp-admin/js/accordion.js src/wp-admin/js/accordion.js index 6cb1c1c..63e14e8 100644
58 58 }); 59 59 }); 60 60 61 var sectionContent = $( '.accordion-section-content' );62 63 61 /** 64 62 * Close the current accordion section and open a new one. 65 63 * … … 69 67 function accordionSwitch ( el ) { 70 68 var section = el.closest( '.accordion-section' ), 71 69 siblings = section.closest( '.accordion-container' ).find( '.open' ), 72 content = section.find( sectionContent);70 content = section.find( '.accordion-section-content' ); 73 71 74 72 // This section has no content and cannot be expanded. 75 73 if ( section.hasClass( 'cannot-expand' ) ) { … … 87 85 content.toggle( true ).slideToggle( 150 ); 88 86 } else { 89 87 siblings.removeClass( 'open' ); 90 siblings.find( sectionContent).show().slideUp( 150 );88 siblings.find( '.accordion-section-content' ).show().slideUp( 150 ); 91 89 content.toggle( false ).slideToggle( 150 ); 92 90 section.toggleClass( 'open' ); 93 91 } … … 125 123 } else { 126 124 // Close all open sections in any accordion level. 127 125 siblings.removeClass( 'open' ); 128 siblings.find( sectionContent).show().slideUp( 0 );126 siblings.find( '.accordion-section-content' ).show().slideUp( 0 ); 129 127 content.show( 0, function() { 130 128 position = content.offset().top; 131 129 scroll = container.scrollTop(); -
src/wp-admin/js/customize-controls.js
diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js index fad223e..fbfe90e 100644
1 1 /* globals _wpCustomizeHeader, _wpMediaViewsL10n */ 2 2 (function( exports, $ ){ 3 var api = wp.customize;3 var bubbleChildValueChanges, Container, api = wp.customize; 4 4 5 5 /** 6 6 * @constructor … … 31 31 }); 32 32 33 33 /** 34 * Watch all changes to Value properties, and bubble changes to parent Values instance 35 * 36 * @param {wp.customize.Class} instance 37 * @param {Array} properties The names of the Value instances to watch. 38 */ 39 bubbleChildValueChanges = function ( instance, properties ) { 40 $.each( properties, function ( i, key ) { 41 instance[ key ].bind( function ( to, from ) { 42 if ( instance.parent && to !== from ) { 43 instance.parent.trigger( 'change', instance ); 44 } 45 } ); 46 } ); 47 }; 48 49 /** 50 * Base class for Panel and Section 51 * 52 * @constructor 53 * @augments wp.customize.Class 54 */ 55 Container = api.Class.extend({ 56 slideSpeed: 150, 57 58 initialize: function ( id, options ) { 59 var self = this; 60 self.id = id; 61 self.params = {}; 62 $.extend( self, options || {} ); 63 self.container = $( self.params.content ); 64 65 self.priority = new api.Value(); 66 self.active = new api.Value(); 67 self.expanded = new api.Value(); 68 69 self.active.bind( function ( active ) { 70 if ( active && self.isContextuallyActive() ) { 71 self.onActivate(); 72 // @todo Trigger 'activated' event? 73 } else { 74 self.onDeactivate(); 75 // @todo Trigger 'deactivated' event? 76 } 77 }); 78 self.expanded.bind( function ( expanded ) { 79 if ( expanded ) { 80 self.onExpand(); 81 // @todo Trigger 'expanded' event? 82 } else { 83 self.onCollapse(); 84 // @todo Trigger 'collapsed' event? 85 } 86 }); 87 88 self.attachEvents(); 89 90 bubbleChildValueChanges( self, [ 'priority', 'active' ] ); 91 92 self.priority.set( self.params.priority || 100 ); 93 self.active.set( true ); // @todo pass from params, eventually from active_callback when defining panel/section 94 self.expanded.set( false ); // @todo True if deeplinking? 95 }, 96 97 /** 98 * Get the child models associated with this parent, sorting them by their priority Value. 99 * 100 * @param {String} parentType 101 * @param {String} childType 102 * @returns {Array} 103 */ 104 _children: function ( parentType, childType ) { 105 var parent = this, 106 children = []; 107 api[ childType ].each( function ( child ) { 108 if ( child[ parentType ].get() === parent.id ) { 109 children.push( child ); 110 } 111 } ); 112 children.sort( function ( a, b ) { 113 return a.priority() - b.priority(); 114 } ); 115 return children; 116 }, 117 118 /** 119 * Change to the section's active state. 120 * 121 * @param {Boolean} active 122 */ 123 toggle: function ( active ) { 124 // @todo 125 if ( active ) { 126 this.deactivate(); 127 } else { 128 this.activate(); 129 } 130 }, 131 132 isContextuallyActive: function () { 133 throw new Error( 'Must override with subclass.' ); 134 }, 135 136 /** 137 * 138 */ 139 activate: function () { 140 this.active.set( true ); 141 // @todo If it was already active, then re-trigger the events 142 }, 143 144 /** 145 * 146 */ 147 onActivate: function () { 148 // @todo Prevent this from proceeding if there are no active controls; as soon as a control becomes active, then this would automatically show 149 this.container.stop( true, true ).slideDown(); // @todo Trigger 'activated' event when complete? 150 }, 151 152 /** 153 * 154 */ 155 deactivate: function () { 156 this.active.set( false ); 157 // @todo If it was already inactive, then re-trigger the events 158 }, 159 160 /** 161 * 162 */ 163 onDeactivate: function () { 164 this.onCollapse(); 165 this.container.stop( true, true ).slideUp(); // @todo Trigger 'deactivated' event when complete? 166 }, 167 168 /** 169 * 170 */ 171 expand: function () { 172 this.expanded.set( true ); 173 }, 174 175 /** 176 * Show the accordion section contents and collapse all other sections at the same time 177 */ 178 onExpand: function () { 179 throw new Error( 'onCollapse method must be overridden' ); 180 }, 181 182 /** 183 * 184 */ 185 collapse: function () { 186 this.expanded( false ); 187 }, 188 189 /** 190 * 191 */ 192 onCollapse: function () { 193 throw new Error( 'onCollapse method must be overridden' ); 194 }, 195 196 /** 197 * 198 */ 199 focus: function () { 200 throw new Error( 'focus method must be overridden' ); 201 } 202 }); 203 204 /** 205 * @constructor 206 * @augments wp.customize.Class 207 */ 208 api.Section = Container.extend({ 209 210 /** 211 * @param {String} id 212 * @param {Array} options 213 */ 214 initialize: function ( id, options ) { 215 var section = this; 216 Container.prototype.initialize.call( section, id, options ); 217 218 section.panel = new api.Value(); 219 section.panel.bind( function ( id ) { 220 $( section.container ).toggleClass( 'control-subsection', !! id ); 221 }); 222 section.panel.set( section.params.panel || '' ); 223 bubbleChildValueChanges( section, [ 'panel' ] ); 224 }, 225 226 /** 227 * 228 */ 229 embed: function ( readyCallback ) { 230 var panel_id, 231 section = this; 232 233 panel_id = this.panel.get(); 234 if ( ! panel_id ) { 235 $( '#customize-theme-controls > ul' ).append( section.container ); 236 readyCallback(); 237 } else { 238 api.panel( panel_id, function ( panel ) { 239 panel.embed(); 240 panel.container.find( 'ul:first' ).append( section.container ); 241 readyCallback(); 242 } ); 243 } 244 }, 245 246 /** 247 * Add behaviors for the accordion section 248 */ 249 attachEvents: function () { 250 var section = this; 251 252 // Expand/Collapse accordion sections on click. 253 section.container.find( '.accordion-section-title' ).on( 'click keydown', function( e ) { 254 if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key 255 return; 256 } 257 e.preventDefault(); // Keep this AFTER the key filter above 258 259 if ( section.expanded() ) { 260 section.collapse(); 261 } else { 262 section.expand(); 263 } 264 }); 265 }, 266 267 /** 268 * Return whether this section has any active controls. 269 * 270 * @returns {boolean} 271 */ 272 isContextuallyActive: function () { 273 var section = this, 274 controls = section.controls(), 275 activeCount = 0; 276 _( controls ).each( function ( control ) { 277 if ( control.active() ) { 278 activeCount += 1; 279 } 280 } ); 281 return ( activeCount !== 0 ); 282 }, 283 284 /** 285 * Get the controls that are associated with this section, sorted by their priority Value. 286 * 287 * @returns {Array} 288 */ 289 controls: function () { 290 return this._children( 'section', 'control' ); 291 }, 292 293 /** 294 * Show the accordion section contents and collapse all other sections at the same time 295 */ 296 onExpand: function () { 297 var section = this, 298 content = section.container.find( '.accordion-section-content' ); 299 300 if ( section.panel() ) { 301 api.panel( section.panel() ).expand(); 302 } 303 304 api.section.each( function ( otherSection ) { 305 if ( otherSection !== section ) { 306 otherSection.collapse(); 307 } 308 }); 309 310 content.stop().slideDown( section.slideSpeed ); 311 section.container.addClass( 'open' ); 312 }, 313 314 /** 315 * 316 */ 317 onCollapse: function () { 318 var section = this, 319 content = section.container.find( '.accordion-section-content' ); 320 321 section.container.removeClass( 'open' ); 322 content.slideUp( section.slideSpeed ); 323 }, 324 325 /** 326 * Bring the containing panel into view and then expand this section and bring it into view 327 * 328 * @todo This is an alias for expand(); do we need it? 329 */ 330 focus: function () { 331 var section = this; 332 // @todo What if it is not active? Return false? 333 section.expand(); 334 } 335 }); 336 337 /** 338 * @constructor 339 * @augments wp.customize.Class 340 */ 341 api.Panel = Container.extend({ 342 initialize: function ( id, options ) { 343 var panel = this; 344 Container.prototype.initialize.call( panel, id, options ); 345 }, 346 347 /** 348 * 349 */ 350 embed: function ( readyCallback ) { 351 $( '#customize-theme-controls > ul' ).append( this.container ); 352 if ( readyCallback ) { 353 readyCallback(); 354 } 355 }, 356 357 /** 358 * 359 */ 360 attachEvents: function () { 361 var meta, panel = this; 362 363 // Expand/Collapse accordion sections on click. 364 panel.container.find( '.accordion-section-title' ).on( 'click keydown', function( e ) { 365 if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key 366 return; 367 } 368 e.preventDefault(); // Keep this AFTER the key filter above 369 370 if ( ! panel.expanded() ) { 371 panel.expand(); 372 } 373 }); 374 375 meta = panel.container.find( '.panel-meta:first' ); 376 377 meta.find( '> .accordion-section-title' ).on( 'click keydown', function( e ) { 378 if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key 379 return; 380 } 381 e.preventDefault(); // Keep this AFTER the key filter above 382 383 if ( meta.hasClass( 'cannot-expand' ) ) { 384 return; 385 } 386 387 var content = meta.find( '.accordion-section-content:first' ); 388 if ( meta.hasClass( 'open' ) ) { 389 meta.toggleClass( 'open' ); 390 content.slideUp( 150 ); 391 } else { 392 content.slideDown( 150 ); 393 meta.toggleClass( 'open' ); 394 } 395 }); 396 397 }, 398 399 /** 400 * Get the sections that are associated with this panel, sorted by their priority Value. 401 * 402 * @returns {Array} 403 */ 404 sections: function () { 405 return this._children( 'panel', 'section' ); 406 }, 407 408 /** 409 * Return whether this section has any active sections. 410 * 411 * @returns {boolean} 412 */ 413 isContextuallyActive: function () { 414 var panel = this, 415 sections = panel.sections(), 416 activeCount = 0; 417 _( sections ).each( function ( section ) { 418 if ( section.active() && section.isContextuallyActive() ) { 419 activeCount += 1; 420 } 421 } ); 422 return ( activeCount !== 0 ); 423 }, 424 425 /** 426 * 427 */ 428 onExpand: function () { 429 430 var position, scroll, 431 panel = this, 432 section = panel.container.closest( '.accordion-section' ), 433 overlay = section.closest( '.wp-full-overlay' ), 434 container = section.closest( '.accordion-container' ), 435 topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ), 436 backBtn = overlay.find( '.control-panel-back' ), 437 content = section.find( '.control-panel-content' ); 438 439 // Collapse any sibling sections/panels 440 api.section.each( function ( section ) { 441 if ( ! section.panel() ) { 442 section.collapse(); // @todo If any sections are open, then the position calculation below will fire too early 443 } 444 }); 445 api.panel.each( function ( otherPanel ) { 446 if ( panel !== otherPanel ) { 447 otherPanel.collapse(); // @todo the position calculation below probably will fire too early 448 } 449 }); 450 451 content.show( 0, function() { 452 position = content.offset().top; 453 scroll = container.scrollTop(); 454 content.css( 'margin-top', ( 45 - position - scroll ) ); 455 section.addClass( 'current-panel' ); 456 overlay.addClass( 'in-sub-panel' ); 457 container.scrollTop( 0 ); 458 } ); 459 topPanel.attr( 'tabindex', '-1' ); 460 backBtn.attr( 'tabindex', '0' ); 461 backBtn.focus(); 462 }, 463 464 /** 465 * 466 */ 467 onCollapse: function () { 468 469 var panel = this, 470 section = panel.container.closest( '.accordion-section' ), 471 overlay = section.closest( '.wp-full-overlay' ), 472 container = section.closest( '.accordion-container' ), 473 siblings = container.find( '.open' ), 474 topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ), 475 backBtn = overlay.find( '.control-panel-back' ), 476 panelTitle = section.find( '.accordion-section-title' ).first(), 477 content = section.find( '.control-panel-content' ); 478 479 siblings.removeClass( 'open' ); 480 section.removeClass( 'current-panel' ); 481 overlay.removeClass( 'in-sub-panel' ); 482 content.delay( 180 ).hide( 0, function() { 483 content.css( 'margin-top', 'inherit' ); // Reset 484 } ); 485 topPanel.attr( 'tabindex', '0' ); 486 backBtn.attr( 'tabindex', '-1' ); 487 panelTitle.focus(); 488 container.scrollTop( 0 ); 489 }, 490 491 /** 492 * Bring the containing panel into view and then expand this section and bring it into view 493 */ 494 focus: function () { 495 var panel = this; 496 // @todo What if it is not active? Return false? 497 panel.expand(); 498 } 499 500 // @todo Need to first exit out of the Panel 501 }); 502 503 /** 34 504 * @constructor 35 505 * @augments wp.customize.Class 36 506 */ … … 44 514 45 515 this.id = id; 46 516 this.selector = '#customize-control-' + id.replace( /\]/g, '' ).replace( /\[/g, '-' ); 47 this.container = $( this.selector ); 517 this.container = this.params.content ? $( this.params.content ) : $( this.selector ); 518 519 this.section = new api.Value( this.params.section ); 520 this.priority = new api.Value( this.params.priority || 10 ); 48 521 this.active = new api.Value( this.params.active ); 49 522 50 523 settings = $.map( this.params.settings, function( value ) { … … 60 533 } 61 534 62 535 control.setting = control.settings['default'] || null; 63 control.ready(); 536 control.embed( function () { 537 control.ready(); 538 } ); 64 539 }) ); 65 540 66 541 control.elements = []; … … 74 549 75 550 if ( node.is(':radio') ) { 76 551 name = node.prop('name'); 77 if ( radios[ name ] ) 552 if ( radios[ name ] ) { 78 553 return; 554 } 79 555 80 556 radios[ name ] = true; 81 557 node = nodes.filter( '[name="' + name + '"]' ); … … 93 569 control.toggle( active ); 94 570 } ); 95 571 control.toggle( control.active() ); 572 573 bubbleChildValueChanges( control, [ 'section', 'priority', 'active' ] ); 574 }, 575 576 /** 577 * @param {Function} [readyCallback] Callback to fire when the embedding is done. 578 */ 579 embed: function ( readyCallback ) { 580 var section_id, 581 control = this; 582 583 section_id = control.section.get(); 584 if ( ! section_id ) { 585 throw new Error( 'A control must have an associated section.' ); 586 } 587 588 // Defer until the associated section is available 589 api.section( section_id, function ( section ) { 590 section.embed( function () { 591 section.container.find( 'ul:first' ).append( control.container ); 592 readyCallback(); 593 } ); 594 } ); 96 595 }, 97 596 98 597 /** … … 101 600 ready: function() {}, 102 601 103 602 /** 104 * Callback for change to the control's active state. 105 * 106 * Override function for custom behavior for the control being active/inactive. 603 * Bring the containing section and panel into view and then this control into view, focusing on the first input 604 */ 605 focus: function () { 606 throw new Error( 'Not implemented yet' ); 607 }, 608 609 /** 610 * Change to the control's active state. 107 611 * 108 612 * @param {Boolean} active 109 613 */ … … 575 1079 576 1080 // Create the collection of Control objects. 577 1081 api.control = new api.Values({ defaultConstructor: api.Control }); 1082 api.section = new api.Values({ defaultConstructor: api.Section }); 1083 api.panel = new api.Values({ defaultConstructor: api.Panel }); 578 1084 579 1085 /** 580 1086 * @constructor … … 979 1485 image: api.ImageControl, 980 1486 header: api.HeaderControl 981 1487 }; 1488 api.panelConstructor = {}; 1489 api.sectionConstructor = {}; 982 1490 983 1491 $( function() { 984 1492 api.settings = window._wpCustomizeSettings; … … 1009 1517 } 1010 1518 }); 1011 1519 1520 // Expand/Collapse the main customizer customize info 1521 $( '#customize-info' ).find( '> .accordion-section-title' ).on( 'click keydown', function( e ) { 1522 if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key 1523 return; 1524 } 1525 e.preventDefault(); // Keep this AFTER the key filter above 1526 1527 var section = $( this ).parent(), 1528 content = section.find( '.accordion-section-content:first' ); 1529 1530 if ( section.hasClass( 'cannot-expand' ) ) { 1531 return; 1532 } 1533 1534 if ( section.hasClass( 'open' ) ) { 1535 section.toggleClass( 'open' ); 1536 content.slideUp( 150 ); 1537 } else { 1538 content.slideDown( 150 ); 1539 section.toggleClass( 'open' ); 1540 } 1541 }); 1542 1012 1543 // Initialize Previewer 1013 1544 api.previewer = new api.Previewer({ 1014 1545 container: '#customize-preview', … … 1102 1633 $.extend( this.nonce, nonce ); 1103 1634 }); 1104 1635 1636 // Create Settings 1105 1637 $.each( api.settings.settings, function( id, data ) { 1106 1638 api.create( id, id, data.value, { 1107 1639 transport: data.transport, … … 1109 1641 } ); 1110 1642 }); 1111 1643 1644 // Create Panels 1645 $.each( api.settings.panels, function ( id, data ) { 1646 var constructor = api.panelConstructor[ data.type ] || api.Panel, 1647 panel; 1648 1649 panel = new constructor( id, { 1650 params: data 1651 } ); 1652 api.panel.add( id, panel ); 1653 }); 1654 1655 // Create Sections 1656 $.each( api.settings.sections, function ( id, data ) { 1657 var constructor = api.sectionConstructor[ data.type ] || api.Section, 1658 section; 1659 1660 section = new constructor( id, { 1661 params: data 1662 } ); 1663 api.section.add( id, section ); 1664 }); 1665 1666 // Create Controls 1667 // @todo factor this out 1112 1668 $.each( api.settings.controls, function( id, data ) { 1113 1669 var constructor = api.controlConstructor[ data.type ] || api.Control, 1114 1670 control; 1115 1671 1116 control = api.control.add( id,new constructor( id, {1672 control = new constructor( id, { 1117 1673 params: data, 1118 1674 previewer: api.previewer 1119 } ) ); 1675 } ); 1676 api.control.add( id, control ); 1120 1677 }); 1121 1678 1679 /** 1680 * Sort panels, sections, controls by priorities. Hide empty sections and panels. 1681 */ 1682 api.reflowPaneContents = _.bind( function () { 1683 1684 var appendContainer, activeElement, rootNodes = []; 1685 1686 if ( document.activeElement ) { 1687 activeElement = $( document.activeElement ); 1688 } 1689 1690 api.panel.each( function ( panel ) { 1691 var sections = panel.sections(); 1692 rootNodes.push( panel ); 1693 appendContainer = panel.container.find( 'ul:first' ); 1694 _.chain( sections ).reverse().each( function ( section ) { 1695 appendContainer.append( section.container ); 1696 } ); 1697 } ); 1698 1699 api.section.each( function ( section ) { 1700 var controls = section.controls(); 1701 if ( ! section.panel() ) { 1702 rootNodes.push( section ); 1703 } 1704 appendContainer = section.container.find( 'ul:first' ); 1705 _.chain( controls ).reverse().each( function ( control ) { 1706 appendContainer.append( control.container ); 1707 } ); 1708 } ); 1709 1710 // Sort the root elements 1711 rootNodes.sort( function ( a, b ) { 1712 return a.priority() - b.priority(); 1713 } ); 1714 appendContainer = $( '#customize-theme-controls > ul' ); 1715 _.chain( rootNodes ).each( function ( rootNode ) { 1716 appendContainer.append( rootNode.container ); 1717 } ); 1718 1719 // Now re-trigger the active Value callbacks to that the panels and sections can decide whether they can be rendered 1720 api.panel.each( function ( panel ) { 1721 var value = panel.active(); 1722 panel.active.callbacks.fireWith( panel.active, [ value, value ] ); 1723 } ); 1724 api.section.each( function ( section ) { 1725 var value = section.active(); 1726 section.active.callbacks.fireWith( section.active, [ value, value ] ); 1727 } ); 1728 1729 if ( activeElement ) { 1730 activeElement.focus(); 1731 } 1732 }, api ); 1733 api.reflowPaneContents = _.debounce( api.reflowPaneContents, 100 ); 1734 $( [ api.panel, api.section, api.control ] ).each( function ( i, values ) { 1735 values.bind( 'add', api.reflowPaneContents ); 1736 values.bind( 'change', api.reflowPaneContents ); 1737 values.bind( 'remove', api.reflowPaneContents ); 1738 } ); 1739 api.bind( 'ready', api.reflowPaneContents ); 1740 1122 1741 // Check if preview url is valid and load the preview frame. 1123 1742 if ( api.previewer.previewUrl() ) { 1124 1743 api.previewer.refresh(); … … 1183 1802 event.preventDefault(); 1184 1803 }); 1185 1804 1805 // Go back to the top-level Customizer accordion. 1806 $( '#customize-header-actions' ).on( 'click keydown', '.control-panel-back', function( e ) { 1807 if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key 1808 return; 1809 } 1810 1811 e.preventDefault(); // Keep this AFTER the key filter above 1812 api.panel.each( function ( panel ) { 1813 panel.collapse(); 1814 }); 1815 }); 1816 1186 1817 closeBtn.keydown( function( event ) { 1187 1818 if ( 9 === event.which ) // tab 1188 1819 return; -
src/wp-includes/class-wp-customize-control.php
diff --git src/wp-includes/class-wp-customize-control.php src/wp-includes/class-wp-customize-control.php index 7937d2d..d38d22f 100644
class WP_Customize_Control { 74 74 public $input_attrs = array(); 75 75 76 76 /** 77 * @deprecated It is better to just call the json() method 77 78 * @access public 78 79 * @var array 79 80 */ … … class WP_Customize_Control { 219 220 220 221 $this->json['type'] = $this->type; 221 222 $this->json['active'] = $this->active(); 223 $this->json['section'] = $this->section; 224 $this->json['content'] = $this->get_content(); 225 } 226 227 /** 228 * Get the data to export to the client via JSON. 229 * 230 * @since 4.1.0 231 * 232 * @return array 233 */ 234 public function json() { 235 $this->to_json(); 236 return $this->json; 222 237 } 223 238 224 239 /** … … class WP_Customize_Control { 242 257 } 243 258 244 259 /** 260 * Get the control's content for insertion into the Customizer pane. 261 * 262 * @since 4.1.0 263 * 264 * @return string 265 */ 266 public final function get_content() { 267 ob_start(); 268 $this->maybe_render(); 269 $template = trim( ob_get_contents() ); 270 ob_end_clean(); 271 return $template; 272 } 273 274 /** 245 275 * Check capabilities and render the control. 246 276 * 247 277 * @since 3.4.0 -
src/wp-includes/class-wp-customize-manager.php
diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php index febd8bc..5041f82 100644
final class WP_Customize_Manager { 878 878 879 879 if ( ! $section->panel ) { 880 880 // Top-level section. 881 $sections[ ] = $section;881 $sections[ $section->id ] = $section; 882 882 } else { 883 883 // This section belongs to a panel. 884 884 if ( isset( $this->panels [ $section->panel ] ) ) { 885 $this->panels[ $section->panel ]->sections[ ] = $section;885 $this->panels[ $section->panel ]->sections[ $section->id ] = $section; 886 886 } 887 887 } 888 888 } … … final class WP_Customize_Manager { 899 899 continue; 900 900 } 901 901 902 u sort( $panel->sections, array( $this, '_cmp_priority' ) );903 $panels[ ] = $panel;902 uasort( $panel->sections, array( $this, '_cmp_priority' ) ); 903 $panels[ $panel->id ] = $panel; 904 904 } 905 905 $this->panels = $panels; 906 906 -
src/wp-includes/class-wp-customize-panel.php
diff --git src/wp-includes/class-wp-customize-panel.php src/wp-includes/class-wp-customize-panel.php index f289cb7..9ba0295 100644
class WP_Customize_Panel { 83 83 public $sections; 84 84 85 85 /** 86 * @since 4.1.0 87 * @access public 88 * @var string 89 */ 90 public $type; 91 92 /** 86 93 * Constructor. 87 94 * 88 95 * Any supplied $args override class property defaults. … … class WP_Customize_Panel { 110 117 } 111 118 112 119 /** 120 * Gather the parameters passed to client JavaScript via JSON. 121 * 122 * @since 4.1.0 123 * 124 * @return array The array to be exported to the client as JSON 125 */ 126 public function json() { 127 $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'type' ) ); 128 $array['content'] = $this->get_content(); 129 return $array; 130 } 131 132 /** 113 133 * Checks required user capabilities and whether the theme has the 114 134 * feature support required by the panel. 115 135 * … … class WP_Customize_Panel { 130 150 } 131 151 132 152 /** 153 * Get the panel's content template for insertion into the Customizer pane. 154 * 155 * @since 4.1.0 156 * 157 * @return string 158 */ 159 public final function get_content() { 160 ob_start(); 161 $this->maybe_render(); 162 $template = trim( ob_get_contents() ); 163 ob_end_clean(); 164 return $template; 165 } 166 167 /** 133 168 * Check capabilities and render the panel. 134 169 * 135 170 * @since 4.0.0 … … class WP_Customize_Panel { 189 224 */ 190 225 protected function render_content() { 191 226 ?> 192 <li class=" accordion-section control-section<?php if ( empty( $this->description ) ) echo ' cannot-expand';?>">227 <li class="panel-meta accordion-section control-section<?php if ( empty( $this->description ) ) { echo ' cannot-expand'; } ?>"> 193 228 <div class="accordion-section-title" tabindex="0"> 194 229 <span class="preview-notice"><?php 195 230 /* translators: %s is the site/panel title in the Customizer */ … … class WP_Customize_Panel { 203 238 <?php endif; ?> 204 239 </li> 205 240 <?php 206 foreach ( $this->sections as $section ) {207 $section->maybe_render();208 }209 241 } 210 242 } -
src/wp-includes/class-wp-customize-section.php
diff --git src/wp-includes/class-wp-customize-section.php src/wp-includes/class-wp-customize-section.php index d740ddb..a905f32 100644
class WP_Customize_Section { 92 92 public $controls; 93 93 94 94 /** 95 * @since 4.1.0 96 * @access public 97 * @var string 98 */ 99 public $type; 100 101 /** 95 102 * Constructor. 96 103 * 97 104 * Any supplied $args override class property defaults. … … class WP_Customize_Section { 118 125 } 119 126 120 127 /** 128 * Gather the parameters passed to client JavaScript via JSON. 129 * 130 * @since 4.1.0 131 * 132 * @return array The array to be exported to the client as JSON 133 */ 134 public function json() { 135 $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'panel', 'type' ) ); 136 $array['content'] = $this->get_content(); 137 return $array; 138 } 139 140 /** 121 141 * Checks required user capabilities and whether the theme has the 122 142 * feature support required by the section. 123 143 * … … class WP_Customize_Section { 136 156 } 137 157 138 158 /** 159 * Get the section's content template for insertion into the Customizer pane. 160 * 161 * @since 4.1.0 162 * 163 * @return string 164 */ 165 public final function get_content() { 166 ob_start(); 167 $this->maybe_render(); 168 $template = trim( ob_get_contents() ); 169 ob_end_clean(); 170 return $template; 171 } 172 173 /** 139 174 * Check capabilities and render the section. 140 175 * 141 176 * @since 3.4.0 … … class WP_Customize_Section { 172 207 */ 173 208 protected function render() { 174 209 $classes = 'control-section accordion-section'; 175 if ( $this->panel ) {176 $classes .= ' control-subsection';177 }178 210 ?> 179 211 <li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>"> 180 212 <h3 class="accordion-section-title" tabindex="0"> … … class WP_Customize_Section { 183 215 </h3> 184 216 <ul class="accordion-section-content"> 185 217 <?php if ( ! empty( $this->description ) ) : ?> 186 <li><p class="description customize-section-description"><?php echo $this->description; ?></p></li> 218 <li class="customize-section-description-container"> 219 <p class="description customize-section-description"><?php echo $this->description; ?></p> 220 </li> 187 221 <?php endif; ?> 188 <?php189 foreach ( $this->controls as $control )190 $control->maybe_render();191 ?>192 222 </ul> 193 223 </li> 194 224 <?php -
src/wp-includes/js/customize-base.js
diff --git src/wp-includes/js/customize-base.js src/wp-includes/js/customize-base.js index d2488dd..6ea2822 100644
window.wp = window.wp || {}; 184 184 to = this.validate( to ); 185 185 186 186 // Bail if the sanitized value is null or unchanged. 187 if ( null === to || _.isEqual( from, to ) ) 187 if ( null === to || _.isEqual( from, to ) ) { 188 188 return this; 189 } 189 190 190 191 this._value = to; 191 192 this._dirty = true;