Вход Регистрация
Файл: wordpress/wp-admin/js/customize-controls.js
Строк: 4202
<?php
/* globals _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer */
(function( exports, $ ){
    var 
Containerfocusapi wp.customize;

    
/**
     * @class
     * @augments wp.customize.Value
     * @augments wp.customize.Class
     *
     * @param options
     * - previewer - The Previewer instance to sync with.
     * - transport - The transport to use for previewing. Supports 'refresh' and 'postMessage'.
     */
    
api.Setting api.Value.extend({
        
initialize: function( idvalueoptions ) {
            
api.Value.prototype.initialize.callthisvalueoptions );

            
this.id id;
            
this.transport this.transport || 'refresh';
            
this._dirty options.dirty || false;

            
this.bindthis.preview );
        },
        
preview: function() {
            switch ( 
this.transport ) {
                case 
'refresh':
                    return 
this.previewer.refresh();
                case 
'postMessage':
                    return 
this.previewer.send'setting', [ this.idthis() ] );
            }
        }
    });

    
/**
     * Utility function namespace
     */
    
api.utils = {};

    
/**
     * Watch all changes to Value properties, and bubble changes to parent Values instance
     *
     * @since 4.1.0
     *
     * @param {wp.customize.Class} instance
     * @param {Array}              properties  The names of the Value instances to watch.
     */
    
api.utils.bubbleChildValueChanges = function ( instanceproperties ) {
        $.
eachproperties, function ( ikey ) {
            
instancekey ].bind( function ( tofrom ) {
                if ( 
instance.parent && to !== from ) {
                    
instance.parent.trigger'change'instance );
                }
            } );
        } );
    };

    
/**
     * Expand a panel, section, or control and focus on the first focusable element.
     *
     * @since 4.1.0
     *
     * @param {Object}   [params]
     * @param {Callback} [params.completeCallback]
     */
    
focus = function ( params ) {
        var 
constructcompleteCallbackfocus;
        
construct this;
        
params params || {};
        
focus = function () {
            var 
focusContainer;
            if ( 
construct.extendedapi.Panel ) && construct.expanded() ) {
                
focusContainer construct.container.find'.control-panel-content:first' );
            } else {
                
focusContainer construct.container;
            }
            
focusContainer.find':focusable:first' ).focus();
            
focusContainer[0].scrollIntoViewtrue );
        };
        if ( 
params.completeCallback ) {
            
completeCallback params.completeCallback;
            
params.completeCallback = function () {
                
focus();
                
completeCallback();
            };
        } else {
            
params.completeCallback focus;
        }
        if ( 
construct.expand ) {
            
construct.expandparams );
        } else {
            
params.completeCallback();
        }
    };

    
/**
     * Stable sort for Panels, Sections, and Controls.
     *
     * If a.priority() === b.priority(), then sort by their respective params.instanceNumber.
     *
     * @since 4.1.0
     *
     * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} a
     * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} b
     * @returns {Number}
     */
    
api.utils.prioritySort = function ( a) {
        if ( 
a.priority() === b.priority() && typeof a.params.instanceNumber === 'number' && typeof b.params.instanceNumber === 'number' ) {
            return 
a.params.instanceNumber b.params.instanceNumber;
        } else {
            return 
a.priority() - b.priority();
        }
    };

    
/**
     * Return whether the supplied Event object is for a keydown event but not the Enter key.
     *
     * @since 4.1.0
     *
     * @param {jQuery.Event} event
     * @returns {boolean}
     */
    
api.utils.isKeydownButNotEnterEvent = function ( event ) {
        return ( 
'keydown' === event.type && 13 !== event.which );
    };

    
/**
     * Return whether the two lists of elements are the same and are in the same order.
     *
     * @since 4.1.0
     *
     * @param {Array|jQuery} listA
     * @param {Array|jQuery} listB
     * @returns {boolean}
     */
    
api.utils.areElementListsEqual = function ( listAlistB ) {
        var 
equal = (
            
listA.length === listB.length && // if lists are different lengths, then naturally they are not equal
            
-=== _.indexOf_.map// are there any false values in the list returned by map?
                
_.ziplistAlistB ), // pair up each element between the two lists
                
function ( pair ) {
                    return $( 
pair[0] ).ispair[1] ); // compare to see if each pair are equal
                
}
            ), 
false // check for presence of false in map's return value
        
);
        return 
equal;
    };

    
/**
     * Base class for Panel and Section.
     *
     * @since 4.1.0
     *
     * @class
     * @augments wp.customize.Class
     */
    
Container api.Class.extend({
        
defaultActiveArguments: { duration'fast'completeCallback: $.noop },
        
defaultExpandedArguments: { duration'fast'completeCallback: $.noop },

        
/**
         * @since 4.1.0
         *
         * @param {String} id
         * @param {Object} options
         */
        
initialize: function ( idoptions ) {
            var 
container this;
            
container.id id;
            
container.params = {};
            $.
extendcontaineroptions || {} );
            
container.container = $( container.params.content );

            
container.deferred = {
                
embedded: new $.Deferred()
            };
            
container.priority = new api.Value();
            
container.active = new api.Value();
            
container.activeArgumentsQueue = [];
            
container.expanded = new api.Value();
            
container.expandedArgumentsQueue = [];

            
container.active.bind( function ( active ) {
                var 
args container.activeArgumentsQueue.shift();
                
args = $.extend( {}, container.defaultActiveArgumentsargs );
                
active = ( active && container.isContextuallyActive() );
                
container.onChangeActiveactiveargs );
            });
            
container.expanded.bind( function ( expanded ) {
                var 
args container.expandedArgumentsQueue.shift();
                
args = $.extend( {}, container.defaultExpandedArgumentsargs );
                
container.onChangeExpandedexpandedargs );
            });

            
container.attachEvents();

            
api.utils.bubbleChildValueChangescontainer, [ 'priority''active' ] );

            
container.priority.setisNaNcontainer.params.priority ) ? 100 container.params.priority );
            
container.active.setcontainer.params.active );
            
container.expanded.setfalse );
        },

        
/**
         * @since 4.1.0
         *
         * @abstract
         */
        
ready: function() {},

        
/**
         * Get the child models associated with this parent, sorting them by their priority Value.
         *
         * @since 4.1.0
         *
         * @param {String} parentType
         * @param {String} childType
         * @returns {Array}
         */
        
_children: function ( parentTypechildType ) {
            var 
parent this,
                
children = [];
            
apichildType ].each( function ( child ) {
                if ( 
childparentType ].get() === parent.id ) {
                    
children.pushchild );
                }
            } );
            
children.sortapi.utils.prioritySort );
            return 
children;
        },

        
/**
         * To override by subclass, to return whether the container has active children.
         *
         * @since 4.1.0
         *
         * @abstract
         */
        
isContextuallyActive: function () {
            throw new 
Error'Container.isContextuallyActive() must be overridden in a subclass.' );
        },

        
/**
         * Handle changes to the active state.
         *
         * This does not change the active state, it merely handles the behavior
         * for when it does change.
         *
         * To override by subclass, update the container's UI to reflect the provided active state.
         *
         * @since 4.1.0
         *
         * @param {Boolean} active
         * @param {Object}  args
         * @param {Object}  args.duration
         * @param {Object}  args.completeCallback
         */
        
onChangeActive: function ( activeargs ) {
            var 
duration = ( 'resolved' === api.previewer.deferred.active.state() ? args.duration );
            if ( ! $.
containsdocumentthis.container ) ) {
                
// jQuery.fn.slideUp is not hiding an element if it is not in the DOM
                
this.container.toggleactive );
                if ( 
args.completeCallback ) {
                    
args.completeCallback();
                }
            } else if ( 
active ) {
                
this.container.stoptruetrue ).slideDowndurationargs.completeCallback );
            } else {
                
this.container.stoptruetrue ).slideUpdurationargs.completeCallback );
            }
        },

        
/**
         * @since 4.1.0
         *
         * @params {Boolean} active
         * @param {Object}   [params]
         * @returns {Boolean} false if state already applied
         */
        
_toggleActive: function ( activeparams ) {
            var 
self this;
            
params params || {};
            if ( ( 
active && this.active.get() ) || ( ! active && ! this.active.get() ) ) {
                
params.unchanged true;
                
self.onChangeActiveself.active.get(), params );
                return 
false;
            } else {
                
params.unchanged false;
                
this.activeArgumentsQueue.pushparams );
                
this.active.setactive );
                return 
true;
            }
        },

        
/**
         * @param {Object} [params]
         * @returns {Boolean} false if already active
         */
        
activate: function ( params ) {
            return 
this._toggleActivetrueparams );
        },

        
/**
         * @param {Object} [params]
         * @returns {Boolean} false if already inactive
         */
        
deactivate: function ( params ) {
            return 
this._toggleActivefalseparams );
        },

        
/**
         * To override by subclass, update the container's UI to reflect the provided active state.
         * @abstract
         */
        
onChangeExpanded: function () {
            throw new 
Error'Must override with subclass.' );
        },

        
/**
         * @param {Boolean} expanded
         * @param {Object} [params]
         * @returns {Boolean} false if state already applied
         */
        
_toggleExpanded: function ( expandedparams ) {
            var 
self this;
            
params params || {};
            var 
section thispreviousCompleteCallback params.completeCallback;
            
params.completeCallback = function () {
                if ( 
previousCompleteCallback ) {
                    
previousCompleteCallback.applysectionarguments );
                }
                if ( 
expanded ) {
                    
section.container.trigger'expanded' );
                } else {
                    
section.container.trigger'collapsed' );
                }
            };
            if ( ( 
expanded && this.expanded.get() ) || ( ! expanded && ! this.expanded.get() ) ) {
                
params.unchanged true;
                
self.onChangeExpandedself.expanded.get(), params );
                return 
false;
            } else {
                
params.unchanged false;
                
this.expandedArgumentsQueue.pushparams );
                
this.expanded.setexpanded );
                return 
true;
            }
        },

        
/**
         * @param {Object} [params]
         * @returns {Boolean} false if already expanded
         */
        
expand: function ( params ) {
            return 
this._toggleExpandedtrueparams );
        },

        
/**
         * @param {Object} [params]
         * @returns {Boolean} false if already collapsed
         */
        
collapse: function ( params ) {
            return 
this._toggleExpandedfalseparams );
        },

        
/**
         * Bring the container into view and then expand this and bring it into view
         * @param {Object} [params]
         */
        
