Changeset 41648 for trunk/src/wp-admin/js/customize-controls.js
- Timestamp:
- 09/29/2017 08:12:19 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/js/customize-controls.js
r41626 r41648 1188 1188 section.containerParent = api.ensure( section.containerParent ); 1189 1189 1190 // Watch for changes to the panel state 1190 // Watch for changes to the panel state. 1191 1191 inject = function ( panelId ) { 1192 1192 var parentContainer; 1193 1193 if ( panelId ) { 1194 // The panel has been supplied, so wait until the panel object is registered 1194 // The panel has been supplied, so wait until the panel object is registered. 1195 1195 api.panel( panelId, function ( panel ) { 1196 // The panel has been registered, wait for it to become ready/initialized 1196 // The panel has been registered, wait for it to become ready/initialized. 1197 1197 panel.deferred.embedded.done( function () { 1198 1198 parentContainer = panel.contentContainer; … … 1219 1219 }; 1220 1220 section.panel.bind( inject ); 1221 inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one 1221 inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one. 1222 1222 }, 1223 1223 … … 1394 1394 * wp.customize.ThemesSection 1395 1395 * 1396 * Custom section for themes that functions similarly to a backwards panel,1397 * and alsohandles the theme-details view rendering and navigation.1396 * Custom section for themes that loads themes by category, and also 1397 * handles the theme-details view rendering and navigation. 1398 1398 * 1399 1399 * @constructor … … 1406 1406 template: '', 1407 1407 screenshotQueue: null, 1408 $window: $( window ), 1409 1410 /** 1408 $window: null, 1409 $body: null, 1410 loaded: 0, 1411 loading: false, 1412 fullyLoaded: false, 1413 term: '', 1414 tags: '', 1415 nextTerm: '', 1416 nextTags: '', 1417 filtersHeight: 0, 1418 headerContainer: null, 1419 1420 /** 1421 * Initialize. 1422 * 1423 * @since 4.9.0 1424 * 1425 * @param {string} id - ID. 1426 * @param {object} options - Options. 1427 * @returns {void} 1428 */ 1429 initialize: function( id, options ) { 1430 var section = this; 1431 section.headerContainer = $(); 1432 section.$window = $( window ); 1433 section.$body = $( document.body ); 1434 api.Section.prototype.initialize.call( section, id, options ); 1435 }, 1436 1437 /** 1438 * Embed the section in the DOM when the themes panel is ready. 1439 * 1440 * Insert the section before the themes container. Assume that a themes section is within a panel, but not necessarily the themes panel. 1441 * 1442 * @since 4.9.0 1443 */ 1444 embed: function() { 1445 var inject, 1446 section = this; 1447 1448 // Watch for changes to the panel state 1449 inject = function( panelId ) { 1450 var parentContainer; 1451 api.panel( panelId, function( panel ) { 1452 1453 // The panel has been registered, wait for it to become ready/initialized 1454 panel.deferred.embedded.done( function() { 1455 parentContainer = panel.contentContainer; 1456 if ( ! section.headContainer.parent().is( parentContainer ) ) { 1457 parentContainer.find( '.customize-themes-full-container-container' ).before( section.headContainer ); 1458 } 1459 if ( ! section.contentContainer.parent().is( section.headContainer ) ) { 1460 section.containerParent.append( section.contentContainer ); 1461 } 1462 section.deferred.embedded.resolve(); 1463 }); 1464 } ); 1465 }; 1466 section.panel.bind( inject ); 1467 inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one 1468 }, 1469 1470 /** 1471 * Set up. 1472 * 1411 1473 * @since 4.2.0 1412 */ 1413 initialize: function () { 1414 this.$customizeSidebar = $( '.wp-full-overlay-sidebar-content:first' ); 1415 return api.Section.prototype.initialize.apply( this, arguments ); 1416 }, 1417 1418 /** 1419 * @since 4.2.0 1420 */ 1421 ready: function () { 1474 * 1475 * @returns {void} 1476 */ 1477 ready: function() { 1422 1478 var section = this; 1423 1479 section.overlay = section.container.find( '.theme-overlay' ); … … 1442 1498 // Pressing the escape key fires a theme:collapse event 1443 1499 if ( 27 === event.keyCode ) { 1444 section.closeDetails(); 1500 if ( section.$body.hasClass( 'modal-open' ) ) { 1501 1502 // Escape from the details modal. 1503 section.closeDetails(); 1504 } else { 1505 1506 // Escape from the inifinite scroll list. 1507 section.headerContainer.find( '.customize-themes-section-title' ).focus(); 1508 } 1445 1509 event.stopPropagation(); // Prevent section from being collapsed. 1446 1510 } 1447 1511 }); 1448 1512 1449 _.bindAll( this, 'renderScreenshots' );1513 _.bindAll( this, 'renderScreenshots', 'loadMore', 'checkTerm', 'filtersChecked' ); 1450 1514 }, 1451 1515 … … 1454 1518 * 1455 1519 * Ignore the active states' of the contained theme controls, and just 1456 * use the section's own active state instead. This ensures empty search1457 * results for theme s to causethe section to become inactive.1520 * use the section's own active state instead. This prevents empty search 1521 * results for theme sections from causing the section to become inactive. 1458 1522 * 1459 1523 * @since 4.2.0 … … 1466 1530 1467 1531 /** 1532 * Attach events. 1533 * 1468 1534 * @since 4.2.0 1535 * 1536 * @returns {void} 1469 1537 */ 1470 1538 attachEvents: function () { 1471 var section = this ;1539 var section = this, debounced; 1472 1540 1473 1541 // Expand/Collapse accordion sections on click. … … 1480 1548 }); 1481 1549 1482 // Expand/Collapse section/panel. 1483 section.container.find( '.change-theme, .customize-theme' ).on( 'click keydown', function( event ) { 1484 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1485 return; 1486 } 1487 event.preventDefault(); // Keep this AFTER the key filter above 1488 1489 if ( section.expanded() ) { 1490 section.collapse(); 1491 } else { 1550 section.headerContainer = $( '#accordion-section-' + section.id ); 1551 1552 // Expand section/panel. Only collapse when opening another section. 1553 section.headerContainer.on( 'click', '.customize-themes-section-title', function() { 1554 1555 // Toggle accordion filters under section headers. 1556 if ( section.headerContainer.find( '.filter-details' ).length ) { 1557 section.headerContainer.find( '.customize-themes-section-title' ) 1558 .toggleClass( 'details-open' ) 1559 .attr( 'aria-expanded', function( i, attr ) { 1560 return 'true' === attr ? 'false' : 'true'; 1561 }); 1562 section.headerContainer.find( '.filter-details' ).slideToggle( 180 ); 1563 } 1564 1565 // Open the section. 1566 if ( ! section.expanded() ) { 1492 1567 section.expand(); 1493 1568 } 1494 1569 }); 1495 1570 1571 // Preview installed themes. 1572 section.container.on( 'click', '.theme-actions .preview-theme', function() { 1573 var themeId = $( this ).data( 'slug' ); 1574 1575 $( '.wp-full-overlay' ).addClass( 'customize-loading' ); 1576 api.panel( 'themes' ).loadThemePreview( themeId ).fail( function() { 1577 $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); 1578 } ); 1579 }); 1580 1496 1581 // Theme navigation in details view. 1497 section.container.on( 'click keydown', '.left', function( event ) { 1498 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1499 return; 1500 } 1501 1502 event.preventDefault(); // Keep this AFTER the key filter above 1503 1582 section.container.on( 'click', '.left', function() { 1504 1583 section.previousTheme(); 1505 1584 }); 1506 1585 1507 section.container.on( 'click keydown', '.right', function( event ) { 1508 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1509 return; 1510 } 1511 1512 event.preventDefault(); // Keep this AFTER the key filter above 1513 1586 section.container.on( 'click', '.right', function() { 1514 1587 section.nextTheme(); 1515 1588 }); 1516 1589 1517 section.container.on( 'click keydown', '.theme-backdrop, .close', function( event ) { 1518 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1519 return; 1520 } 1521 1522 event.preventDefault(); // Keep this AFTER the key filter above 1523 1590 section.container.on( 'click', '.theme-backdrop, .close', function() { 1524 1591 section.closeDetails(); 1525 1592 }); 1526 1593 1527 var renderScreenshots = _.throttle( _.bind( section.renderScreenshots, this ), 100 ); 1528 section.container.on( 'input', '#themes-filter', function( event ) { 1529 var count, 1530 term = event.currentTarget.value.toLowerCase().trim().replace( '-', ' ' ), 1531 controls = section.controls(); 1532 1533 _.each( controls, function( control ) { 1534 control.filter( term ); 1594 // Filter-search all theme objects loaded in the section. 1595 section.container.on( 'input', '.wp-filter-search-themes', function( event ) { 1596 section.filterSearch( event.currentTarget ); 1597 }); 1598 1599 // Event listeners for remote wporg queries with user-entered terms. 1600 if ( 'wporg' === section.params.action ) { 1601 1602 // Search terms. 1603 debounced = _.debounce( section.checkTerm, 500 ); // Wait until there is no input for 500 milliseconds to initiate a search. 1604 section.contentContainer.on( 'input', '#wp-filter-search-input', function() { 1605 debounced( section ); 1606 if ( ! section.expanded() ) { 1607 section.expand(); 1608 } 1609 section.checkTerm( section ); 1535 1610 }); 1536 1611 1537 renderScreenshots(); 1538 1539 // Update theme count. 1540 count = section.container.find( 'li.customize-control:visible' ).length; 1541 section.container.find( '.theme-count' ).text( count ); 1542 }); 1543 1544 // Pre-load the first 3 theme screenshots. 1545 api.bind( 'ready', function () { 1546 _.each( section.controls().slice( 0, 3 ), function ( control ) { 1547 var img, src = control.params.theme.screenshot[0]; 1548 if ( src ) { 1549 img = new Image(); 1550 img.src = src; 1612 // Feature filters. 1613 section.contentContainer.on( 'click', '.filter-group input', function() { 1614 section.filtersChecked(); 1615 section.checkTerm( section ); 1616 }); 1617 1618 // Toggle feature filter sections. 1619 section.contentContainer.on( 'click', '.feature-filter-toggle', function( e ) { 1620 $( e.currentTarget ) 1621 .toggleClass( 'open' ) 1622 .attr( 'aria-expanded', function( i, attr ) { 1623 return 'true' === attr ? 'false' : 'true'; 1624 }) 1625 .next( '.filter-drawer' ).slideToggle( 180, 'linear', function() { 1626 if ( 0 === section.filtersHeight ) { 1627 section.filtersHeight = $( this ).height(); 1628 1629 // First time, so it's opened. 1630 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 ); 1631 } 1632 }); 1633 if ( $( e.currentTarget ).hasClass( 'open' ) ) { 1634 section.contentContainer.find( '.themes' ).css( 'margin-top', section.filtersHeight + 76 ); 1635 } else { 1636 section.contentContainer.find( '.themes' ).css( 'margin-top', 0 ); 1551 1637 } 1552 1638 }); 1639 } 1640 1641 // Setup section cross-linking. 1642 section.contentContainer.on( 'click', '.no-themes-local .search-dotorg-themes', function() { 1643 api.section( 'wporg_themes' ).focus(); 1644 }); 1645 1646 // Move section controls to the themes area. 1647 api.bind( 'ready', function () { 1648 section.contentContainer = section.container.find( '.customize-themes-section' ); 1649 section.contentContainer.appendTo( $( '.customize-themes-full-container' ) ); 1650 section.container.add( section.headerContainer ); 1553 1651 }); 1554 1652 }, … … 1562 1660 * @param {Object} args 1563 1661 * @param {Boolean} args.unchanged 1564 * @param {Callback} args.completeCallback 1662 * @param {Function} args.completeCallback 1663 * @returns {void} 1565 1664 */ 1566 1665 onChangeExpanded: function ( expanded, args ) { 1666 1667 // Note: there is a second argument 'args' passed 1668 var section = this, 1669 container = section.contentContainer.closest( '.customize-themes-full-container' ); 1567 1670 1568 1671 // Immediately call the complete callback if there were no changes … … 1574 1677 } 1575 1678 1576 // Note: there is a second argument 'args' passed 1577 var panel = this, 1578 section = panel.contentContainer, 1579 overlay = section.closest( '.wp-full-overlay' ), 1580 container = section.closest( '.wp-full-overlay-sidebar-content' ), 1581 customizeBtn = section.find( '.customize-theme' ), 1582 changeBtn = panel.headContainer.find( '.change-theme' ); 1583 1584 if ( expanded && ! section.hasClass( 'current-panel' ) ) { 1679 if ( expanded ) { 1680 1681 // Try to load controls if none are loaded yet. 1682 if ( 0 === section.loaded ) { 1683 section.loadControls(); 1684 } 1685 1585 1686 // Collapse any sibling sections/panels 1586 1687 api.section.each( function ( otherSection ) { 1587 if ( otherSection !== panel ) { 1688 var searchTerm; 1689 1690 if ( otherSection !== section ) { 1691 1692 // Try to sync the current search term to the new section. 1693 if ( 'themes' === otherSection.params.type ) { 1694 searchTerm = otherSection.contentContainer.find( '.wp-filter-search' ).val(); 1695 section.contentContainer.find( '.wp-filter-search' ).val( searchTerm ); 1696 1697 // Directly initialize an empty remote search to avoid a race condition. 1698 if ( '' === searchTerm && '' !== section.term && 'installed' !== section.params.action ) { 1699 section.term = ''; 1700 section.initializeNewQuery( section.term, section.tags ); 1701 } else { 1702 section.checkTerm( section ); 1703 } 1704 section.filterSearch( section.contentContainer.find( '.wp-filter-search' ).get( 0 ) ); 1705 } 1588 1706 otherSection.collapse( { duration: args.duration } ); 1589 1707 } 1590 1708 }); 1591 api.panel.each( function ( otherPanel ) { 1592 otherPanel.collapse( { duration: 0 } ); 1709 1710 section.contentContainer.addClass( 'current-section' ); 1711 container.scrollTop(); 1712 section.headerContainer.find( '.customize-themes-section-title' ).addClass( 'selected' ).attr( 'aria-expanded', 'true' ); 1713 1714 container.on( 'scroll', _.throttle( section.renderScreenshots, 300 ) ); 1715 container.on( 'scroll', _.throttle( section.loadMore, 300 ) ); 1716 1717 if ( args.completeCallback ) { 1718 args.completeCallback(); 1719 } 1720 section.updateCount(); // Show this section's count. 1721 } else { 1722 section.contentContainer.removeClass( 'current-section' ); 1723 1724 // Always hide, even if they don't exist or are already hidden. 1725 section.headerContainer.find( '.customize-themes-section-title' ).removeClass( 'selected details-open' ).attr( 'aria-expanded', 'false' ); 1726 section.headerContainer.find( '.filter-details' ).slideUp( 180 ); 1727 1728 container.off( 'scroll' ); 1729 1730 if ( args.completeCallback ) { 1731 args.completeCallback(); 1732 } 1733 } 1734 }, 1735 1736 /** 1737 * Return the section's content element without detaching from the parent. 1738 * 1739 * @since 4.9.0 1740 * 1741 * @returns {jQuery} 1742 */ 1743 getContent: function() { 1744 return this.container.find( '.control-section-content' ); 1745 }, 1746 1747 /** 1748 * Load theme data via Ajax and add themes to the section as controls. 1749 * 1750 * @since 4.9.0 1751 * 1752 * @returns {void} 1753 */ 1754 loadControls: function() { 1755 var section = this, params, page, request; 1756 1757 if ( section.loading ) { 1758 return; // We're already loading a batch of themes. 1759 } 1760 1761 // Parameters for every API query. Additional params are set in PHP. 1762 page = Math.ceil( section.loaded / 100 ) + 1; 1763 params = { 1764 'switch-themes-nonce': api.settings.nonce['switch-themes'], 1765 'wp_customize': 'on', 1766 'theme_action': section.params.action, 1767 'customized_theme': api.settings.theme.stylesheet, 1768 'page': page 1769 }; 1770 1771 // Add fields for wporg actions. 1772 if ( 'wporg' === section.params.action ) { 1773 params.search = section.term; 1774 params.tags = section.tags; 1775 } 1776 1777 // Load themes. 1778 section.headContainer.closest( '.wp-full-overlay' ).addClass( 'loading' ); 1779 section.loading = true; 1780 section.container.find( '.no-themes' ).hide(); 1781 request = wp.ajax.post( 'customize-load-themes', params ); 1782 request.done(function( data ) { 1783 var themes = data.themes, themeControl, newThemeControls; 1784 1785 // Stop and try again if the term changed while loading. 1786 if ( '' !== section.nextTerm || '' !== section.nextTags ) { 1787 if ( section.nextTerm ) { 1788 section.term = section.nextTerm; 1789 } 1790 if ( section.nextTags ) { 1791 section.tags = section.nextTags; 1792 } 1793 section.nextTerm = ''; 1794 section.nextTags = ''; 1795 section.loading = false; 1796 section.loadControls(); 1797 return; 1798 } 1799 1800 if ( 0 !== themes.length ) { 1801 newThemeControls = []; 1802 1803 // Add controls for each theme. 1804 _.each( themes, function( theme ) { 1805 var customizeId = section.params.action + '_theme_' + theme.id; 1806 themeControl = new api.controlConstructor.theme( customizeId, { 1807 params: { 1808 type: 'theme', 1809 content: '<li id="customize-control-theme-' + section.params.action + '_' + theme.id + '" class="customize-control customize-control-theme"></li>', 1810 section: section.params.id, 1811 active: true, 1812 theme: theme, 1813 priority: section.loaded + 1 1814 }, 1815 previewer: api.previewer 1816 } ); 1817 1818 api.control.add( customizeId, themeControl ); 1819 newThemeControls.push( themeControl ); 1820 section.loaded = section.loaded + 1; 1821 }); 1822 1823 if ( 1 === page ) { 1824 1825 // Pre-load the first 3 theme screenshots. 1826 _.each( section.controls().slice( 0, 3 ), function( control ) { 1827 var img, src = control.params.theme.screenshot[0]; 1828 if ( src ) { 1829 img = new Image(); 1830 img.src = src; 1831 } 1832 }); 1833 if ( 'installed' !== section.params.action ) { 1834 wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) ); 1835 } 1836 } else { 1837 Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue. 1838 } 1839 _.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible. 1840 1841 if ( 'installed' === section.params.action || 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list. 1842 section.fullyLoaded = true; 1843 } 1844 } else { 1845 if ( 0 === section.loaded ) { 1846 section.container.find( '.no-themes' ).show(); 1847 wp.a11y.speak( section.container.find( '.no-themes' ).text() ); 1848 } else { 1849 section.fullyLoaded = true; 1850 } 1851 } 1852 if ( 'installed' === section.params.action ) { 1853 section.updateCount(); // Count of visible theme controls. 1854 } else { 1855 section.updateCount( data.info.results ); // Total number of results including pages not yet loaded. 1856 } 1857 section.container.find( '.unexpected-error' ).hide(); // Hide error notice in case it was previously shown. 1858 1859 // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case. 1860 section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' ); 1861 section.loading = false; 1862 }); 1863 request.fail(function( data ) { 1864 if ( 'undefined' === typeof data ) { 1865 section.container.find( '.unexpected-error' ).show(); 1866 wp.a11y.speak( section.container.find( '.unexpected-error' ).text() ); 1867 } else if ( 'undefined' !== typeof console && console.error ) { 1868 console.error( data ); 1869 } 1870 1871 // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case. 1872 section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' ); 1873 section.loading = false; 1874 }); 1875 }, 1876 1877 /** 1878 * Determines whether more themes should be loaded, and loads them. 1879 * 1880 * @since 4.9.0 1881 * @returns {void} 1882 */ 1883 loadMore: function() { 1884 var section = this, container, bottom, threshold; 1885 if ( ! section.fullyLoaded && ! section.loading ) { 1886 container = section.container.closest( '.customize-themes-full-container' ); 1887 1888 bottom = container.scrollTop() + container.height(); 1889 threshold = container.prop( 'scrollHeight' ) - 3000; // Use a fixed distance to the bottom of loaded results to avoid unnecessarily loading results sooner when using a percentage of scroll distance. 1890 1891 if ( bottom > threshold ) { 1892 section.loadControls(); 1893 } 1894 } 1895 }, 1896 1897 /** 1898 * Event handler for search input that filters visible controls. 1899 * 1900 * @since 4.9.0 1901 * 1902 * @param {Element} el - The search input element as a raw JS object. 1903 * @returns {void} 1904 */ 1905 filterSearch: function( el ) { 1906 var count = 0, 1907 visible = false, 1908 section = this, 1909 noFilter = ( undefined !== api.section( 'wporg_themes' ) && 'wporg' !== section.params.action ) ? '.no-themes-local' : '.no-themes', 1910 term = el.value.toLowerCase().trim().replace( '-', ' ' ), 1911 controls = section.controls(), 1912 renderScreenshots; 1913 1914 if ( section.loading ) { 1915 return; 1916 } 1917 1918 _.each( controls, function( control ) { 1919 visible = control.filter( term ); 1920 if ( visible ) { 1921 count = count + 1; 1922 } 1923 }); 1924 1925 if ( 0 === count ) { 1926 section.container.find( noFilter ).show(); 1927 wp.a11y.speak( section.container.find( noFilter ).text() ); 1928 } else { 1929 section.container.find( noFilter ).hide(); 1930 } 1931 1932 renderScreenshots = _.throttle( _.bind( section.renderScreenshots, this ), 100 ); 1933 1934 renderScreenshots(); 1935 1936 // Update theme count. 1937 section.updateCount( count ); 1938 }, 1939 1940 /** 1941 * Event handler for search input that determines if the terms have changed and loads new controls as needed. 1942 * 1943 * @since 4.9.0 1944 * 1945 * @param {wp.customize.ThemesSection} section - The current theme section, passed through the debouncer. 1946 * @returns {void} 1947 */ 1948 checkTerm: function( section ) { 1949 var newTerm; 1950 if ( 'wporg' === section.params.action ) { 1951 newTerm = $( '#wp-filter-search-input' ).val(); 1952 if ( section.term !== newTerm ) { 1953 section.initializeNewQuery( newTerm, section.tags ); 1954 } 1955 } 1956 }, 1957 1958 /** 1959 * Check for filters checked in the feature filter list and initialize a new query. 1960 * 1961 * @since 4.9.0 1962 * 1963 * @returns {void} 1964 */ 1965 filtersChecked: function() { 1966 var section = this, 1967 items = section.container.find( '.filter-group' ).find( ':checkbox' ), 1968 tags = []; 1969 1970 _.each( items.filter( ':checked' ), function( item ) { 1971 tags.push( $( item ).prop( 'value' ) ); 1972 }); 1973 1974 // When no filters are checked, restore initial state. Update filter count. 1975 if ( 0 === tags.length ) { 1976 tags = ''; 1977 section.contentContainer.find( '.feature-filter-toggle .filter-count-0' ).show(); 1978 section.contentContainer.find( '.feature-filter-toggle .filter-count-filters' ).hide(); 1979 } else { 1980 section.contentContainer.find( '.feature-filter-toggle .theme-filter-count' ).text( tags.length ); 1981 section.contentContainer.find( '.feature-filter-toggle .filter-count-0' ).hide(); 1982 section.contentContainer.find( '.feature-filter-toggle .filter-count-filters' ).show(); 1983 } 1984 1985 // Check whether tags have changed, and either load or queue them. 1986 if ( ! _.isEqual( section.tags, tags ) ) { 1987 if ( section.loading ) { 1988 section.nextTags = tags; 1989 } else { 1990 section.initializeNewQuery( section.term, tags ); 1991 } 1992 } 1993 }, 1994 1995 /** 1996 * Reset the current query and load new results. 1997 * 1998 * @since 4.9.0 1999 * 2000 * @param {string} newTerm - New term. 2001 * @param {Array} newTags - New tags. 2002 * @returns {void} 2003 */ 2004 initializeNewQuery: function( newTerm, newTags ) { 2005 var section = this; 2006 2007 // Clear the controls in the section. 2008 _.each( section.controls(), function( control ) { 2009 control.container.remove(); 2010 api.control.remove( control.id ); 2011 }); 2012 section.loaded = 0; 2013 section.fullyLoaded = false; 2014 section.screenshotQueue = null; 2015 2016 // Run a new query, with loadControls handling paging, etc. 2017 if ( ! section.loading ) { 2018 section.term = newTerm; 2019 section.tags = newTags; 2020 section.loadControls(); 2021 } else { 2022 section.nextTerm = newTerm; // This will reload from loadControls() with the newest term once the current batch is loaded. 2023 section.nextTags = newTags; // This will reload from loadControls() with the newest tags once the current batch is loaded. 2024 } 2025 if ( ! section.expanded() ) { 2026 section.expand(); // Expand the section if it isn't expanded. 2027 } 2028 }, 2029 2030 /** 2031 * Render control's screenshot if the control comes into view. 2032 * 2033 * @since 4.2.0 2034 * 2035 * @returns {void} 2036 */ 2037 renderScreenshots: function() { 2038 var section = this; 2039 2040 // Fill queue initially, or check for more if empty. 2041 if ( null === section.screenshotQueue || 0 === section.screenshotQueue.length ) { 2042 2043 // Add controls that haven't had their screenshots rendered. 2044 section.screenshotQueue = _.filter( section.controls(), function( control ) { 2045 return ! control.screenshotRendered; 1593 2046 }); 1594 1595 panel._animateChangeExpanded( function() { 1596 changeBtn.attr( 'tabindex', '-1' ); 1597 customizeBtn.attr( 'tabindex', '0' ); 1598 1599 customizeBtn.focus(); 1600 section.css( 'top', '' ); 1601 container.scrollTop( 0 ); 1602 1603 if ( args.completeCallback ) { 1604 args.completeCallback(); 1605 } 1606 } ); 1607 1608 overlay.addClass( 'in-themes-panel' ); 1609 section.addClass( 'current-panel' ); 1610 _.delay( panel.renderScreenshots, 10 ); // Wait for the controls 1611 panel.$customizeSidebar.on( 'scroll.customize-themes-section', _.throttle( panel.renderScreenshots, 300 ) ); 1612 1613 } else if ( ! expanded && section.hasClass( 'current-panel' ) ) { 1614 panel._animateChangeExpanded( function() { 1615 changeBtn.attr( 'tabindex', '0' ); 1616 customizeBtn.attr( 'tabindex', '-1' ); 1617 1618 changeBtn.focus(); 1619 section.css( 'top', '' ); 1620 1621 if ( args.completeCallback ) { 1622 args.completeCallback(); 1623 } 1624 } ); 1625 1626 overlay.removeClass( 'in-themes-panel' ); 1627 section.removeClass( 'current-panel' ); 1628 panel.$customizeSidebar.off( 'scroll.customize-themes-section' ); 1629 } 1630 }, 1631 1632 /** 1633 * Render control's screenshot if the control comes into view. 1634 * 1635 * @since 4.2.0 1636 */ 1637 renderScreenshots: function( ) { 1638 var section = this; 1639 1640 // Fill queue initially. 1641 if ( section.screenshotQueue === null ) { 1642 section.screenshotQueue = section.controls(); 1643 } 1644 1645 // Are all screenshots rendered? 2047 } 2048 2049 // Are all screenshots rendered (for now)? 1646 2050 if ( ! section.screenshotQueue.length ) { 1647 2051 return; … … 1679 2083 1680 2084 /** 2085 * Get visible count. 2086 * 2087 * @since 4.9.0 2088 * 2089 * @returns {int} Visible count. 2090 */ 2091 getVisibleCount: function() { 2092 return this.contentContainer.find( 'li.customize-control:visible' ).length; 2093 }, 2094 2095 /** 2096 * Update the number of themes in the section. 2097 * 2098 * @since 4.9.0 2099 * 2100 * @returns {void} 2101 */ 2102 updateCount: function( count ) { 2103 var section = this, countEl, displayed; 2104 2105 if ( ! count && 0 !== count ) { 2106 count = section.getVisibleCount(); 2107 } 2108 2109 displayed = section.contentContainer.find( '.themes-displayed' ); 2110 countEl = section.contentContainer.find( '.theme-count' ); 2111 2112 if ( 0 === count ) { 2113 countEl.text( '0' ); 2114 } else { 2115 2116 // Animate the count change for emphasis. 2117 displayed.fadeOut( 180, function() { 2118 countEl.text( count ); 2119 displayed.fadeIn( 180 ); 2120 } ); 2121 wp.a11y.speak( api.settings.l10n.announceThemeCount.replace( '%d', count ) ); 2122 } 2123 }, 2124 2125 /** 1681 2126 * Advance the modal to the next theme. 1682 2127 * 1683 2128 * @since 4.2.0 2129 * 2130 * @returns {void} 1684 2131 */ 1685 2132 nextTheme: function () { … … 1696 2143 * 1697 2144 * @since 4.2.0 2145 * 2146 * @returns {object|boolean} Next theme. 1698 2147 */ 1699 2148 getNextTheme: function () { 1700 var control, next;1701 control = api.control( 'theme_' + this.currentTheme );2149 var section = this, control, next; 2150 control = api.control( section.params.action + '_theme_' + this.currentTheme ); 1702 2151 next = control.container.next( 'li.customize-control-theme' ); 1703 2152 if ( ! next.length ) { 1704 2153 return false; 1705 2154 } 1706 next = next[0].id.replace( 'customize-control- ', '' );2155 next = next[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' ); 1707 2156 control = api.control( next ); 1708 2157 … … 1714 2163 * 1715 2164 * @since 4.2.0 2165 * @returns {void} 1716 2166 */ 1717 2167 previousTheme: function () { … … 1728 2178 * 1729 2179 * @since 4.2.0 2180 * @returns {object|boolean} Previous theme. 1730 2181 */ 1731 2182 getPreviousTheme: function () { 1732 var control, previous;1733 control = api.control( 'theme_' + this.currentTheme );2183 var section = this, control, previous; 2184 control = api.control( section.params.action + '_theme_' + this.currentTheme ); 1734 2185 previous = control.container.prev( 'li.customize-control-theme' ); 1735 2186 if ( ! previous.length ) { 1736 2187 return false; 1737 2188 } 1738 previous = previous[0].id.replace( 'customize-control- ', '' );2189 previous = previous[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' ); 1739 2190 control = api.control( previous ); 1740 2191 … … 1746 2197 * 1747 2198 * @since 4.2.0 2199 * 2200 * @returns {void} 1748 2201 */ 1749 2202 updateLimits: function () { … … 1821 2274 * @since 4.2.0 1822 2275 * 1823 * @param {Object} theme 2276 * @param {object} theme - Theme. 2277 * @param {Function} [callback] - Callback once the details have been shown. 2278 * @returns {void} 1824 2279 */ 1825 2280 showDetails: function ( theme, callback ) { 1826 var section = this, link; 1827 callback = callback || function(){}; 2281 var section = this; 1828 2282 section.currentTheme = theme.id; 1829 2283 section.overlay.html( section.template( theme ) ) 1830 2284 .fadeIn( 'fast' ) 1831 2285 .focus(); 1832 $( 'body' ).addClass( 'modal-open' );2286 section.$body.addClass( 'modal-open' ); 1833 2287 section.containFocus( section.overlay ); 1834 2288 section.updateLimits(); 1835 1836 link = section.overlay.find( '.inactive-theme > a' ); 1837 1838 link.on( 'click', function( event ) { 1839 event.preventDefault(); 1840 1841 // Short-circuit if request is currently being made. 1842 if ( link.hasClass( 'disabled' ) ) { 1843 return; 1844 } 1845 link.addClass( 'disabled' ); 1846 1847 section.loadThemePreview( theme.id ).fail( function() { 1848 link.removeClass( 'disabled' ); 1849 } ); 1850 } ); 1851 callback(); 2289 wp.a11y.speak( api.settings.l10n.announceThemeDetails.replace( '%s', theme.name ) ); 2290 if ( callback ) { 2291 callback(); 2292 } 1852 2293 }, 1853 2294 … … 1856 2297 * 1857 2298 * @since 4.2.0 2299 * 2300 * @returns {void} 1858 2301 */ 1859 2302 closeDetails: function () { 1860 $( 'body' ).removeClass( 'modal-open' ); 1861 this.overlay.fadeOut( 'fast' ); 1862 api.control( 'theme_' + this.currentTheme ).focus(); 2303 var section = this; 2304 section.$body.removeClass( 'modal-open' ); 2305 section.overlay.fadeOut( 'fast' ); 2306 api.control( section.params.action + '_theme_' + section.currentTheme ).container.find( '.theme' ).focus(); 1863 2307 }, 1864 2308 … … 1867 2311 * 1868 2312 * @since 4.2.0 2313 * 2314 * @param {jQuery} el - Element to contain focus. 2315 * @returns {void} 1869 2316 */ 1870 2317 containFocus: function( el ) { … … 1919 2366 section.containerParent = '#customize-outer-theme-controls'; 1920 2367 section.containerPaneParent = '.customize-outer-pane-parent'; 1921 returnapi.Section.prototype.initialize.apply( section, arguments );2368 api.Section.prototype.initialize.apply( section, arguments ); 1922 2369 }, 1923 2370 … … 1940 2387 backBtn = content.find( '.customize-section-back' ), 1941 2388 sectionTitle = section.headContainer.find( '.accordion-section-title' ).first(), 1942 body = $( 'body'),2389 body = $( document.body ), 1943 2390 expand, panel; 1944 2391 … … 2059 2506 if ( ! panel.contentContainer.parent().is( panel.headContainer ) ) { 2060 2507 container.append( panel.contentContainer ); 2061 panel.renderContent();2062 }2508 } 2509 panel.renderContent(); 2063 2510 2064 2511 panel.deferred.embedded.resolve(); … … 2132 2579 * @since 4.1.0 2133 2580 * 2134 * @returns {boolean} 2581 * @returns {boolean} Whether contextually active. 2135 2582 */ 2136 2583 isContextuallyActive: function () { … … 2147 2594 2148 2595 /** 2149 * Update UI to reflect expanded state 2596 * Update UI to reflect expanded state. 2150 2597 * 2151 2598 * @since 4.1.0 … … 2155 2602 * @param {Boolean} args.unchanged 2156 2603 * @param {Function} args.completeCallback 2604 * @returns {void} 2157 2605 */ 2158 2606 onChangeExpanded: function ( expanded, args ) { … … 2262 2710 panel.contentContainer.html( template( panel.params ) ); 2263 2711 } 2712 } 2713 }); 2714 2715 /** 2716 * Class wp.customize.ThemesPanel. 2717 * 2718 * Custom section for themes that displays without the customize preview. 2719 * 2720 * @constructor 2721 * @augments wp.customize.Panel 2722 * @augments wp.customize.Container 2723 */ 2724 api.ThemesPanel = api.Panel.extend({ 2725 2726 /** 2727 * Initialize. 2728 * 2729 * @since 4.9.0 2730 * 2731 * @param {string} id - The ID for the panel. 2732 * @param {object} options - Options. 2733 * @returns {void} 2734 */ 2735 initialize: function( id, options ) { 2736 var panel = this; 2737 panel.installingThemes = []; 2738 api.Panel.prototype.initialize.call( panel, id, options ); 2739 }, 2740 2741 /** 2742 * Attach events. 2743 * 2744 * @since 4.9.0 2745 * @returns {void} 2746 */ 2747 attachEvents: function() { 2748 var panel = this; 2749 2750 // Attach regular panel events. 2751 api.Panel.prototype.attachEvents.apply( panel ); 2752 2753 // Collapse panel to customize the current theme. 2754 panel.contentContainer.on( 'click', '.customize-theme', function() { 2755 panel.collapse(); 2756 }); 2757 2758 // Toggle between filtering and browsing themes on mobile. 2759 panel.contentContainer.on( 'click', '.customize-themes-section-title, .customize-themes-mobile-back', function() { 2760 $( '.wp-full-overlay' ).toggleClass( 'showing-themes' ); 2761 }); 2762 2763 // Install (and maybe preview) a theme. 2764 panel.contentContainer.on( 'click', '.theme-install', function( event ) { 2765 panel.installTheme( event ); 2766 }); 2767 2768 // Update a theme. Theme cards have the class, the details modal has the id. 2769 panel.contentContainer.on( 'click', '.update-theme, #update-theme', function( event ) { 2770 2771 // #update-theme is a link. 2772 event.preventDefault(); 2773 event.stopPropagation(); 2774 2775 panel.updateTheme( event ); 2776 }); 2777 2778 // Delete a theme. 2779 panel.contentContainer.on( 'click', '.delete-theme', function( event ) { 2780 panel.deleteTheme( event ); 2781 }); 2782 2783 _.bindAll( panel, 'installTheme', 'updateTheme' ); 2784 }, 2785 2786 /** 2787 * Update UI to reflect expanded state 2788 * 2789 * @since 4.9.0 2790 * 2791 * @param {Boolean} expanded - Expanded state. 2792 * @param {Object} args - Args. 2793 * @param {Boolean} args.unchanged - Whether or not the state changed. 2794 * @param {Function} args.completeCallback - Callback to execute when the animation completes. 2795 * @returns {void} 2796 */ 2797 onChangeExpanded: function( expanded, args ) { 2798 var panel = this, overlay; 2799 2800 // Expand/collapse the panel normally. 2801 api.Panel.prototype.onChangeExpanded.apply( this, [ expanded, args ] ); 2802 2803 // Immediately call the complete callback if there were no changes 2804 if ( args.unchanged ) { 2805 if ( args.completeCallback ) { 2806 args.completeCallback(); 2807 } 2808 return; 2809 } 2810 2811 overlay = panel.headContainer.closest( '.wp-full-overlay' ); 2812 2813 if ( expanded ) { 2814 overlay 2815 .addClass( 'in-themes-panel' ) 2816 .delay( 200 ).find( '.customize-themes-full-container' ).addClass( 'animate' ); 2817 2818 // Automatically open the installed themes section (except on small screens). 2819 if ( 600 < window.innerWidth ) { 2820 api.section( 'installed_themes' ).expand(); 2821 } 2822 } else { 2823 overlay 2824 .removeClass( 'in-themes-panel' ) 2825 .find( '.customize-themes-full-container' ).removeClass( 'animate' ); 2826 } 2827 }, 2828 2829 /** 2830 * Install a theme via wp.updates. 2831 * 2832 * @since 4.9.0 2833 * 2834 * @returns {void} 2835 */ 2836 installTheme: function( event ) { 2837 var panel = this, preview = false, slug = $( event.target ).data( 'slug' ); 2838 2839 if ( _.contains( panel.installingThemes, slug ) ) { 2840 return; // Theme is already being installed. 2841 } 2842 2843 wp.updates.maybeRequestFilesystemCredentials( event ); 2844 2845 $( document ).one( 'wp-theme-install-success', function( event, response ) { 2846 var theme = false, customizeId, themeControl; 2847 if ( preview ) { 2848 2849 panel.loadThemePreview( slug ).fail( function() { 2850 $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); 2851 } ); 2852 2853 } else { 2854 api.control.each( function( control ) { 2855 if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) { 2856 theme = control.params.theme; // Used below to add theme control. 2857 control.rerenderAsInstalled( true ); 2858 } 2859 }); 2860 2861 // Don't add the same theme more than once. 2862 if ( ! theme || api.control.has( 'installed_theme_' + theme.id ) ) { 2863 return; 2864 } 2865 2866 // Add theme control to installed section. 2867 theme.type = 'installed'; 2868 customizeId = 'installed_theme_' + theme.id; 2869 themeControl = new api.controlConstructor.theme( customizeId, { 2870 params: { 2871 type: 'theme', 2872 content: $( '<li class="customize-control customize-control-theme"></li>' ).attr( 'id', 'customize-control-theme-installed_' + theme.id ).prop( 'outerHTML' ), 2873 section: 'installed_themes', 2874 active: true, 2875 theme: theme, 2876 priority: 0 // Add all newly-installed themes to the top. 2877 }, 2878 previewer: api.previewer 2879 } ); 2880 2881 api.control.add( customizeId, themeControl ); 2882 api.control( customizeId ).container.trigger( 'render-screenshot' ); 2883 2884 // Close the details modal if it's open to the installed theme. 2885 api.section.each( function( section ) { 2886 if ( 'themes' === section.params.type ) { 2887 if ( theme.id === section.currentTheme ) { // Don't close the modal if the user has navigated elsewhere. 2888 section.closeDetails(); 2889 } 2890 } 2891 }); 2892 } 2893 } ); 2894 2895 panel.installingThemes.push( $( event.target ).data( 'slug' ) ); // Note: we don't remove elements from installingThemes, since they shouldn't be installed again. 2896 wp.updates.installTheme( { 2897 slug: slug 2898 } ); 2899 2900 // Also preview the theme as the event is triggered on Install & Preview. 2901 if ( $( event.target ).hasClass( 'preview' ) ) { 2902 preview = true; 2903 $( '.wp-full-overlay' ).addClass( 'customize-loading' ); 2904 wp.a11y.speak( $( '#customize-themes-loading-container .customize-loading-text-installing-theme' ).text() ); 2905 } 2906 }, 2907 2908 /** 2909 * Load theme preview. 2910 * 2911 * @since 4.9.0 2912 * 2913 * @param {string} themeId Theme ID. 2914 * @returns {jQuery.promise} Promise. 2915 */ 2916 loadThemePreview: function( themeId ) { 2917 var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser; 2918 2919 urlParser = document.createElement( 'a' ); 2920 urlParser.href = location.href; 2921 urlParser.search = $.param( _.extend( 2922 api.utils.parseQueryString( urlParser.search.substr( 1 ) ), 2923 { 2924 theme: themeId, 2925 changeset_uuid: api.settings.changeset.uuid 2926 } 2927 ) ); 2928 2929 // Update loading message. Everything else is handled by reloading the page. 2930 $( '#customize-themes-loading-container span' ).hide(); 2931 $( '#customize-themes-loading-container .customize-loading-text' ).css( 'display', 'block' ); 2932 wp.a11y.speak( $( '#customize-themes-loading-container .customize-loading-text' ).text() ); 2933 overlay = $( '.wp-full-overlay' ); 2934 overlay.addClass( 'customize-loading' ); 2935 2936 onceProcessingComplete = function() { 2937 var request; 2938 if ( api.state( 'processing' ).get() > 0 ) { 2939 return; 2940 } 2941 2942 api.state( 'processing' ).unbind( onceProcessingComplete ); 2943 2944 request = api.requestChangesetUpdate(); 2945 request.done( function() { 2946 deferred.resolve(); 2947 $( window ).off( 'beforeunload.customize-confirm' ); 2948 window.location.href = urlParser.href; 2949 } ); 2950 request.fail( function() { 2951 overlay.removeClass( 'customize-loading' ); 2952 deferred.reject(); 2953 } ); 2954 }; 2955 2956 if ( 0 === api.state( 'processing' ).get() ) { 2957 onceProcessingComplete(); 2958 } else { 2959 api.state( 'processing' ).bind( onceProcessingComplete ); 2960 } 2961 2962 return deferred.promise(); 2963 }, 2964 2965 /** 2966 * Update a theme via wp.updates. 2967 * 2968 * @since 4.9.0 2969 * 2970 * @param {jQuery.Event} event - Event. 2971 * @returns {void} 2972 */ 2973 updateTheme: function( event ) { 2974 wp.updates.maybeRequestFilesystemCredentials( event ); 2975 2976 $( document ).one( 'wp-theme-update-success', function( e, response ) { 2977 2978 // Rerender the control to reflect the update. 2979 api.control.each( function( control ) { 2980 if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) { 2981 control.params.theme.hasUpdate = false; 2982 control.rerenderAsInstalled( true ); 2983 } 2984 }); 2985 } ); 2986 2987 wp.updates.updateTheme( { 2988 slug: $( event.target ).closest( '.notice' ).data( 'slug' ) 2989 } ); 2990 }, 2991 2992 /** 2993 * Delete a theme via wp.updates. 2994 * 2995 * @since 4.9.0 2996 * 2997 * @param {jQuery.Event} event - Event. 2998 * @returns {void} 2999 */ 3000 deleteTheme: function( event ) { 3001 var theme, section; 3002 theme = $( event.target ).data( 'slug' ); 3003 section = api.section( 'installed_themes' ); 3004 3005 event.preventDefault(); 3006 3007 // Confirmation dialog for deleting a theme. 3008 if ( ! window.confirm( api.settings.l10n.confirmDeleteTheme ) ) { 3009 return; 3010 } 3011 3012 wp.updates.maybeRequestFilesystemCredentials( event ); 3013 3014 $( document ).one( 'wp-theme-delete-success', function() { 3015 var control = api.control( 'installed_theme_' + theme ); 3016 3017 // Remove theme control. 3018 control.container.remove(); 3019 api.control.remove( control.id ); 3020 3021 // Update installed count. 3022 section.loaded = section.loaded - 1; 3023 section.updateCount(); 3024 3025 // Rerender any other theme controls as uninstalled. 3026 api.control.each( function( control ) { 3027 if ( 'theme' === control.params.type && control.params.theme.id === theme ) { 3028 control.rerenderAsInstalled( false ); 3029 } 3030 }); 3031 } ); 3032 3033 wp.updates.deleteTheme( { 3034 slug: theme 3035 } ); 3036 3037 // Close modal and focus the section. 3038 section.closeDetails(); 3039 section.focus(); 2264 3040 } 2265 3041 }); … … 2614 3390 * @param {Object} args 2615 3391 * @param {Number} args.duration 2616 * @param { Callback} args.completeCallback3392 * @param {Function} args.completeCallback 2617 3393 */ 2618 3394 onChangeActive: function ( active, args ) { … … 3786 4562 3787 4563 touchDrag: false, 3788 isRendered: false, 3789 3790 /** 3791 * Defer rendering the theme control until the section is displayed. 3792 * 3793 * @since 4.2.0 3794 */ 3795 renderContent: function () { 3796 var control = this, 3797 renderContentArgs = arguments; 3798 3799 api.section( control.section(), function( section ) { 3800 if ( section.expanded() ) { 3801 api.Control.prototype.renderContent.apply( control, renderContentArgs ); 3802 control.isRendered = true; 3803 } else { 3804 section.expanded.bind( function( expanded ) { 3805 if ( expanded && ! control.isRendered ) { 3806 api.Control.prototype.renderContent.apply( control, renderContentArgs ); 3807 control.isRendered = true; 3808 } 3809 } ); 3810 } 3811 } ); 3812 }, 4564 screenshotRendered: false, 3813 4565 3814 4566 /** … … 3834 4586 3835 4587 // Prevent the modal from showing when the user clicks the action button. 3836 if ( $( event.target ).is( '.theme-actions .button ' ) ) {4588 if ( $( event.target ).is( '.theme-actions .button, .update-theme' ) ) { 3837 4589 return; 3838 4590 } 3839 4591 3840 api.section( control.section() ).loadThemePreview( control.params.theme.id );3841 });3842 3843 control.container.on( 'click keydown', '.theme-actions .theme-details', function( event ) {3844 if ( api.utils.isKeydownButNotEnterEvent( event ) ) {3845 return;3846 }3847 3848 4592 event.preventDefault(); // Keep this AFTER the key filter above 3849 3850 4593 api.section( control.section() ).showDetails( control.params.theme ); 3851 4594 }); … … 3858 4601 $screenshot.attr( 'src', source ); 3859 4602 } 3860 }); 3861 }, 3862 3863 /** 3864 * Show or hide the theme based on the presence of the term in the title, description, and author. 4603 control.screenshotRendered = true; 4604 }); 4605 }, 4606 4607 /** 4608 * Show or hide the theme based on the presence of the term in the title, description, tags, and author. 3865 4609 * 3866 4610 * @since 4.2.0 4611 * @returns {boolean} Whether a theme control was activated or not. 3867 4612 */ 3868 4613 filter: function( term ) { … … 3875 4620 if ( -1 !== haystack.search( term ) ) { 3876 4621 control.activate(); 4622 return true; 3877 4623 } else { 3878 4624 control.deactivate(); 3879 } 4625 return false; 4626 } 4627 }, 4628 4629 /** 4630 * Rerender the theme from its JS template with the installed type. 4631 * 4632 * @since 4.9.0 4633 * 4634 * @returns {void} 4635 */ 4636 rerenderAsInstalled: function( installed ) { 4637 var control = this, section; 4638 if ( installed ) { 4639 control.params.theme.type = 'installed'; 4640 } else { 4641 section = api.section( control.params.section ); 4642 control.params.theme.type = section.params.action; 4643 } 4644 control.renderContent(); // Replaces existing content. 4645 control.container.trigger( 'render-screenshot' ); 3880 4646 } 3881 4647 }); … … 5281 6047 code_editor: api.CodeEditorControl 5282 6048 }; 5283 api.panelConstructor = {}; 6049 api.panelConstructor = { 6050 themes: api.ThemesPanel 6051 }; 5284 6052 api.sectionConstructor = { 5285 6053 themes: api.ThemesSection, … … 5400 6168 // Sort the sections within each panel 5401 6169 api.panel.each( function ( panel ) { 6170 if ( 'themes' === panel.id ) { 6171 return; // Don't reflow theme sections, as doing so moves them after the themes container. 6172 } 6173 5402 6174 var sections = panel.sections(), 5403 6175 sectionHeadContainers = _.pluck( sections, 'headContainer' ); … … 6224 6996 }; 6225 6997 6226 /** 6227 * Deactivate themes section if changeset status is not auto-draft 6228 */ 6229 api.section( 'themes', function( section ) { 6998 // Deactivate themes panel if changeset status is not auto-draft. 6999 api.panel( 'themes', function( panel ) { 6230 7000 var canActivate; 6231 7001 … … 6234 7004 }; 6235 7005 6236 section.active.validate = canActivate;6237 section.active.set( canActivate() );7006 panel.active.validate = canActivate; 7007 panel.active.set( canActivate() ); 6238 7008 changesetStatus.bind( function() { 6239 section.active.set( canActivate() );7009 panel.active.set( canActivate() ); 6240 7010 } ); 6241 7011 } ); … … 6401 7171 6402 7172 // Keyboard shortcuts - esc to exit section/panel. 6403 $( 'body' ).on( 'keydown', function( event ) {7173 body.on( 'keydown', function( event ) { 6404 7174 var collapsedObject, expandedControls = [], expandedSections = [], expandedPanels = []; 6405 7175 … … 6441 7211 collapsedObject = expandedControls[0] || expandedSections[0] || expandedPanels[0]; 6442 7212 if ( collapsedObject ) { 7213 if ( 'themes' === collapsedObject.params.type ) { 7214 7215 // Themes panel or section. 7216 if ( body.hasClass( 'modal-open' ) ) { 7217 collapsedObject.closeDetails(); 7218 } else { 7219 7220 // If we're collapsing a section, collapse the panel also. 7221 wp.customize.panel( 'themes' ).collapse(); 7222 } 7223 return; 7224 } 6443 7225 collapsedObject.collapse(); 6444 7226 event.preventDefault();
Note: See TracChangeset
for help on using the changeset viewer.