focusfocus
    
});

    
/**
     * @since 4.1.0
     *
     * @class
     * @augments wp.customize.Class
     */
    
api.Section Container.extend({

        
/**
         * @since 4.1.0
         *
         * @param {String} id
         * @param {Array}  options
         */
        
initialize: function ( idoptions ) {
            var 
section this;
            
Container.prototype.initialize.callsectionidoptions );

            
section.id id;
            
section.panel = new api.Value();
            
section.panel.bind( function ( id ) {
                $( 
section.container ).toggleClass'control-subsection', !! id );
            });
            
section.panel.setsection.params.panel || '' );
            
api.utils.bubbleChildValueChangessection, [ 'panel' ] );

            
section.embed();
            
section.deferred.embedded.done( function () {
                
section.ready();
            });
        },

        
/**
         * Embed the container in the DOM when any parent panel is ready.
         *
         * @since 4.1.0
         */
        
embed: function () {
            var 
section thisinject;

            
// Watch for changes to the panel state
            
inject = function ( panelId ) {
                var 
parentContainer;
                if ( 
panelId ) {
                    
// The panel has been supplied, so wait until the panel object is registered
                    
api.panelpanelId, function ( panel ) {
                        
// The panel has been registered, wait for it to become ready/initialized
                        
panel.deferred.embedded.done( function () {
                            
parentContainer panel.container.find'ul:first' );
                            if ( ! 
section.container.parent().isparentContainer ) ) {
                                
parentContainer.appendsection.container );
                            }
                            
section.deferred.embedded.resolve();
                        });
                    } );
                } else {
                    
// There is no panel, so embed the section in the root of the customizer
                    
parentContainer = $( '#customize-theme-controls' ).children'ul' ); // @todo This should be defined elsewhere, and to be configurable
                    
if ( ! section.container.parent().isparentContainer ) ) {
                        
parentContainer.appendsection.container );
                    }
                    
section.deferred.embedded.resolve();
                }
            };
            
section.panel.bindinject );
            
injectsection.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one
        
},

        
/**
         * Add behaviors for the accordion section.
         *
         * @since 4.1.0
         */
        
attachEvents: function () {
            var 
section this;

            
// Expand/Collapse accordion sections on click.
            
section.container.find'.accordion-section-title' ).on'click keydown', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }
                
event.preventDefault(); // Keep this AFTER the key filter above

                
if ( section.expanded() ) {
                    
section.collapse();
                } else {
                    
section.expand();
                }
            });
        },

        
/**
         * Return whether this section has any active controls.
         *
         * @since 4.1.0
         *
         * @returns {Boolean}
         */
        
isContextuallyActive: function () {
            var 
section this,
                
controls section.controls(),
                
activeCount 0;
            
_controls ).each( function ( control ) {
                if ( 
control.active() ) {
                    
activeCount += 1;
                }
            } );
            return ( 
activeCount !== );
        },

        
/**
         * Get the controls that are associated with this section, sorted by their priority Value.
         *
         * @since 4.1.0
         *
         * @returns {Array}
         */
        
controls: function () {
            return 
this._children'section''control' );
        },

        
/**
         * Update UI to reflect expanded state.
         *
         * @since 4.1.0
         *
         * @param {Boolean} expanded
         * @param {Object}  args
         */
        
onChangeExpanded: function ( expandedargs ) {
            var 
section this,
                
content section.container.find'.accordion-section-content' ),
                
expand;

            if ( 
expanded ) {

                if ( 
args.unchanged ) {
                    
expand args.completeCallback;
                } else {
                    
expand = function () {
                        
content.stop().slideDownargs.durationargs.completeCallback );
                        
section.container.addClass'open' );
                    };
                }

                if ( ! 
args.allowMultiple ) {
                    
api.section.each( function ( otherSection ) {
                        if ( 
otherSection !== section ) {
                            
otherSection.collapse( { durationargs.duration } );
                        }
                    });
                }

                if ( 
section.panel() ) {
                    
api.panelsection.panel() ).expand({
                        
durationargs.duration,
                        
completeCallbackexpand
                    
});
                } else {
                    
expand();
                }

            } else {
                
section.container.removeClass'open' );
                
content.slideUpargs.durationargs.completeCallback );
            }
        }
    });

    
/**
     * wp.customize.ThemesSection
     *
     * Custom section for themes that functions similarly to a backwards panel,
     * and also handles the theme-details view rendering and navigation.
     *
     * @constructor
     * @augments wp.customize.Section
     * @augments wp.customize.Container
     */
    
api.ThemesSection api.Section.extend({
        
currentTheme'',
        
overlay'',
        
template'',
        
screenshotQueuenull,
        
$window: $( window ),

        
/**
         * @since 4.2.0
         */
        
initialize: function () {
            
this.$customizeSidebar = $( '.wp-full-overlay-sidebar-content:first' );
            return 
api.Section.prototype.initialize.applythisarguments );
        },

        
/**
         * @since 4.2.0
         */
        
ready: function () {
            var 
section this;
            
section.overlay section.container.find'.theme-overlay' );
            
section.template wp.template'customize-themes-details-view' );

            
// Bind global keyboard events.
            
$( 'body' ).on'keyup', function( event ) {
                if ( ! 
section.overlay.find'.theme-wrap' ).is':visible' ) ) {
                    return;
                }

                
// Pressing the right arrow key fires a theme:next event
                
if ( 39 === event.keyCode ) {
                    
section.nextTheme();
                }

                
// Pressing the left arrow key fires a theme:previous event
                
if ( 37 === event.keyCode ) {
                    
section.previousTheme();
                }

                
// Pressing the escape key fires a theme:collapse event
                
if ( 27 === event.keyCode ) {
                    
section.closeDetails();
                }
            });

            
_.bindAllthis'renderScreenshots' );
        },

        
/**
         * Override Section.isContextuallyActive method.
         *
         * Ignore the active states' of the contained theme controls, and just
         * use the section's own active state instead. This ensures empty search
         * results for themes to cause the section to become inactive.
         *
         * @since 4.2.0
         *
         * @returns {Boolean}
         */
        
isContextuallyActive: function () {
            return 
this.active();
        },

        
/**
         * @since 4.2.0
         */
        
attachEvents: function () {
            var 
section this;

            
// Expand/Collapse section/panel.
            
section.container.find'.change-theme, .customize-theme' ).on'click keydown', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }
                
event.preventDefault(); // Keep this AFTER the key filter above

                
if ( section.expanded() ) {
                    
section.collapse();
                } else {
                    
section.expand();
                }
            });

            
// Theme navigation in details view.
            
section.container.on'click keydown''.left', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }

                
event.preventDefault(); // Keep this AFTER the key filter above

                
section.previousTheme();
            });

            
section.container.on'click keydown''.right', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }

                
event.preventDefault(); // Keep this AFTER the key filter above

                
section.nextTheme();
            });

            
section.container.on'click keydown''.theme-backdrop, .close', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }

                
event.preventDefault(); // Keep this AFTER the key filter above

                
section.closeDetails();
            });

            var 
renderScreenshots _.throttle_.bindsection.renderScreenshotsthis ), 100 );
            
section.container.on'input''#themes-filter', function( event ) {
                var 
count,
                    
term event.currentTarget.value.toLowerCase().trim().replace'-'' ' ),
                    
controls section.controls();

                
_.eachcontrols, function( control ) {
                    
control.filterterm );
                });

                
renderScreenshots();

                
// Update theme count.
                
count section.container.find'li.customize-control:visible' ).length;
                
section.container.find'.theme-count' ).textcount );
            });

            
// Pre-load the first 3 theme screenshots.
            
api.bind'ready', function () {
                
_.eachsection.controls().slice0), function ( control ) {
                    var 
imgsrc control.params.theme.screenshot[0];
                    if ( 
src ) {
                        
img = new Image();
                        
img.src src;
                    }
                });
            });
        },

        
/**
         * Update UI to reflect expanded state
         *
         * @since 4.2.0
         *
         * @param {Boolean}  expanded
         * @param {Object}   args
         * @param {Boolean}  args.unchanged
         * @param {Callback} args.completeCallback
         */
        
onChangeExpanded: function ( expandedargs ) {

            
// Immediately call the complete callback if there were no changes
            
if ( args.unchanged ) {
                if ( 
args.completeCallback ) {
                    
args.completeCallback();
                }
                return;
            }

            
// Note: there is a second argument 'args' passed
            
var positionscroll,
                
panel this,
                
section panel.container.closest'.accordion-section' ),
                
overlay section.closest'.wp-full-overlay' ),
                
container section.closest'.wp-full-overlay-sidebar-content' ),
                
siblings container.find'.open' ),
                
topPanel overlay.find'#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add'#customize-info > .accordion-section-title' ),
                
customizeBtn section.find'.customize-theme' ),
                
changeBtn section.find'.change-theme' ),
                
content section.find'.control-panel-content' );

            if ( 
expanded ) {

                
// Collapse any sibling sections/panels
                
api.section.each( function ( otherSection ) {
                    if ( 
otherSection !== panel ) {
                        
otherSection.collapse( { durationargs.duration } );
                    }
                });
                
api.panel.each( function ( otherPanel ) {
                    
otherPanel.collapse( { duration} );
                });

                
content.show0, function() {
                    
position content.offset().top;
                    
scroll container.scrollTop();
                    
content.css'margin-top', ( $( '#customize-header-actions' ).height() - position scroll ) );
                    
section.addClass'current-panel' );
                    
overlay.addClass'in-themes-panel' );
                    
container.scrollTop);
                    
_.delaypanel.renderScreenshots10 ); // Wait for the controls
                    
panel.$customizeSidebar.on'scroll.customize-themes-section'_.throttlepanel.renderScreenshots300 ) );
                    if ( 
args.completeCallback ) {
                        
args.completeCallback();
                    }
                } );
                
topPanel.attr'tabindex''-1' );
                
changeBtn.attr'tabindex''-1' );
                
customizeBtn.focus();
            } else {
                
siblings.removeClass'open' );
                
section.removeClass'current-panel' );
                
overlay.removeClass'in-themes-panel' );
                
panel.$customizeSidebar.off'scroll.customize-themes-section' );
                
content.delay180 ).hide0, function() {
                    
content.css'margin-top''inherit' ); // Reset
                    
if ( args.completeCallback ) {
                        
args.completeCallback();
                    }
                } );
                
topPanel.attr'tabindex''0' );
                
customizeBtn.attr'tabindex''0' );
                
changeBtn.focus();
                
container.scrollTop);
            }
        },

        
/**
         * Render control's screenshot if the control comes into view.
         *
         * @since 4.2.0
         */
        
renderScreenshots: function( ) {
            var 
section this;

            
// Fill queue initially.
            
if ( section.screenshotQueue === null ) {
                
section.screenshotQueue section.controls();
            }

            
// Are all screenshots rendered?
            
if ( ! section.screenshotQueue.length ) {
                return;
            }

            
section.screenshotQueue _.filtersection.screenshotQueue, function( control ) {
                var 
$imageWrapper control.container.find'.theme-screenshot' ),
                    
$image $imageWrapper.find'img' );

                if ( ! 
$image.length ) {
                    return 
false;
                }

                if ( 
$image.is':hidden' ) ) {
                    return 
true;
                }

                
// Based on unveil.js.
                
var wt section.$window.scrollTop(),
                    
wb wt section.$window.height(),
                    
et $image.offset().top,
                    
ih $imageWrapper.height(),
                    
eb et ih,
                    
threshold ih 3,
                    
inView eb >= wt threshold && et <= wb threshold;

                if ( 
inView ) {
                    
control.container.trigger'render-screenshot' );
                }

                
// If the image is in view return false so it's cleared from the queue.
                
return ! inView;
            } );
        },

        
/**
         * Advance the modal to the next theme.
         *
         * @since 4.2.0
         */
        
nextTheme: function () {
            var 
section this;
            if ( 
section.getNextTheme() ) {
                
section.showDetailssection.getNextTheme(), function() {
                    
section.overlay.find'.right' ).focus();
                } );
            }
        },

        
/**
         * Get the next theme model.
         *
         * @since 4.2.0
         */
        
getNextTheme: function () {
            var 
controlnext;
            
control api.control'theme_' this.currentTheme );
            
next control.container.next'li.customize-control-theme' );
            if ( ! 
next.length ) {
                return 
false;
            }
            
next next[0].id.replace'customize-control-''' );
            
control api.controlnext );

            return 
control.params.theme;
        },

        
/**
         * Advance the modal to the previous theme.
         *
         * @since 4.2.0
         */
        
previousTheme: function () {
            var 
section this;
            if ( 
section.getPreviousTheme() ) {
                
section.showDetailssection.getPreviousTheme(), function() {
                    
section.overlay.find'.left' ).focus();
                } );
            }
        },

        
/**
         * Get the previous theme model.
         *
         * @since 4.2.0
         */
        
getPreviousTheme: function () {
            var 
controlprevious;
            
control api.control'theme_' this.currentTheme );
            
previous control.container.prev'li.customize-control-theme' );
            if ( ! 
previous.length ) {
                return 
false;
            }
            
previous previous[0].id.replace'customize-control-''' );
            
control api.controlprevious );

            return 
control.params.theme;
        },

        
/**
         * Disable buttons when we're viewing the first or last theme.
         *
         * @since 4.2.0
         */
        
updateLimits: function () {
            if ( ! 
this.getNextTheme() ) {
                
this.overlay.find'.right' ).addClass'disabled' );
            }
            if ( ! 
this.getPreviousTheme() ) {
                
this.overlay.find'.left' ).addClass'disabled' );
            }
        },

        
/**
         * Render & show the theme details for a given theme model.
         *
         * @since 4.2.0
         *
         * @param {Object}   theme
         */
        
showDetails: function ( themecallback ) {
            var 
section this;
            
callback callback || function(){};
            
section.currentTheme theme.id;
            
section.overlay.htmlsection.templatetheme ) )
                .
fadeIn'fast' )
                .
focus();
            $( 
'body' ).addClass'modal-open' );
            
section.containFocussection.overlay );
            
section.updateLimits();
            
callback();
        },

        
/**
         * Close the theme details modal.
         *
         * @since 4.2.0
         */
        
closeDetails: function () {
            $( 
'body' ).removeClass'modal-open' );
            
this.overlay.fadeOut'fast' );
            
api.control'theme_' this.currentTheme ).focus();
        },

        
/**
         * Keep tab focus within the theme details modal.
         *
         * @since 4.2.0
         */
        
containFocus: function( el ) {
            var 
tabbables;

            
el.on'keydown', function( event ) {

                
// Return if it's not the tab key
                // When navigating with prev/next focus is already handled
                
if ( !== event.keyCode ) {
                    return;
                }

                
// uses jQuery UI to get the tabbable elements
                
tabbables = $( ':tabbable'el );

                
// Keep focus within the overlay
                
if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
                    
tabbables.first().focus();
                    return 
false;
                } else if ( 
tabbables.first()[0] === event.target && event.shiftKey ) {
                    
tabbables.last().focus();
                    return 
false;
                }
            });
        }
    });

    
/**
     * @since 4.1.0
     *
     * @class
     * @augments wp.customize.Class
     */
    
api.Panel Container.extend({
        
/**
         * @since 4.1.0
         *
         * @param  {String} id
         * @param  {Object} options
         */
        
initialize: function ( idoptions ) {
            var 
panel this;
            
Container.prototype.initialize.callpanelidoptions );
            
panel.embed();
            
panel.deferred.embedded.done( function () {
                
panel.ready();
            });
        },

        
/**
         * Embed the container in the DOM when any parent panel is ready.
         *
         * @since 4.1.0
         */
        
embed: function () {
            var 
panel this,
                
parentContainer = $( '#customize-theme-controls > ul' ); // @todo This should be defined elsewhere, and to be configurable

            
if ( ! panel.container.parent().isparentContainer ) ) {
                
parentContainer.appendpanel.container );
            }
            
panel.deferred.embedded.resolve();
        },

        
/**
         * @since 4.1.0
         */
        
attachEvents: function () {
            var 
metapanel this;

            
// Expand/Collapse accordion sections on click.
            
panel.container.find'.accordion-section-title' ).on'click keydown', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }
                
event.preventDefault(); // Keep this AFTER the key filter above

                
if ( ! panel.expanded() ) {
                    
panel.expand();
                }
            });

            
meta panel.container.find'.panel-meta:first' );

            
meta.find'> .accordion-section-title' ).on'click keydown', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }
                
event.preventDefault(); // Keep this AFTER the key filter above

                
if ( meta.hasClass'cannot-expand' ) ) {
                    return;
                }

                var 
content meta.find'.accordion-section-content:first' );
                if ( 
meta.hasClass'open' ) ) {
                    
meta.toggleClass'open' );
                    
content.slideUppanel.defaultExpandedArguments.duration );
                } else {
                    
content.slideDownpanel.defaultExpandedArguments.duration );
                    
meta.toggleClass'open' );
                }
            });

        },

        
/**
         * Get the sections that are associated with this panel, sorted by their priority Value.
         *
         * @since 4.1.0
         *
         * @returns {Array}
         */
        
sections: function () {
            return 
this._children'panel''section' );
        },

        
/**
         * Return whether this panel has any active sections.
         *
         * @since 4.1.0
         *
         * @returns {boolean}
         */
        
isContextuallyActive: function () {
            var 
panel this,
                
sections panel.sections(),
                
activeCount 0;
            
_sections ).each( function ( section ) {
                if ( 
section.active() && section.isContextuallyActive() ) {
                    
activeCount += 1;
                }
            } );
            return ( 
activeCount !== );
        },

        
/**
         * Update UI to reflect expanded state
         *
         * @since 4.1.0
         *
         * @param {Boolean}  expanded
         * @param {Object}   args
         * @param {Boolean}  args.unchanged
         * @param {Callback} args.completeCallback
         */
        
onChangeExpanded: function ( expandedargs ) {

            
// Immediately call the complete callback if there were no changes
            
if ( args.unchanged ) {
                if ( 
args.completeCallback ) {
                    
args.completeCallback();
                }
                return;
            }

            
// Note: there is a second argument 'args' passed
            
var positionscroll,
                
panel this,
                
section panel.container.closest'.accordion-section' ),
                
overlay section.closest'.wp-full-overlay' ),
                
container section.closest'.wp-full-overlay-sidebar-content' ),
                
siblings container.find'.open' ),
                
topPanel overlay.find'#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add'#customize-info > .accordion-section-title' ),
                
backBtn overlay.find'.control-panel-back' ),
                
panelTitle section.find'.accordion-section-title' ).first(),
                
content section.find'.control-panel-content' );

            if ( 
expanded ) {

                
// Collapse any sibling sections/panels
                
api.section.each( function ( section ) {
                    if ( ! 
section.panel() ) {
                        
section.collapse( { duration} );
                    }
                });
                
api.panel.each( function ( otherPanel ) {
                    if ( 
panel !== otherPanel ) {
                        
otherPanel.collapse( { duration} );
                    }
                });

                
content.show0, function() {
                    
content.parent().show();
                    
position content.offset().top;
                    
scroll container.scrollTop();
                    
content.css'margin-top', ( $( '#customize-header-actions' ).height() - position scroll ) );
                    
section.addClass'current-panel' );
                    
overlay.addClass'in-sub-panel' );
                    
container.scrollTop);
                    if ( 
args.completeCallback ) {
                        
args.completeCallback();
                    }
                } );
                
topPanel.attr'tabindex''-1' );
                
backBtn.attr'tabindex''0' );
                
backBtn.focus();
            } else {
                
siblings.removeClass'open' );
                
section.removeClass'current-panel' );
                
overlay.removeClass'in-sub-panel' );
                
content.delay180 ).hide0, function() {
                    
content.css'margin-top''inherit' ); // Reset
                    
if ( args.completeCallback ) {
                        
args.completeCallback();
                    }
                } );
                
topPanel.attr'tabindex''0' );
                
backBtn.attr'tabindex''-1' );
                
panelTitle.focus();
                
container.scrollTop);
            }
        }
    });

    
/**
     * A Customizer Control.
     *
     * A control provides a UI element that allows a user to modify a Customizer Setting.
     *
     * @see PHP class WP_Customize_Control.
     *
     * @class
     * @augments wp.customize.Class
     *
     * @param {string} id                            Unique identifier for the control instance.
     * @param {object} options                       Options hash for the control instance.
     * @param {object} options.params
     * @param {object} options.params.type           Type of control (e.g. text, radio, dropdown-pages, etc.)
     * @param {string} options.params.content        The HTML content for the control.
     * @param {string} options.params.priority       Order of priority to show the control within the section.
     * @param {string} options.params.active
     * @param {string} options.params.section
     * @param {string} options.params.label
     * @param {string} options.params.description
     * @param {string} options.params.instanceNumber Order in which this instance was created in relation to other instances.
     */
    
api.Control api.Class.extend({
        
defaultActiveArguments: { duration'fast'completeCallback: $.noop },

        
initialize: function( idoptions ) {
            var 
control this,
                
nodesradiossettings;

            
control.params = {};
            $.
extendcontroloptions || {} );
            
control.id id;
            
control.selector '#customize-control-' id.replace( /]/g'' ).replace( /[/g'-' );
            
control.templateSelector 'customize-control-' control.params.type '-content';
            
control.container control.params.content ? $( control.params.content ) : $( control.selector );

            
control.deferred = {
                
embedded: new $.Deferred()
            };
            
control.section = new api.Value();
            
control.priority = new api.Value();
            
control.active = new api.Value();
            
control.activeArgumentsQueue = [];

            
control.elements = [];

            
nodes  control.container.find('[data-customize-setting-link]');
            
radios = {};

            
nodes.each( function() {
                var 
node = $( this ),
                    
name;

                if ( 
node.is':radio' ) ) {
                    
name node.prop'name' );
                    if ( 
radiosname ] ) {
                        return;
                    }

                    
radiosname ] = true;
                    
node nodes.filter'[name="' name '"]' );
                }

                
apinode.data'customizeSettingLink' ), function( setting ) {
                    var 
element = new api.Elementnode );
                    
control.elements.pushelement );
                    
element.syncsetting );
                    
element.setsetting() );
                });
            });

            
control.active.bind( function ( active ) {
                var 
args control.activeArgumentsQueue.shift();
                
args = $.extend( {}, control.defaultActiveArgumentsargs );
                
control.onChangeActiveactiveargs );
            } );

            
control.section.setcontrol.params.section );
            
control.priority.setisNaNcontrol.params.priority ) ? 10 control.params.priority );
            
control.active.setcontrol.params.active );

            
api.utils.bubbleChildValueChangescontrol, [ 'section''priority''active' ] );

            
// Associate this control with its settings when they are created
            
settings = $.mapcontrol.params.settings, function( value ) {
                return 
value;
            });
            
api.applyapisettings.concat( function () {
                var 
key;

                
control.settings = {};
                for ( 
key in control.params.settings ) {
                    
control.settingskey ] = apicontrol.params.settingskey ] );
                }

                
control.setting control.settings['default'] || null;

                
control.embed();
            }) );

            
control.deferred.embedded.done( function () {
                
control.ready();
            });
        },

        
/**
         * Embed the control into the page.
         */
        
embed: function () {
            var 
control this,
                
inject;

            
// Watch for changes to the section state
            
inject = function ( sectionId ) {
                var 
parentContainer;
                if ( ! 
sectionId ) { // @todo allow a control to be embedded without a section, for instance a control embedded in the frontend
                    
return;
                }
                
// Wait for the section to be registered
                
api.sectionsectionId, function ( section ) {
                    
// Wait for the section to be ready/initialized
                    
section.deferred.embedded.done( function () {
                        
parentContainer section.container.find'ul:first' );
                        if ( ! 
control.container.parent().isparentContainer ) ) {
                            
parentContainer.appendcontrol.container );
                            
control.renderContent();
                        }
                        
control.deferred.embedded.resolve();
                    });
                });
            };
            
control.section.bindinject );
            
injectcontrol.section.get() );
        },

        
/**
         * Triggered when the control's markup has been injected into the DOM.
         *
         * @abstract
         */
        
ready: function() {},

        
/**
         * Normal controls do not expand, so just expand its parent
         *
         * @param {Object} [params]
         */
        
expand: function ( params ) {
            
api.sectionthis.section() ).expandparams );
        },

        
/**
         * Bring the containing section and panel into view and then
         * this control into view, focusing on the first input.
         */
        
focusfocus,

        
/**
         * Update UI in response to a change in the control's active state.
         * This does not change the active state, it merely handles the behavior
         * for when it does change.
         *
         * @since 4.1.0
         *
         * @param {Boolean}  active
         * @param {Object}   args
         * @param {Number}   args.duration
         * @param {Callback} args.completeCallback
         */
        
onChangeActive: function ( activeargs ) {
            if ( ! $.
containsdocumentthis.container ) ) {
                
// jQuery.fn.slideUp is not hiding an element if it is not in the DOM
                
this.container.toggleactive );
                if ( 
args.completeCallback ) {
                    
args.completeCallback();
                }
            } else if ( 
active ) {
                
this.container.slideDownargs.durationargs.completeCallback );
            } else {
                
this.container.slideUpargs.durationargs.completeCallback );
            }
        },

        
/**
         * @deprecated 4.1.0 Use this.onChangeActive() instead.
         */
        
toggle: function ( active ) {
            return 
this.onChangeActiveactivethis.defaultActiveArguments );
        },

        
/**
         * Shorthand way to enable the active state.
         *
         * @since 4.1.0
         *
         * @param {Object} [params]
         * @returns {Boolean} false if already active
         */
        
activateContainer.prototype.activate,

        
/**
         * Shorthand way to disable the active state.
         *
         * @since 4.1.0
         *
         * @param {Object} [params]
         * @returns {Boolean} false if already inactive
         */
        
deactivateContainer.prototype.deactivate,

        
/**
         * Re-use _toggleActive from Container class.
         *
         * @access private
         */
        
_toggleActiveContainer.prototype._toggleActive,

        
dropdownInit: function() {
            var 
control      this,
                
statuses     this.container.find('.dropdown-status'),
                
params       this.params,
                
toggleFreeze false,
                
update       = function( to ) {
                    if ( 
typeof to === 'string' && params.statuses && params.statusesto ] )
                        
statuses.htmlparams.statusesto ] ).show();
                    else
                        
statuses.hide();
                };

            
// Support the .dropdown class to open/close complex elements
            
this.container.on'click keydown''.dropdown', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }

                
event.preventDefault();

                if (!
toggleFreeze)
                    
control.container.toggleClass('open');

                if ( 
control.container.hasClass('open') )
                    
control.container.parent().parent().find('li.library-selected').focus();

                
// Don't want to fire focus and click at same time
                
toggleFreeze true;
                
setTimeout(function () {
                    
toggleFreeze false;
                }, 
400);
            });

            
this.setting.bindupdate );
            
updatethis.setting() );
        },

        
/**
         * Render the control from its JS template, if it exists.
         *
         * The control's container must already exist in the DOM.
         *
         * @since 4.1.0
         */
        
renderContent: function () {
            var 
template,
                
control this;

            
// Replace the container element's content with the control.
            
if ( !== $( '#tmpl-' control.templateSelector ).length ) {
                
template wp.templatecontrol.templateSelector );
                if ( 
template && control.container ) {
                    
control.container.htmltemplatecontrol.params ) );
                }
            }
        }
    });

    
/**
     * A colorpicker control.
     *
     * @class
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    
api.ColorControl api.Control.extend({
        
ready: function() {
            var 
control this,
                
picker this.container.find('.color-picker-hex');

            
picker.valcontrol.setting() ).wpColorPicker({
                
change: function() {
                    
control.setting.setpicker.wpColorPicker('color') );
                },
                
clear: function() {
                    
control.setting.setfalse );
                }
            });

            
this.setting.bind( function ( value ) {
                
picker.valvalue );
                
picker.wpColorPicker'color'value );
            });
        }
    });

    
/**
     * A control that implements the media modal.
     *
     * @class
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    
api.MediaControl api.Control.extend({

        
/**
         * When the control's DOM structure is ready,
         * set up internal event bindings.
         */
        
ready: function() {
            var 
control this;
            
// Shortcut so that we don't have to use _.bind every time we add a callback.
            
_.bindAllcontrol'restoreDefault''removeFile''openFrame''select''pausePlayer' );

            
// Bind events, with delegation to facilitate re-rendering.
            
control.container.on'click keydown''.upload-button'control.openFrame );
            
control.container.on'click keydown''.upload-button'control.pausePlayer );
            
control.container.on'click keydown''.thumbnail-image img'control.openFrame );
            
control.container.on'click keydown''.default-button'control.restoreDefault );
            
control.container.on'click keydown''.remove-button'control.pausePlayer );
            
control.container.on'click keydown''.remove-button'control.removeFile );
            
control.container.on'click keydown''.remove-button'control.cleanupPlayer );

            
// Resize the player controls when it becomes visible (ie when section is expanded)
            
api.sectioncontrol.section() ).container
                
.on'expanded', function() {
                    if ( 
control.player ) {
                        
control.player.setControlsSize();
                    }
                })
                .
on'collapsed', function() {
                    
control.pausePlayer();
                });

            
// Re-render whenever the control's setting changes.
            
control.setting.bind( function () { control.renderContent(); } );
        },

        
pausePlayer: function () {
            
this.player && this.player.pause();
        },

        
cleanupPlayer: function () {
            
this.player && wp.media.mixin.removePlayerthis.player );
        },

        
/**
         * Open the media modal.
         */
        
openFrame: function( event ) {
            if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                return;
            }

            
event.preventDefault();

            if ( ! 
this.frame ) {
                
this.initFrame();
            }

            
this.frame.open();
        },

        
/**
         * Create a media modal select frame, and store it so the instance can be reused when needed.
         */
        
initFrame: function() {
            
this.frame wp.media({
                
button: {
                    
textthis.params.button_labels.frame_button
                
},
                
states: [
                    new 
wp.media.controller.Library({
                        
title:     this.params.button_labels.frame_title,
                        
library:   wp.media.query({ typethis.params.mime_type }),
                        
multiple:  false,
                        
date:      false
                    
})
                ]
            });

            
// When a file is selected, run a callback.
            
this.frame.on'select'this.select );
        },

        
/**
         * Callback handler for when an attachment is selected in the media modal.
         * Gets the selected image information, and sets it within the control.
         */
        
select: function() {
            
// Get the attachment from the modal frame.
            
var node,
                
attachment this.frame.state().get'selection' ).first().toJSON(),
                
mejsSettings window._wpmejsSettings || {};

            
this.params.attachment attachment;

            
// Set the Customizer setting; the callback takes care of rendering.
            
this.settingattachment.id );
            
node this.container.find'audio, video' ).get(0);

            
// Initialize audio/video previews.
            
if ( node ) {
                
this.player = new MediaElementPlayernodemejsSettings );
            } else {
                
this.cleanupPlayer();
            }
        },

        
/**
         * Reset the setting to the default value.
         */
        
restoreDefault: function( event ) {
            if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                return;
            }
            
event.preventDefault();

            
this.params.attachment this.params.defaultAttachment;
            
this.settingthis.params.defaultAttachment.url );
        },

        
/**
         * Called when the "Remove" link is clicked. Empties the setting.
         *
         * @param {object} event jQuery Event object
         */
        
removeFile: function( event ) {
            if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                return;
            }
            
event.preventDefault();

            
this.params.attachment = {};
            
this.setting'' );
            
this.renderContent(); // Not bound to setting change when emptying.
        
}
    });

    
/**
     * An upload control, which utilizes the media modal.
     *
     * @class
     * @augments wp.customize.MediaControl
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    
api.UploadControl api.MediaControl.extend({

        
/**
         * Callback handler for when an attachment is selected in the media modal.
         * Gets the selected image information, and sets it within the control.
         */
        
select: function() {
            
// Get the attachment from the modal frame.
            
var node,
                
attachment this.frame.state().get'selection' ).first().toJSON(),
                
mejsSettings window._wpmejsSettings || {};

            
this.params.attachment attachment;

            
// Set the Customizer setting; the callback takes care of rendering.
            
this.settingattachment.url );
            
node this.container.find'audio, video' ).get(0);

            
// Initialize audio/video previews.
            
if ( node ) {
                
this.player = new MediaElementPlayernodemejsSettings );
            } else {
                
this.cleanupPlayer();
            }
        },

        
// @deprecated
        
success: function() {},

        
// @deprecated
        
removerVisibility: function() {}
    });

    
/**
     * A control for uploading images.
     *
     * This control no longer needs to do anything more
     * than what the upload control does in JS.
     *
     * @class
     * @augments wp.customize.UploadControl
     * @augments wp.customize.MediaControl
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    
api.ImageControl api.UploadControl.extend({
        
// @deprecated
        
thumbnailSrc: function() {}
    });

    
/**
     * A control for uploading background images.
     *
     * @class
     * @augments wp.customize.UploadControl
     * @augments wp.customize.MediaControl
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    
api.BackgroundControl api.UploadControl.extend({

        
/**
         * When the control's DOM structure is ready,
         * set up internal event bindings.
         */
        
ready: function() {
            
api.UploadControl.prototype.ready.applythisarguments );
        },

        
/**
         * Callback handler for when an attachment is selected in the media modal.
         * Does an additional AJAX request for setting the background context.
         */
        
select: function() {
            
api.UploadControl.prototype.select.applythisarguments );

            
wp.ajax.post'custom-background-add', {
                
nonce_wpCustomizeBackground.nonces.add,
                
wp_customize'on',
                
themeapi.settings.theme.stylesheet,
                
attachment_idthis.params.attachment.id
            
} );
        }
    });

    
/**
     * @class
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    
api.HeaderControl api.Control.extend({
        
ready: function() {
            
this.btnRemove = $('#customize-control-header_image .actions .remove');
            
this.btnNew    = $('#customize-control-header_image .actions .new');

            
_.bindAll(this'openMedia''removeImage');

            
this.btnNew.on'click'this.openMedia );
            
this.btnRemove.on'click'this.removeImage );

            
api.HeaderTool.currentHeader this.getInitialHeaderImage();

            new 
api.HeaderTool.CurrentView({
                
modelapi.HeaderTool.currentHeader,
                
el'#customize-control-header_image .current .container'
            
});

            new 
api.HeaderTool.ChoiceListView({
                
collectionapi.HeaderTool.UploadsList = new api.HeaderTool.ChoiceList(),
                
el'#customize-control-header_image .choices .uploaded .list'
            
});

            new 
api.HeaderTool.ChoiceListView({
                
collectionapi.HeaderTool.DefaultsList = new api.HeaderTool.DefaultsList(),
                
el'#customize-control-header_image .choices .default .list'
            
});

            
api.HeaderTool.combinedList api.HeaderTool.CombinedList = new api.HeaderTool.CombinedList([
                
api.HeaderTool.UploadsList,
                
api.HeaderTool.DefaultsList
            
]);
        },

        
/**
         * Returns a new instance of api.HeaderTool.ImageModel based on the currently
         * saved header image (if any).
         *
         * @since 4.2.0
         *
         * @returns {Object} Options
         */
        
getInitialHeaderImage: function() {
            if ( ! 
api.get().header_image || ! api.get().header_image_data || _.contains( [ 'remove-header''random-default-image''random-uploaded-image' ], api.get().header_image ) ) {
                return new 
api.HeaderTool.ImageModel();
            }

            
// Get the matching uploaded image object.
            
var currentHeaderObject _.find_wpCustomizeHeader.uploads, function( imageObj ) {
                return ( 
imageObj.attachment_id === api.get().header_image_data.attachment_id );
            } );
            
// Fall back to raw current header image.
            
if ( ! currentHeaderObject ) {
                
currentHeaderObject = {
                    
urlapi.get().header_image,
                    
thumbnail_urlapi.get().header_image,
                    
attachment_idapi.get().header_image_data.attachment_id
                
};
            }

            return new 
api.HeaderTool.ImageModel({
                
headercurrentHeaderObject,
                
choicecurrentHeaderObject.url.split'/' ).pop()
            });
        },

        
/**
         * Returns a set of options, computed from the attached image data and
         * theme-specific data, to be fed to the imgAreaSelect plugin in
         * wp.media.view.Cropper.
         *
         * @param {wp.media.model.Attachment} attachment
         * @param {wp.media.controller.Cropper} controller
         * @returns {Object} Options
         */
        
calculateImageSelectOptions: function(attachmentcontroller) {
            var 
xInit parseInt(_wpCustomizeHeader.data.width10),
                
yInit parseInt(_wpCustomizeHeader.data.height10),
                
flexWidth = !! parseInt(_wpCustomizeHeader.data['flex-width'], 10),
                
flexHeight = !! parseInt(_wpCustomizeHeader.data['flex-height'], 10),
                
ratioxImgyImgrealHeightrealWidth,
                
imgSelectOptions;

            
realWidth attachment.get('width');
            
realHeight attachment.get('height');

            
this.headerImage = new api.HeaderTool.ImageModel();
            
this.headerImage.set({
                
themeWidthxInit,
                
themeHeightyInit,
                
themeFlexWidthflexWidth,
                
themeFlexHeightflexHeight,
                
imageWidthrealWidth,
                
imageHeightrealHeight
            
});

            
controller.set'canSkipCrop', ! this.headerImage.shouldBeCropped() );

            
ratio xInit yInit;
            
xImg realWidth;
            
yImg realHeight;

            if ( 
xImg yImg ratio ) {
                
yInit yImg;
                
xInit yInit ratio;
            } else {
                
xInit xImg;
                
yInit xInit ratio;
            }

            
imgSelectOptions = {
                
handlestrue,
                
keystrue,
                
instancetrue,
                
persistenttrue,
                
imageWidthrealWidth,
                
imageHeightrealHeight,
                
x10,
                
y10,
                
x2xInit,
                
y2yInit
            
};

            if (
flexHeight === false && flexWidth === false) {
                
imgSelectOptions.aspectRatio xInit ':' yInit;
            }
            if (
flexHeight === false ) {
                
imgSelectOptions.maxHeight yInit;
            }
            if (
flexWidth === false ) {
                
imgSelectOptions.maxWidth xInit;
            }

            return 
imgSelectOptions;
        },

        
/**
         * Sets up and opens the Media Manager in order to select an image.
         * Depending on both the size of the image and the properties of the
         * current theme, a cropping step after selection may be required or
         * skippable.
         *
         * @param {event} event
         */
        
openMedia: function(event) {
            var 
l10n _wpMediaViewsL10n;

            
event.preventDefault();

            
this.frame wp.media({
                
button: {
                    
textl10n.selectAndCrop,
                    
closefalse
                
},
                
states: [
                    new 
wp.media.controller.Library({
                        
title:     l10n.chooseImage,
                        
library:   wp.media.query({ type'image' }),
                        
multiple:  false,
                        
date:      false,
                        
priority:  20,
                        
suggestedWidth_wpCustomizeHeader.data.width,
                        
suggestedHeight_wpCustomizeHeader.data.height
                    
}),
                    new 
wp.media.controller.Cropper({
                        
imgSelectOptionsthis.calculateImageSelectOptions
                    
})
                ]
            });

            
this.frame.on('select'this.onSelectthis);
            
this.frame.on('cropped'this.onCroppedthis);
            
this.frame.on('skippedcrop'this.onSkippedCropthis);

            
this.frame.open();
        },

        
/**
         * After an image is selected in the media modal,
         * switch to the cropper state.
         */
        
onSelect: function() {
            
this.frame.setState('cropper');
        },

        
/**
         * After the image has been cropped, apply the cropped image data to the setting.
         *
         * @param {object} croppedImage Cropped attachment data.
         */
        
onCropped: function(croppedImage) {
            var 
url croppedImage.post_content,
                
attachmentId croppedImage.attachment_id,
                
croppedImage.width,
                
croppedImage.height;
            
this.setImageFromURL(urlattachmentIdwh);
        },

        
/**
         * If cropping was skipped, apply the image data directly to the setting.
         *
         * @param {object} selection
         */
        
onSkippedCrop: function(selection) {
            var 
url selection.get('url'),
                
selection.get('width'),
                
selection.get('height');
            
this.setImageFromURL(urlselection.idwh);
        },

        
/**
         * Creates a new wp.customize.HeaderTool.ImageModel from provided
         * header image data and inserts it into the user-uploaded headers
         * collection.
         *
         * @param {String} url
         * @param {Number} attachmentId
         * @param {Number} width
         * @param {Number} height
         */
        
setImageFromURL: function(urlattachmentIdwidthheight) {
            var 
choicedata = {};

            
data.url url;
            
data.thumbnail_url url;
            
data.timestamp _.now();

            if (
attachmentId) {
                
data.attachment_id attachmentId;
            }

            if (
width) {
                
data.width width;
            }

            if (
height) {
                
data.height height;
            }

            
choice = new api.HeaderTool.ImageModel({
                
headerdata,
                
choiceurl.split('/').pop()
            });
            
api.HeaderTool.UploadsList.add(choice);
            
api.HeaderTool.currentHeader.set(choice.toJSON());
            
choice.save();
            
choice.importImage();
        },

        
/**
         * Triggers the necessary events to deselect an image which was set as
         * the currently selected one.
         */
        
removeImage: function() {
            
api.HeaderTool.currentHeader.trigger('hide');
            
api.HeaderTool.CombinedList.trigger('control:removeImage');
        }

    });

    
/**
     * wp.customize.ThemeControl
     *
     * @constructor
     * @augments wp.customize.Control
     * @augments wp.customize.Class
     */
    
api.ThemeControl api.Control.extend({

        
touchDragfalse,
        
isRenderedfalse,

        
/**
         * Defer rendering the theme control until the section is displayed.
         *
         * @since 4.2.0
         */
        
renderContent: function () {
            var 
control this,
                
renderContentArgs arguments;

            
api.sectioncontrol.section(), function( section ) {
                if ( 
section.expanded() ) {
                    
api.Control.prototype.renderContent.applycontrolrenderContentArgs );
                    
control.isRendered true;
                } else {
                    
section.expanded.bind( function( expanded ) {
                        if ( 
expanded && ! control.isRendered ) {
                            
api.Control.prototype.renderContent.applycontrolrenderContentArgs );
                            
control.isRendered true;
                        }
                    } );
                }
            } );
        },

        
/**
         * @since 4.2.0
         */
        
ready: function() {
            var 
control this;

            
control.container.on'touchmove''.theme', function() {
                
control.touchDrag true;
            });

            
// Bind details view trigger.
            
control.container.on'click keydown touchend''.theme', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }

                
// Bail if the user scrolled on a touch device.
                
if ( control.touchDrag === true ) {
                    return 
control.touchDrag false;
                }

                
// Prevent the modal from showing when the user clicks the action button.
                
if ( $( event.target ).is'.theme-actions .button' ) ) {
                    return;
                }

                var 
previewUrl = $( this ).data'previewUrl' );

                $( 
'.wp-full-overlay' ).addClass'customize-loading' );

                
window.parent.location previewUrl;
            });

            
control.container.on'click keydown''.theme-actions .theme-details', function( event ) {
                if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                    return;
                }

                
event.preventDefault(); // Keep this AFTER the key filter above

                
api.sectioncontrol.section() ).showDetailscontrol.params.theme );
            });

            
control.container.on'render-screenshot', function() {
                var 
$screenshot = $( this ).find'img' ),
                    
source $screenshot.data'src' );

                if ( 
source ) {
                    
$screenshot.attr'src'source );
                }
            });
        },

        
/**
         * Show or hide the theme based on the presence of the term in the title, description, and author.
         *
         * @since 4.2.0
         */
        
filter: function( term ) {
            var 
control this,
                
haystack control.params.theme.name ' ' +
                    
control.params.theme.description ' ' +
                    
control.params.theme.tags ' ' +
                    
control.params.theme.author;
            
haystack haystack.toLowerCase().replace'-'' ' );
            if ( -
!== haystack.searchterm ) ) {
                
control.activate();
            } else {
                
control.deactivate();
            }
        }
    });

    
// Change objects contained within the main customize object to Settings.
    
api.defaultConstructor api.Setting;

    
// Create the collections for Controls, Sections and Panels.
    
api.control = new api.Values({ defaultConstructorapi.Control });
    
api.section = new api.Values({ defaultConstructorapi.Section });
    
api.panel = new api.Values({ defaultConstructorapi.Panel });

    
/**
     * @class
     * @augments wp.customize.Messenger
     * @augments wp.customize.Class
     * @mixes wp.customize.Events
     */
    
api.PreviewFrame api.Messenger.extend({
        
sensitivity2000,

        
initialize: function( paramsoptions ) {
            var 
deferred = $.Deferred();

            
// This is the promise object.
            
deferred.promisethis );

            
this.container params.container;
            
this.signature params.signature;

            $.
extendparams, { channelapi.PreviewFrame.uuid() });

            
api.Messenger.prototype.initialize.callthisparamsoptions );

            
this.add'previewUrl'params.previewUrl );

            
this.query = $.extendparams.query || {}, { customize_messenger_channelthis.channel() });

            
this.rundeferred );
        },

        
run: function( deferred ) {
            var 
self   this,
                
loaded false,
                
ready  false;

            if ( 
this._ready ) {
                
this.unbind'ready'this._ready );
            }

            
this._ready = function() {
                
ready true;

                if ( 
loaded ) {
                    
deferred.resolveWithself );
                }
            };

            
this.bind'ready'this._ready );

            
this.bind'ready', function ( data ) {

                
this.container.addClass'iframe-ready' );

                if ( ! 
data ) {
                    return;
                }

                
/*
                 * Walk over all panels, sections, and controls and set their
                 * respective active states to true if the preview explicitly
                 * indicates as such.
                 */
                
var constructs = {
                    
paneldata.activePanels,
                    
sectiondata.activeSections,
                    
controldata.activeControls
                
};
                
_constructs ).each( function ( activeConstructstype ) {
                    
apitype ].each( function ( constructid ) {
                        var 
active = !! ( activeConstructs && activeConstructsid ] );
                        
construct.activeactive );
                    } );
                } );
            } );

            
this.request = $.ajaxthis.previewUrl(), {
                
type'POST',
                
datathis.query,
                
xhrFields: {
                    
withCredentialstrue
                
}
            } );

            
this.request.fail( function() {
                
deferred.rejectWithself, [ 'request failure' ] );
            });

            
this.request.done( function( response ) {
                var 
location self.request.getResponseHeader('Location'),
                    
signature self.signature,
                    
index;

                
// Check if the location response header differs from the current URL.
                // If so, the request was redirected; try loading the requested page.
                
if ( location && location !== self.previewUrl() ) {
                    
deferred.rejectWithself, [ 'redirect'location ] );
                    return;
                }

                
// Check if the user is not logged in.
                
if ( '0' === response ) {
                    
self.logindeferred );
                    return;
                }

                
// Check for cheaters.
                
if ( '-1' === response ) {
                    
deferred.rejectWithself, [ 'cheatin' ] );
                    return;
                }

                
// Check for a signature in the request.
                
index response.lastIndexOfsignature );
                if ( -
=== index || index response.lastIndexOf('</html>') ) {
                    
deferred.rejectWithself, [ 'unsigned' ] );
                    return;
                }

                
// Strip the signature from the request.
                
response response.slice0index ) + response.sliceindex signature.length );

                
// Create the iframe and inject the html content.
                
self.iframe = $( '<iframe />', { 'title'api.l10n.previewIframeTitle } ).appendToself.container );

                
// Bind load event after the iframe has been added to the page;
                // otherwise it will fire when injected into the DOM.
                
self.iframe.one'load', function() {
                    
loaded true;

                    if ( 
ready ) {
                        
deferred.resolveWithself );
                    } else {
                        
setTimeout( function() {
                            
deferred.rejectWithself, [ 'ready timeout' ] );
                        }, 
self.sensitivity );
                    }
                });

                
self.targetWindowself.iframe[0].contentWindow );

                
self.targetWindow().document.open();
                
self.targetWindow().document.writeresponse );
                
self.targetWindow().document.close();
            });
        },

        
login: function( deferred ) {
            var 
self this,
                
reject;

            
reject = function() {
                
deferred.rejectWithself, [ 'logged out' ] );
            };

            if ( 
this.triedLogin ) {
                return 
reject();
            }

            
// Check if we have an admin cookie.
            
$.getapi.settings.url.ajax, {
                
action'logged-in'
            
}).failreject ).done( function( response ) {
                var 
iframe;

                if ( 
'1' !== response ) {
                    
reject();
                }

                
iframe = $( '<iframe />', { 'src'self.previewUrl(), 'title'api.l10n.previewIframeTitle } ).hide();
                
iframe.appendToself.container );
                
iframe.load( function() {
                    
self.triedLogin true;

                    
iframe.remove();
                    
self.rundeferred );
                });
            });
        },

        
destroy: function() {
            
api.Messenger.prototype.destroy.callthis );
            
this.request.abort();

            if ( 
this.iframe )
                
this.iframe.remove();

            
delete this.request;
            
delete this.iframe;
            
delete this.targetWindow;
        }
    });

    (function(){
        var 
uuid 0;
        
/**
         * Create a universally unique identifier.
         *
         * @return {int}
         */
        
api.PreviewFrame.uuid = function() {
            return 
'preview-' uuid++;
        };
    }());

    
/**
     * Set the document title of the customizer.
     *
     * @since 4.1.0
     *
     * @param {string} documentTitle
     */
    
api.setDocumentTitle = function ( documentTitle ) {
        var 
tmpltitle;
        
tmpl api.settings.documentTitleTmpl;
        
title tmpl.replace'%s'documentTitle );
        
document.title title;
        
api.trigger'title'title );
    };

    
/**
     * @class
     * @augments wp.customize.Messenger
     * @augments wp.customize.Class
     * @mixes wp.customize.Events
     */
    
api.Previewer api.Messenger.extend({
        
refreshBuffer250,

        
/**
         * Requires params:
         *  - container  - a selector or jQuery element
         *  - previewUrl - the URL of preview frame
         */
        
initialize: function( paramsoptions ) {
            var 
self this,
                
rscheme = /^https?/;

            $.
extendthisoptions || {} );
            
this.deferred = {
                
active: $.Deferred()
            };

            
/*
             * Wrap this.refresh to prevent it from hammering the servers:
             *
             * If refresh is called once and no other refresh requests are
             * loading, trigger the request immediately.
             *
             * If refresh is called while another refresh request is loading,
             * debounce the refresh requests:
             * 1. Stop the loading request (as it is instantly outdated).
             * 2. Trigger the new request once refresh hasn't been called for
             *    self.refreshBuffer milliseconds.
             */
            
this.refresh = (function( self ) {
                var 
refresh  self.refresh,
                    
callback = function() {
                        
timeout null;
                        
refresh.callself );
                    },
                    
timeout;

                return function() {
                    if ( 
typeof timeout !== 'number' ) {
                        if ( 
self.loading ) {
                            
self.abort();
                        } else {
                            return 
callback();
                        }
                    }

                    
clearTimeouttimeout );
                    
timeout setTimeoutcallbackself.refreshBuffer );
                };
            })( 
this );

            
this.container   api.ensureparams.container );
            
this.allowedUrls params.allowedUrls;
            
this.signature   params.signature;

            
params.url window.location.href;

            
api.Messenger.prototype.initialize.callthisparams );

            
this.add'scheme'this.origin() ).linkthis.origin ).setter( function( to ) {
                var 
match to.matchrscheme );
                return 
match match[0] : '';
            });

            
// Limit the URL to internal, front-end links.
            //
            // If the frontend and the admin are served from the same domain, load the
            // preview over ssl if the Customizer is being loaded over ssl. This avoids
            // insecure content warnings. This is not attempted if the admin and frontend
            // are on different domains to avoid the case where the frontend doesn't have
            // ssl certs.

            
this.add'previewUrl'params.previewUrl ).setter( function( to ) {
                var 
result;

                
// Check for URLs that include "/wp-admin/" or end in "/wp-admin".
                // Strip hashes and query strings before testing.
                
if ( //wp-admin(/|$)/.test( to.replace( /[#?].*$/, '' ) ) )
                    
return null;

                
// Attempt to match the URL to the control frame's scheme
                // and check if it's allowed. If not, try the original URL.
                
$.each([ to.replacerschemeself.scheme() ), to ], function( iurl ) {
                    $.
eachself.allowedUrls, function( iallowed ) {
                        var 
path;

                        
allowed allowed.replace//+$/, '' );
                        
path url.replaceallowed'' );

                        if ( 
=== url.indexOfallowed ) && /^([/#?]|$)/.test( path ) ) {
                            
result url;
                            return 
false;
                        }
                    });
                    if ( 
result )
                        return 
false;
                });

                
// If we found a matching result, return it. If not, bail.
                
return result result null;
            });

            
// Refresh the preview when the URL is changed (but not yet).
            
this.previewUrl.bindthis.refresh );

            
this.scroll 0;
            
this.bind'scroll', function( distance ) {
                
this.scroll distance;
            });

            
// Update the URL when the iframe sends a URL message.
            
this.bind'url'this.previewUrl );

            
// Update the document title when the preview changes.
            
this.bind'documentTitle', function ( title ) {
                
api.setDocumentTitletitle );
            } );
        },

        
query: function() {},

        
abort: function() {
            if ( 
this.loading ) {
                
this.loading.destroy();
                
delete this.loading;
            }
        },

        
refresh: function() {
            var 
self this;

            
// Display loading indicator
            
this.send'loading-initiated' );

            
this.abort();

            
this.loading = new api.PreviewFrame({
                
url:        this.url(),
                
previewUrlthis.previewUrl(),
                
query:      this.query() || {},
                
container:  this.container,
                
signature:  this.signature
            
});

            
this.loading.done( function() {
                
// 'this' is the loading frame
                
this.bind'synced', function() {
                    if ( 
self.preview )
                        
self.preview.destroy();
                    
self.preview this;
                    
delete self.loading;

                    
self.targetWindowthis.targetWindow() );
                    
self.channelthis.channel() );

                    
self.deferred.active.resolve();
                    
self.send'active' );
                });

                
this.send'sync', {
                    
scroll:   self.scroll,
                    
settingsapi.get()
                });
            });

            
this.loading.fail( function( reasonlocation ) {
                
self.send'loading-failed' );
                if ( 
'redirect' === reason && location ) {
                    
self.previewUrllocation );
                }

                if ( 
'logged out' === reason ) {
                    if ( 
self.preview ) {
                        
self.preview.destroy();
                        
delete self.preview;
                    }

                    
self.login().doneself.refresh );
                }

                if ( 
'cheatin' === reason ) {
                    
self.cheatin();
                }
            });
        },

        
login: function() {
            var 
previewer this,
                
deferredmessengeriframe;

            if ( 
this._login )
                return 
this._login;

            
deferred = $.Deferred();
            
this._login deferred.promise();

            
messenger = new api.Messenger({
                
channel'login',
                
url:     api.settings.url.login
            
});

            
iframe = $( '<iframe />', { 'src'api.settings.url.login'title'api.l10n.loginIframeTitle } ).appendTothis.container );

            
messenger.targetWindowiframe[0].contentWindow );

            
messenger.bind'login', function () {
                var 
refreshNonces previewer.refreshNonces();

                
refreshNonces.always( function() {
                    
iframe.remove();
                    
messenger.destroy();
                    
delete previewer._login;
                });

                
refreshNonces.done( function() {
                    
deferred.resolve();
                });

                
refreshNonces.fail( function() {
                    
previewer.cheatin();
                    
deferred.reject();
                });
            });

            return 
this._login;
        },

        
cheatin: function() {
            $( 
document.body ).empty().addClass('cheatin').append'<p>' api.l10n.cheatin '</p>' );
        },

        
refreshNonces: function() {
            var 
requestdeferred = $.Deferred();

            
deferred.promise();

            
request wp.ajax.post'customize_refresh_nonces', {
                
wp_customize'on',
                
themeapi.settings.theme.stylesheet
            
});

            
request.done( function( response ) {
                
api.trigger'nonce-refresh'response );
                
deferred.resolve();
            });

            
request.fail( function() {
                
deferred.reject();
            });

            return 
deferred;
        }
    });

    
api.controlConstructor = {
        
color:      api.ColorControl,
        
media:      api.MediaControl,
        
upload:     api.UploadControl,
        
image:      api.ImageControl,
        
header:     api.HeaderControl,
        
backgroundapi.BackgroundControl,
        
theme:      api.ThemeControl
    
};
    
api.panelConstructor = {};
    
api.sectionConstructor = {
        
themesapi.ThemesSection
    
};

    $( function() {
        
api.settings window._wpCustomizeSettings;
        
api.l10n window._wpCustomizeControlsL10n;

        
// Check if we can run the Customizer.
        
if ( ! api.settings ) {
            return;
        }

        
// Redirect to the fallback preview if any incompatibilities are found.
        
if ( ! $.support.postMessage || ( ! $.support.cors && api.settings.isCrossDomain ) )
            return 
window.location api.settings.url.fallback;

        var 
parenttopFocus,
            
body = $( document.body ),
            
overlay body.children'.wp-full-overlay' ),
            
title = $( '#customize-info .theme-name.site-title' ),
            
closeBtn = $( '.customize-controls-close' ),
            
saveBtn = $( '#save' );

        
// Prevent the form from saving when enter is pressed on an input or select element.
        
$('#customize-controls').on'keydown', function( ) {
            var 
isEnter = ( 13 === e.which ),
                
$el = $( e.target );

            if ( 
isEnter && ( $el.is'input:not([type=button])' ) || $el.is'select' ) ) ) {
                
e.preventDefault();
            }
        });

        
// Expand/Collapse the main customizer customize info.
        
$( '#customize-info' ).find'> .accordion-section-title' ).on'click keydown', function( event ) {
            if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                return;
            }
            
event.preventDefault(); // Keep this AFTER the key filter above

            
var section = $( this ).parent(),
                
content section.find'.accordion-section-content:first' );

            if ( 
section.hasClass'cannot-expand' ) ) {
                return;
            }

            if ( 
section.hasClass'open' ) ) {
                
section.toggleClass'open' );
                
content.slideUpapi.Panel.prototype.defaultExpandedArguments.duration );
            } else {
                
content.slideDownapi.Panel.prototype.defaultExpandedArguments.duration );
                
section.toggleClass'open' );
            }
        });

        
// Initialize Previewer
        
api.previewer = new api.Previewer({
            
container:   '#customize-preview',
            
form:        '#customize-controls',
            
previewUrl:  api.settings.url.preview,
            
allowedUrlsapi.settings.url.allowed,
            
signature:   'WP_CUSTOMIZER_SIGNATURE'
        
}, {

            
nonceapi.settings.nonce,

            
query: function() {
                var 
dirtyCustomized = {};
                
api.each( function ( valuekey ) {
                    if ( 
value._dirty ) {
                        
dirtyCustomizedkey ] = value();
                    }
                } );

                return {
                    
wp_customize'on',
                    
theme:      api.settings.theme.stylesheet,
                    
customizedJSON.stringifydirtyCustomized ),
                    
nonce:      this.nonce.preview
                
};
            },

            
save: function() {
                var 
self this,
                    
processing api.state'processing' ),
                    
submitWhenDoneProcessing,
                    
submit;

                
body.addClass'saving' );

                
submit = function () {
                    var 
requestquery;
                    
query = $.extendself.query(), {
                        
nonce:  self.nonce.save
                    
} );
                    
request wp.ajax.post'customize_save'query );

                    
api.trigger'save'request );

                    
request.always( function () {
                        
body.removeClass'saving' );
                    } );

                    
request.fail( function ( response ) {
                        if ( 
'0' === response ) {
                            
response 'not_logged_in';
                        } else if ( 
'-1' === response ) {
                            
// Back-compat in case any other check_ajax_referer() call is dying
                            
response 'invalid_nonce';
                        }

                        if ( 
'invalid_nonce' === response ) {
                            
self.cheatin();
                        } else if ( 
'not_logged_in' === response ) {
                            
self.preview.iframe.hide();
                            
self.login().done( function() {
                                
self.save();
                                
self.preview.iframe.show();
                            } );
                        }
                        
api.trigger'error'response );
                    } );

                    
request.done( function( response ) {
                        
// Clear setting dirty states
                        
api.each( function ( value ) {
                            
value._dirty false;
                        } );

                        
api.trigger'saved'response );
                    } );
                };

                if ( 
=== processing() ) {
                    
submit();
                } else {
                    
submitWhenDoneProcessing = function () {
                        if ( 
=== processing() ) {
                            
api.state.unbind'change'submitWhenDoneProcessing );
                            
submit();
                        }
                    };
                    
api.state.bind'change'submitWhenDoneProcessing );
                }

            }
        });

        
// Refresh the nonces if the preview sends updated nonces over.
        
api.previewer.bind'nonce', function( nonce ) {
            $.
extendthis.noncenonce );
        });

        
// Refresh the nonces if login sends updated nonces over.
        
api.bind'nonce-refresh', function( nonce ) {
            $.
extendapi.settings.noncenonce );
            $.
extendapi.previewer.noncenonce );
        });

        
// Create Settings
        
$.eachapi.settings.settings, function( iddata ) {
            
api.createididdata.value, {
                
transportdata.transport,
                
previewerapi.previewer,
                
dirty: !! data.dirty
            
} );
        });

        
// Create Panels
        
$.eachapi.settings.panels, function ( iddata ) {
            var 
constructor api.panelConstructordata.type ] || api.Panel,
                
panel;

            
panel = new constructorid, {
                
paramsdata
            
} );
            
api.panel.addidpanel );
        });

        
// Create Sections
        
$.eachapi.settings.sections, function ( iddata ) {
            var 
constructor api.sectionConstructordata.type ] || api.Section,
                
section;

            
section = new constructorid, {
                
paramsdata
            
} );
            
api.section.addidsection );
        });

        
// Create Controls
        
$.eachapi.settings.controls, function( iddata ) {
            var 
constructor api.controlConstructordata.type ] || api.Control,
                
control;

            
control = new constructorid, {
                
paramsdata,
                
previewerapi.previewer
            
} );
            
api.control.addidcontrol );
        });

        
// Focus the autofocused element
        
_.each( [ 'panel''section''control' ], function ( type ) {
            var 
instanceid api.settings.autofocustype ];
            if ( 
id && apitype ]( id ) ) {
                
instance apitype ]( id );
                
// Wait until the element is embedded in the DOM
                
instance.deferred.embedded.done( function () {
                    
// Wait until the preview has activated and so active panels, sections, controls have been set
                    
api.previewer.deferred.active.done( function () {
                        
instance.focus();
                    });
                });
            }
        });

        
/**
         * Sort panels, sections, controls by priorities. Hide empty sections and panels.
         *
         * @since 4.1.0
         */
        
api.reflowPaneContents _.bind( function () {

            var 
appendContaineractiveElementrootContainersrootNodes = [], wasReflowed false;

            if ( 
document.activeElement ) {
                
activeElement = $( document.activeElement );
            }

            
// Sort the sections within each panel
            
api.panel.each( function ( panel ) {
                var 
sections panel.sections(),
                    
sectionContainers _.plucksections'container' );
                
rootNodes.pushpanel );
                
appendContainer panel.container.find'ul:first' );
                if ( ! 
api.utils.areElementListsEqualsectionContainersappendContainer.children'[id]' ) ) ) {
                    
_sections ).each( function ( section ) {
                        
appendContainer.appendsection.container );
                    } );
                    
wasReflowed true;
                }
            } );

            
// Sort the controls within each section
            
api.section.each( function ( section ) {
                var 
controls section.controls(),
                    
controlContainers _.pluckcontrols'container' );
                if ( ! 
section.panel() ) {
                    
rootNodes.pushsection );
                }
                
appendContainer section.container.find'ul:first' );
                if ( ! 
api.utils.areElementListsEqualcontrolContainersappendContainer.children'[id]' ) ) ) {
                    
_controls ).each( function ( control ) {
                        
appendContainer.appendcontrol.container );
                    } );
                    
wasReflowed true;
                }
            } );

            
// Sort the root panels and sections
            
rootNodes.sortapi.utils.prioritySort );
            
rootContainers _.pluckrootNodes'container' );
            
appendContainer = $( '#customize-theme-controls' ).children'ul' ); // @todo This should be defined elsewhere, and to be configurable
            
if ( ! api.utils.areElementListsEqualrootContainersappendContainer.children() ) ) {
                
_rootNodes ).each( function ( rootNode ) {
                    
appendContainer.appendrootNode.container );
                } );
                
wasReflowed true;
            }

            
// Now re-trigger the active Value callbacks to that the panels and sections can decide whether they can be rendered
            
api.panel.each( function ( panel ) {
                var 
value panel.active();
                
panel.active.callbacks.fireWithpanel.active, [ valuevalue ] );
            } );
            
api.section.each( function ( section ) {
                var 
value section.active();
                
section.active.callbacks.fireWithsection.active, [ valuevalue ] );
            } );

            
// Restore focus if there was a reflow and there was an active (focused) element
            
if ( wasReflowed && activeElement ) {
                
activeElement.focus();
            }
        }, 
api );
        
api.bind'ready'api.reflowPaneContents );
        
api.reflowPaneContents _.debounceapi.reflowPaneContents100 );
        $( [ 
api.panelapi.sectionapi.control ] ).each( function ( ivalues ) {
            
values.bind'add'api.reflowPaneContents );
            
values.bind'change'api.reflowPaneContents );
            
values.bind'remove'api.reflowPaneContents );
        } );

        
// Check if preview url is valid and load the preview frame.
        
if ( api.previewer.previewUrl() ) {
            
api.previewer.refresh();
        } else {
            
api.previewer.previewUrlapi.settings.url.home );
        }

        
// Save and activated states
        
(function() {
            var 
state = new api.Values(),
                
saved state.create'saved' ),
                
activated state.create'activated' ),
                
processing state.create'processing' );

            
state.bind'change', function() {
                if ( ! 
activated() ) {
                    
saveBtn.valapi.l10n.activate ).prop'disabled'false );
                    
closeBtn.find'.screen-reader-text' ).textapi.l10n.cancel );

                } else if ( 
saved() ) {
                    
saveBtn.valapi.l10n.saved ).prop'disabled'true );
                    
closeBtn.find'.screen-reader-text' ).textapi.l10n.close );

                } else {
                    
saveBtn.valapi.l10n.save ).prop'disabled'false );
                    
closeBtn.find'.screen-reader-text' ).textapi.l10n.cancel );
                }
            });

            
// Set default states.
            
savedtrue );
            
activatedapi.settings.theme.active );
            
processing);

            
api.bind'change', function() {
                
state('saved').setfalse );
            });

            
api.bind'saved', function() {
                
state('saved').settrue );
                
state('activated').settrue );
            });

            
activated.bind( function( to ) {
                if ( 
to )
                    
api.trigger'activated' );
            });

            
// Expose states to the API.
            
api.state state;
        }());

        
// Button bindings.
        
saveBtn.click( function( event ) {
            
api.previewer.save();
            
event.preventDefault();
        }).
keydown( function( event ) {
            if ( 
=== event.which // tab
                
return;
            if ( 
13 === event.which // enter
                
api.previewer.save();
            
event.preventDefault();
        });

        
// Go back to the top-level Customizer accordion.
        
$( '#customize-header-actions' ).on'click keydown''.control-panel-back', function( event ) {
            if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                return;
            }

            
event.preventDefault(); // Keep this AFTER the key filter above
            
api.panel.each( function ( panel ) {
                
panel.collapse();
            });
        });

        
closeBtn.keydown( function( event ) {
            if ( 
=== event.which // tab
                
return;
            if ( 
13 === event.which // enter
                
this.click();
            
event.preventDefault();
        });

        $(
'.collapse-sidebar').on'click keydown', function( event ) {
            if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                return;
            }

            
overlay.toggleClass'collapsed' ).toggleClass'expanded' );
            
event.preventDefault();
        });

        $( 
'.customize-controls-preview-toggle' ).on'click keydown', function( event ) {
            if ( 
api.utils.isKeydownButNotEnterEventevent ) ) {
                return;
            }

            
overlay.toggleClass'preview-only' );
            
event.preventDefault();
        });

        
// Bind site title display to the corresponding field.
        
if ( title.length ) {
            $( 
'#customize-control-blogname input' ).on'input', function() {
                
title.textthis.value );
            } );
        }

        
// Create a potential postMessage connection with the parent frame.
        
parent = new api.Messenger({
            
urlapi.settings.url.parent,
            
channel'loader'
        
});

        
// If we receive a 'back' event, we're inside an iframe.
        // Send any clicks to the 'Return' link to the parent page.
        
parent.bind'back', function() {
            
closeBtn.on'click.customize-controls-close', function( event ) {
                
event.preventDefault();
                
parent.send'close' );
            });
        });

        
// Prompt user with AYS dialog if leaving the Customizer with unsaved changes
        
$( window ).on'beforeunload', function () {
            if ( ! 
api.state'saved' )() ) {
                
setTimeout( function() {
                    
overlay.removeClass'customize-loading' );
                }, 
);
                return 
api.l10n.saveAlert;
            }
        } );

        
// Pass events through to the parent.
        
$.each( [ 'saved''change' ], function ( ievent ) {
            
api.bindevent, function() {
                
parent.sendevent );
            });
        } );

        
// When activated, let the loader handle redirecting the page.
        // If no loader exists, redirect the page ourselves (if a url exists).
        
api.bind'activated', function() {
            if ( 
parent.targetWindow() )
                
parent.send'activated'api.settings.url.activated );
            else if ( 
api.settings.url.activated )
                
window.location api.settings.url.activated;
        });

        
// Pass titles to the parent
        
api.bind'title', function( newTitle ) {
            
parent.send'title'newTitle );
        });

        
// Initialize the connection with the parent frame.
        
parent.send'ready' );

        
// Control visibility for default controls
        
$.each({
            
'background_image': {
                
controls: [ 'background_repeat''background_position_x''background_attachment' ],
                
callback: function( to ) { return !! to; }
            },
            
'show_on_front': {
                
controls: [ 'page_on_front''page_for_posts' ],
                
callback: function( to ) { return 'page' === to; }
            },
            
'header_textcolor': {
                
controls: [ 'header_textcolor' ],
                
callback: function( to ) { return 'blank' !== to; }
            }
        }, function( 
settingId) {
            
apisettingId, function( setting ) {
                $.
eacho.controls, function( icontrolId ) {
                    
api.controlcontrolId, function( control ) {
                        var 
visibility = function( to ) {
                            
control.container.toggleo.callbackto ) );
                        };

                        
visibilitysetting.get() );
                        
setting.bindvisibility );
                    });
                });
            });
        });

        
// Juggle the two controls that use header_textcolor
        
api.control'display_header_text', function( control ) {
            var 
last '';

            
control.elements[0].unsyncapi'header_textcolor' ) );

            
control.element = new api.Elementcontrol.container.find('input') );
            
control.element.set'blank' !== control.setting() );

            
control.element.bind( function( to ) {
                if ( ! 
to )
                    
last api'header_textcolor' ).get();

                
control.setting.setto last 'blank' );
            });

            
control.setting.bind( function( to ) {
                
control.element.set'blank' !== to );
            });
        });

        
api.trigger'ready' );

        
// Make sure left column gets focus
        
topFocus closeBtn;
        
topFocus.focus();
        
setTimeout(function () {
            
topFocus.focus();
        }, 
200);

    });

})( 
wpjQuery );
?>
Онлайн: 1
Реклама