home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / js / customize-base.js < prev    next >
Encoding:
JavaScript  |  2017-10-09  |  25.0 KB  |  969 lines

  1. /** @namespace wp */
  2. window.wp = window.wp || {};
  3.  
  4. (function( exports, $ ){
  5.     var api = {}, ctor, inherits,
  6.         slice = Array.prototype.slice;
  7.  
  8.     // Shared empty constructor function to aid in prototype-chain creation.
  9.     ctor = function() {};
  10.  
  11.     /**
  12.      * Helper function to correctly set up the prototype chain, for subclasses.
  13.      * Similar to `goog.inherits`, but uses a hash of prototype properties and
  14.      * class properties to be extended.
  15.      *
  16.      * @param  object parent      Parent class constructor to inherit from.
  17.      * @param  object protoProps  Properties to apply to the prototype for use as class instance properties.
  18.      * @param  object staticProps Properties to apply directly to the class constructor.
  19.      * @return child              The subclassed constructor.
  20.      */
  21.     inherits = function( parent, protoProps, staticProps ) {
  22.         var child;
  23.  
  24.         // The constructor function for the new subclass is either defined by you
  25.         // (the "constructor" property in your `extend` definition), or defaulted
  26.         // by us to simply call `super()`.
  27.         if ( protoProps && protoProps.hasOwnProperty( 'constructor' ) ) {
  28.             child = protoProps.constructor;
  29.         } else {
  30.             child = function() {
  31.                 // Storing the result `super()` before returning the value
  32.                 // prevents a bug in Opera where, if the constructor returns
  33.                 // a function, Opera will reject the return value in favor of
  34.                 // the original object. This causes all sorts of trouble.
  35.                 var result = parent.apply( this, arguments );
  36.                 return result;
  37.             };
  38.         }
  39.  
  40.         // Inherit class (static) properties from parent.
  41.         $.extend( child, parent );
  42.  
  43.         // Set the prototype chain to inherit from `parent`, without calling
  44.         // `parent`'s constructor function.
  45.         ctor.prototype  = parent.prototype;
  46.         child.prototype = new ctor();
  47.  
  48.         // Add prototype properties (instance properties) to the subclass,
  49.         // if supplied.
  50.         if ( protoProps )
  51.             $.extend( child.prototype, protoProps );
  52.  
  53.         // Add static properties to the constructor function, if supplied.
  54.         if ( staticProps )
  55.             $.extend( child, staticProps );
  56.  
  57.         // Correctly set child's `prototype.constructor`.
  58.         child.prototype.constructor = child;
  59.  
  60.         // Set a convenience property in case the parent's prototype is needed later.
  61.         child.__super__ = parent.prototype;
  62.  
  63.         return child;
  64.     };
  65.  
  66.     /**
  67.      * Base class for object inheritance.
  68.      */
  69.     api.Class = function( applicator, argsArray, options ) {
  70.         var magic, args = arguments;
  71.  
  72.         if ( applicator && argsArray && api.Class.applicator === applicator ) {
  73.             args = argsArray;
  74.             $.extend( this, options || {} );
  75.         }
  76.  
  77.         magic = this;
  78.  
  79.         /*
  80.          * If the class has a method called "instance",
  81.          * the return value from the class' constructor will be a function that
  82.          * calls the "instance" method.
  83.          *
  84.          * It is also an object that has properties and methods inside it.
  85.          */
  86.         if ( this.instance ) {
  87.             magic = function() {
  88.                 return magic.instance.apply( magic, arguments );
  89.             };
  90.  
  91.             $.extend( magic, this );
  92.         }
  93.  
  94.         magic.initialize.apply( magic, args );
  95.         return magic;
  96.     };
  97.  
  98.     /**
  99.      * Creates a subclass of the class.
  100.      *
  101.      * @param  object protoProps  Properties to apply to the prototype.
  102.      * @param  object staticProps Properties to apply directly to the class.
  103.      * @return child              The subclass.
  104.      */
  105.     api.Class.extend = function( protoProps, classProps ) {
  106.         var child = inherits( this, protoProps, classProps );
  107.         child.extend = this.extend;
  108.         return child;
  109.     };
  110.  
  111.     api.Class.applicator = {};
  112.  
  113.     /**
  114.      * Initialize a class instance.
  115.      *
  116.      * Override this function in a subclass as needed.
  117.      */
  118.     api.Class.prototype.initialize = function() {};
  119.  
  120.     /*
  121.      * Checks whether a given instance extended a constructor.
  122.      *
  123.      * The magic surrounding the instance parameter causes the instanceof
  124.      * keyword to return inaccurate results; it defaults to the function's
  125.      * prototype instead of the constructor chain. Hence this function.
  126.      */
  127.     api.Class.prototype.extended = function( constructor ) {
  128.         var proto = this;
  129.  
  130.         while ( typeof proto.constructor !== 'undefined' ) {
  131.             if ( proto.constructor === constructor )
  132.                 return true;
  133.             if ( typeof proto.constructor.__super__ === 'undefined' )
  134.                 return false;
  135.             proto = proto.constructor.__super__;
  136.         }
  137.         return false;
  138.     };
  139.  
  140.     /**
  141.      * An events manager object, offering the ability to bind to and trigger events.
  142.      *
  143.      * Used as a mixin.
  144.      */
  145.     api.Events = {
  146.         trigger: function( id ) {
  147.             if ( this.topics && this.topics[ id ] )
  148.                 this.topics[ id ].fireWith( this, slice.call( arguments, 1 ) );
  149.             return this;
  150.         },
  151.  
  152.         bind: function( id ) {
  153.             this.topics = this.topics || {};
  154.             this.topics[ id ] = this.topics[ id ] || $.Callbacks();
  155.             this.topics[ id ].add.apply( this.topics[ id ], slice.call( arguments, 1 ) );
  156.             return this;
  157.         },
  158.  
  159.         unbind: function( id ) {
  160.             if ( this.topics && this.topics[ id ] )
  161.                 this.topics[ id ].remove.apply( this.topics[ id ], slice.call( arguments, 1 ) );
  162.             return this;
  163.         }
  164.     };
  165.  
  166.     /**
  167.      * Observable values that support two-way binding.
  168.      *
  169.      * @memberOf wp.customize
  170.      * @alias wp.customize.Value
  171.      *
  172.      * @constructor
  173.      */
  174.     api.Value = api.Class.extend(/** @lends wp.customize.Value.prototype */{
  175.         /**
  176.          * @param {mixed}  initial The initial value.
  177.          * @param {object} options
  178.          */
  179.         initialize: function( initial, options ) {
  180.             this._value = initial; // @todo: potentially change this to a this.set() call.
  181.             this.callbacks = $.Callbacks();
  182.             this._dirty = false;
  183.  
  184.             $.extend( this, options || {} );
  185.  
  186.             this.set = $.proxy( this.set, this );
  187.         },
  188.  
  189.         /*
  190.          * Magic. Returns a function that will become the instance.
  191.          * Set to null to prevent the instance from extending a function.
  192.          */
  193.         instance: function() {
  194.             return arguments.length ? this.set.apply( this, arguments ) : this.get();
  195.         },
  196.  
  197.         /**
  198.          * Get the value.
  199.          *
  200.          * @return {mixed}
  201.          */
  202.         get: function() {
  203.             return this._value;
  204.         },
  205.  
  206.         /**
  207.          * Set the value and trigger all bound callbacks.
  208.          *
  209.          * @param {object} to New value.
  210.          */
  211.         set: function( to ) {
  212.             var from = this._value;
  213.  
  214.             to = this._setter.apply( this, arguments );
  215.             to = this.validate( to );
  216.  
  217.             // Bail if the sanitized value is null or unchanged.
  218.             if ( null === to || _.isEqual( from, to ) ) {
  219.                 return this;
  220.             }
  221.  
  222.             this._value = to;
  223.             this._dirty = true;
  224.  
  225.             this.callbacks.fireWith( this, [ to, from ] );
  226.  
  227.             return this;
  228.         },
  229.  
  230.         _setter: function( to ) {
  231.             return to;
  232.         },
  233.  
  234.         setter: function( callback ) {
  235.             var from = this.get();
  236.             this._setter = callback;
  237.             // Temporarily clear value so setter can decide if it's valid.
  238.             this._value = null;
  239.             this.set( from );
  240.             return this;
  241.         },
  242.  
  243.         resetSetter: function() {
  244.             this._setter = this.constructor.prototype._setter;
  245.             this.set( this.get() );
  246.             return this;
  247.         },
  248.  
  249.         validate: function( value ) {
  250.             return value;
  251.         },
  252.  
  253.         /**
  254.          * Bind a function to be invoked whenever the value changes.
  255.          *
  256.          * @param {...Function} A function, or multiple functions, to add to the callback stack.
  257.          */
  258.         bind: function() {
  259.             this.callbacks.add.apply( this.callbacks, arguments );
  260.             return this;
  261.         },
  262.  
  263.         /**
  264.          * Unbind a previously bound function.
  265.          *
  266.          * @param {...Function} A function, or multiple functions, to remove from the callback stack.
  267.          */
  268.         unbind: function() {
  269.             this.callbacks.remove.apply( this.callbacks, arguments );
  270.             return this;
  271.         },
  272.  
  273.         link: function() { // values*
  274.             var set = this.set;
  275.             $.each( arguments, function() {
  276.                 this.bind( set );
  277.             });
  278.             return this;
  279.         },
  280.  
  281.         unlink: function() { // values*
  282.             var set = this.set;
  283.             $.each( arguments, function() {
  284.                 this.unbind( set );
  285.             });
  286.             return this;
  287.         },
  288.  
  289.         sync: function() { // values*
  290.             var that = this;
  291.             $.each( arguments, function() {
  292.                 that.link( this );
  293.                 this.link( that );
  294.             });
  295.             return this;
  296.         },
  297.  
  298.         unsync: function() { // values*
  299.             var that = this;
  300.             $.each( arguments, function() {
  301.                 that.unlink( this );
  302.                 this.unlink( that );
  303.             });
  304.             return this;
  305.         }
  306.     });
  307.  
  308.     /**
  309.      * A collection of observable values.
  310.      *
  311.      * @memberOf wp.customize
  312.      * @alias wp.customize.Values
  313.      *
  314.      * @constructor
  315.      * @augments wp.customize.Class
  316.      * @mixes wp.customize.Events
  317.      */
  318.     api.Values = api.Class.extend(/** @lends wp.customize.Values.prototype */{
  319.  
  320.         /**
  321.          * The default constructor for items of the collection.
  322.          *
  323.          * @type {object}
  324.          */
  325.         defaultConstructor: api.Value,
  326.  
  327.         initialize: function( options ) {
  328.             $.extend( this, options || {} );
  329.  
  330.             this._value = {};
  331.             this._deferreds = {};
  332.         },
  333.  
  334.         /**
  335.          * Get the instance of an item from the collection if only ID is specified.
  336.          *
  337.          * If more than one argument is supplied, all are expected to be IDs and
  338.          * the last to be a function callback that will be invoked when the requested
  339.          * items are available.
  340.          *
  341.          * @see {api.Values.when}
  342.          *
  343.          * @param  {string}   id ID of the item.
  344.          * @param  {...}         Zero or more IDs of items to wait for and a callback
  345.          *                       function to invoke when they're available. Optional.
  346.          * @return {mixed}    The item instance if only one ID was supplied.
  347.          *                    A Deferred Promise object if a callback function is supplied.
  348.          */
  349.         instance: function( id ) {
  350.             if ( arguments.length === 1 )
  351.                 return this.value( id );
  352.  
  353.             return this.when.apply( this, arguments );
  354.         },
  355.  
  356.         /**
  357.          * Get the instance of an item.
  358.          *
  359.          * @param  {string} id The ID of the item.
  360.          * @return {[type]}    [description]
  361.          */
  362.         value: function( id ) {
  363.             return this._value[ id ];
  364.         },
  365.  
  366.         /**
  367.          * Whether the collection has an item with the given ID.
  368.          *
  369.          * @param  {string}  id The ID of the item to look for.
  370.          * @return {Boolean}
  371.          */
  372.         has: function( id ) {
  373.             return typeof this._value[ id ] !== 'undefined';
  374.         },
  375.  
  376.         /**
  377.          * Add an item to the collection.
  378.          *
  379.          * @param {string|wp.customize.Class} item - The item instance to add, or the ID for the instance to add. When an ID string is supplied, then itemObject must be provided.
  380.          * @param {wp.customize.Class}        [itemObject] - The item instance when the first argument is a ID string.
  381.          * @return {wp.customize.Class} The new item's instance, or an existing instance if already added.
  382.          */
  383.         add: function( item, itemObject ) {
  384.             var collection = this, id, instance;
  385.             if ( 'string' === typeof item ) {
  386.                 id = item;
  387.                 instance = itemObject;
  388.             } else {
  389.                 if ( 'string' !== typeof item.id ) {
  390.                     throw new Error( 'Unknown key' );
  391.                 }
  392.                 id = item.id;
  393.                 instance = item;
  394.             }
  395.  
  396.             if ( collection.has( id ) ) {
  397.                 return collection.value( id );
  398.             }
  399.  
  400.             collection._value[ id ] = instance;
  401.             instance.parent = collection;
  402.  
  403.             // Propagate a 'change' event on an item up to the collection.
  404.             if ( instance.extended( api.Value ) ) {
  405.                 instance.bind( collection._change );
  406.             }
  407.  
  408.             collection.trigger( 'add', instance );
  409.  
  410.             // If a deferred object exists for this item,
  411.             // resolve it.
  412.             if ( collection._deferreds[ id ] ) {
  413.                 collection._deferreds[ id ].resolve();
  414.             }
  415.  
  416.             return collection._value[ id ];
  417.         },
  418.  
  419.         /**
  420.          * Create a new item of the collection using the collection's default constructor
  421.          * and store it in the collection.
  422.          *
  423.          * @param  {string} id    The ID of the item.
  424.          * @param  {mixed}  value Any extra arguments are passed into the item's initialize method.
  425.          * @return {mixed}  The new item's instance.
  426.          */
  427.         create: function( id ) {
  428.             return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) );
  429.         },
  430.  
  431.         /**
  432.          * Iterate over all items in the collection invoking the provided callback.
  433.          *
  434.          * @param  {Function} callback Function to invoke.
  435.          * @param  {object}   context  Object context to invoke the function with. Optional.
  436.          */
  437.         each: function( callback, context ) {
  438.             context = typeof context === 'undefined' ? this : context;
  439.  
  440.             $.each( this._value, function( key, obj ) {
  441.                 callback.call( context, obj, key );
  442.             });
  443.         },
  444.  
  445.         /**
  446.          * Remove an item from the collection.
  447.          *
  448.          * @param  {string} id The ID of the item to remove.
  449.          */
  450.         remove: function( id ) {
  451.             var value = this.value( id );
  452.  
  453.             if ( value ) {
  454.  
  455.                 // Trigger event right before the element is removed from the collection.
  456.                 this.trigger( 'remove', value );
  457.  
  458.                 if ( value.extended( api.Value ) ) {
  459.                     value.unbind( this._change );
  460.                 }
  461.                 delete value.parent;
  462.             }
  463.  
  464.             delete this._value[ id ];
  465.             delete this._deferreds[ id ];
  466.  
  467.             // Trigger removed event after the item has been eliminated from the collection.
  468.             if ( value ) {
  469.                 this.trigger( 'removed', value );
  470.             }
  471.         },
  472.  
  473.         /**
  474.          * Runs a callback once all requested values exist.
  475.          *
  476.          * when( ids*, [callback] );
  477.          *
  478.          * For example:
  479.          *     when( id1, id2, id3, function( value1, value2, value3 ) {} );
  480.          *
  481.          * @returns $.Deferred.promise();
  482.          */
  483.         when: function() {
  484.             var self = this,
  485.                 ids  = slice.call( arguments ),
  486.                 dfd  = $.Deferred();
  487.  
  488.             // If the last argument is a callback, bind it to .done()
  489.             if ( $.isFunction( ids[ ids.length - 1 ] ) )
  490.                 dfd.done( ids.pop() );
  491.  
  492.             /*
  493.              * Create a stack of deferred objects for each item that is not
  494.              * yet available, and invoke the supplied callback when they are.
  495.              */
  496.             $.when.apply( $, $.map( ids, function( id ) {
  497.                 if ( self.has( id ) )
  498.                     return;
  499.  
  500.                 /*
  501.                  * The requested item is not available yet, create a deferred
  502.                  * object to resolve when it becomes available.
  503.                  */
  504.                 return self._deferreds[ id ] = self._deferreds[ id ] || $.Deferred();
  505.             })).done( function() {
  506.                 var values = $.map( ids, function( id ) {
  507.                         return self( id );
  508.                     });
  509.  
  510.                 // If a value is missing, we've used at least one expired deferred.
  511.                 // Call Values.when again to generate a new deferred.
  512.                 if ( values.length !== ids.length ) {
  513.                     // ids.push( callback );
  514.                     self.when.apply( self, ids ).done( function() {
  515.                         dfd.resolveWith( self, values );
  516.                     });
  517.                     return;
  518.                 }
  519.  
  520.                 dfd.resolveWith( self, values );
  521.             });
  522.  
  523.             return dfd.promise();
  524.         },
  525.  
  526.         /**
  527.          * A helper function to propagate a 'change' event from an item
  528.          * to the collection itself.
  529.          */
  530.         _change: function() {
  531.             this.parent.trigger( 'change', this );
  532.         }
  533.     });
  534.  
  535.     // Create a global events bus on the Customizer.
  536.     $.extend( api.Values.prototype, api.Events );
  537.  
  538.  
  539.     /**
  540.      * Cast a string to a jQuery collection if it isn't already.
  541.      *
  542.      * @param {string|jQuery collection} element
  543.      */
  544.     api.ensure = function( element ) {
  545.         return typeof element == 'string' ? $( element ) : element;
  546.     };
  547.  
  548.     /**
  549.      * An observable value that syncs with an element.
  550.      *
  551.      * Handles inputs, selects, and textareas by default.
  552.      *
  553.      * @memberOf wp.customize
  554.      * @alias wp.customize.Element
  555.      *
  556.      * @constructor
  557.      * @augments wp.customize.Value
  558.      * @augments wp.customize.Class
  559.      */
  560.     api.Element = api.Value.extend(/** @lends wp.customize.Element */{
  561.         initialize: function( element, options ) {
  562.             var self = this,
  563.                 synchronizer = api.Element.synchronizer.html,
  564.                 type, update, refresh;
  565.  
  566.             this.element = api.ensure( element );
  567.             this.events = '';
  568.  
  569.             if ( this.element.is( 'input, select, textarea' ) ) {
  570.                 type = this.element.prop( 'type' );
  571.                 this.events += ' change input';
  572.                 synchronizer = api.Element.synchronizer.val;
  573.  
  574.                 if ( this.element.is( 'input' ) && api.Element.synchronizer[ type ] ) {
  575.                     synchronizer = api.Element.synchronizer[ type ];
  576.                 }
  577.             }
  578.  
  579.             api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) );
  580.             this._value = this.get();
  581.  
  582.             update = this.update;
  583.             refresh = this.refresh;
  584.  
  585.             this.update = function( to ) {
  586.                 if ( to !== refresh.call( self ) ) {
  587.                     update.apply( this, arguments );
  588.                 }
  589.             };
  590.             this.refresh = function() {
  591.                 self.set( refresh.call( self ) );
  592.             };
  593.  
  594.             this.bind( this.update );
  595.             this.element.bind( this.events, this.refresh );
  596.         },
  597.  
  598.         find: function( selector ) {
  599.             return $( selector, this.element );
  600.         },
  601.  
  602.         refresh: function() {},
  603.  
  604.         update: function() {}
  605.     });
  606.  
  607.     api.Element.synchronizer = {};
  608.  
  609.     $.each( [ 'html', 'val' ], function( index, method ) {
  610.         api.Element.synchronizer[ method ] = {
  611.             update: function( to ) {
  612.                 this.element[ method ]( to );
  613.             },
  614.             refresh: function() {
  615.                 return this.element[ method ]();
  616.             }
  617.         };
  618.     });
  619.  
  620.     api.Element.synchronizer.checkbox = {
  621.         update: function( to ) {
  622.             this.element.prop( 'checked', to );
  623.         },
  624.         refresh: function() {
  625.             return this.element.prop( 'checked' );
  626.         }
  627.     };
  628.  
  629.     api.Element.synchronizer.radio = {
  630.         update: function( to ) {
  631.             this.element.filter( function() {
  632.                 return this.value === to;
  633.             }).prop( 'checked', true );
  634.         },
  635.         refresh: function() {
  636.             return this.element.filter( ':checked' ).val();
  637.         }
  638.     };
  639.  
  640.     $.support.postMessage = !! window.postMessage;
  641.  
  642.     /**
  643.      * A communicator for sending data from one window to another over postMessage.
  644.      *
  645.      * @memberOf wp.customize
  646.      * @alias wp.customize.Messenger
  647.      *
  648.      * @constructor
  649.      * @augments wp.customize.Class
  650.      * @mixes wp.customize.Events
  651.      */
  652.     api.Messenger = api.Class.extend(/** @lends wp.customize.Messenger.prototype */{
  653.         /**
  654.          * Create a new Value.
  655.          *
  656.          * @param  {string} key     Unique identifier.
  657.          * @param  {mixed}  initial Initial value.
  658.          * @param  {mixed}  options Options hash. Optional.
  659.          * @return {Value}          Class instance of the Value.
  660.          */
  661.         add: function( key, initial, options ) {
  662.             return this[ key ] = new api.Value( initial, options );
  663.         },
  664.  
  665.         /**
  666.          * Initialize Messenger.
  667.          *
  668.          * @param  {object} params - Parameters to configure the messenger.
  669.          *         {string} params.url - The URL to communicate with.
  670.          *         {window} params.targetWindow - The window instance to communicate with. Default window.parent.
  671.          *         {string} params.channel - If provided, will send the channel with each message and only accept messages a matching channel.
  672.          * @param  {object} options - Extend any instance parameter or method with this object.
  673.          */
  674.         initialize: function( params, options ) {
  675.             // Target the parent frame by default, but only if a parent frame exists.
  676.             var defaultTarget = window.parent === window ? null : window.parent;
  677.  
  678.             $.extend( this, options || {} );
  679.  
  680.             this.add( 'channel', params.channel );
  681.             this.add( 'url', params.url || '' );
  682.             this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) {
  683.                 var urlParser = document.createElement( 'a' );
  684.                 urlParser.href = to;
  685.                 // Port stripping needed by IE since it adds to host but not to event.origin.
  686.                 return urlParser.protocol + '//' + urlParser.host.replace( /:(80|443)$/, '' );
  687.             });
  688.  
  689.             // first add with no value
  690.             this.add( 'targetWindow', null );
  691.             // This avoids SecurityErrors when setting a window object in x-origin iframe'd scenarios.
  692.             this.targetWindow.set = function( to ) {
  693.                 var from = this._value;
  694.  
  695.                 to = this._setter.apply( this, arguments );
  696.                 to = this.validate( to );
  697.  
  698.                 if ( null === to || from === to ) {
  699.                     return this;
  700.                 }
  701.  
  702.                 this._value = to;
  703.                 this._dirty = true;
  704.  
  705.                 this.callbacks.fireWith( this, [ to, from ] );
  706.  
  707.                 return this;
  708.             };
  709.             // now set it
  710.             this.targetWindow( params.targetWindow || defaultTarget );
  711.  
  712.  
  713.             // Since we want jQuery to treat the receive function as unique
  714.             // to this instance, we give the function a new guid.
  715.             //
  716.             // This will prevent every Messenger's receive function from being
  717.             // unbound when calling $.off( 'message', this.receive );
  718.             this.receive = $.proxy( this.receive, this );
  719.             this.receive.guid = $.guid++;
  720.  
  721.             $( window ).on( 'message', this.receive );
  722.         },
  723.  
  724.         destroy: function() {
  725.             $( window ).off( 'message', this.receive );
  726.         },
  727.  
  728.         /**
  729.          * Receive data from the other window.
  730.          *
  731.          * @param  {jQuery.Event} event Event with embedded data.
  732.          */
  733.         receive: function( event ) {
  734.             var message;
  735.  
  736.             event = event.originalEvent;
  737.  
  738.             if ( ! this.targetWindow || ! this.targetWindow() ) {
  739.                 return;
  740.             }
  741.  
  742.             // Check to make sure the origin is valid.
  743.             if ( this.origin() && event.origin !== this.origin() )
  744.                 return;
  745.  
  746.             // Ensure we have a string that's JSON.parse-able
  747.             if ( typeof event.data !== 'string' || event.data[0] !== '{' ) {
  748.                 return;
  749.             }
  750.  
  751.             message = JSON.parse( event.data );
  752.  
  753.             // Check required message properties.
  754.             if ( ! message || ! message.id || typeof message.data === 'undefined' )
  755.                 return;
  756.  
  757.             // Check if channel names match.
  758.             if ( ( message.channel || this.channel() ) && this.channel() !== message.channel )
  759.                 return;
  760.  
  761.             this.trigger( message.id, message.data );
  762.         },
  763.  
  764.         /**
  765.          * Send data to the other window.
  766.          *
  767.          * @param  {string} id   The event name.
  768.          * @param  {object} data Data.
  769.          */
  770.         send: function( id, data ) {
  771.             var message;
  772.  
  773.             data = typeof data === 'undefined' ? null : data;
  774.  
  775.             if ( ! this.url() || ! this.targetWindow() )
  776.                 return;
  777.  
  778.             message = { id: id, data: data };
  779.             if ( this.channel() )
  780.                 message.channel = this.channel();
  781.  
  782.             this.targetWindow().postMessage( JSON.stringify( message ), this.origin() );
  783.         }
  784.     });
  785.  
  786.     // Add the Events mixin to api.Messenger.
  787.     $.extend( api.Messenger.prototype, api.Events );
  788.  
  789.     /**
  790.      * Notification.
  791.      *
  792.      * @class
  793.      * @augments wp.customize.Class
  794.      * @since 4.6.0
  795.      *
  796.      * @memberOf wp.customize
  797.      * @alias wp.customize.Notification
  798.      *
  799.      * @param {string}  code - The error code.
  800.      * @param {object}  params - Params.
  801.      * @param {string}  params.message=null - The error message.
  802.      * @param {string}  [params.type=error] - The notification type.
  803.      * @param {boolean} [params.fromServer=false] - Whether the notification was server-sent.
  804.      * @param {string}  [params.setting=null] - The setting ID that the notification is related to.
  805.      * @param {*}       [params.data=null] - Any additional data.
  806.      */
  807.     api.Notification = api.Class.extend(/** @lends wp.customize.Notification.prototype */{
  808.  
  809.         /**
  810.          * Template function for rendering the notification.
  811.          *
  812.          * This will be populated with template option or else it will be populated with template from the ID.
  813.          *
  814.          * @since 4.9.0
  815.          * @var {Function}
  816.          */
  817.         template: null,
  818.  
  819.         /**
  820.          * ID for the template to render the notification.
  821.          *
  822.          * @since 4.9.0
  823.          * @var {string}
  824.          */
  825.         templateId: 'customize-notification',
  826.  
  827.         /**
  828.          * Additional class names to add to the notification container.
  829.          *
  830.          * @since 4.9.0
  831.          * @var {string}
  832.          */
  833.         containerClasses: '',
  834.  
  835.         /**
  836.          * Initialize notification.
  837.          *
  838.          * @since 4.9.0
  839.          *
  840.          * @param {string}   code - Notification code.
  841.          * @param {object}   params - Notification parameters.
  842.          * @param {string}   params.message - Message.
  843.          * @param {string}   [params.type=error] - Type.
  844.          * @param {string}   [params.setting] - Related setting ID.
  845.          * @param {Function} [params.template] - Function for rendering template. If not provided, this will come from templateId.
  846.          * @param {string}   [params.templateId] - ID for template to render the notification.
  847.          * @param {string}   [params.containerClasses] - Additional class names to add to the notification container.
  848.          * @param {boolean}  [params.dismissible] - Whether the notification can be dismissed.
  849.          */
  850.         initialize: function( code, params ) {
  851.             var _params;
  852.             this.code = code;
  853.             _params = _.extend(
  854.                 {
  855.                     message: null,
  856.                     type: 'error',
  857.                     fromServer: false,
  858.                     data: null,
  859.                     setting: null,
  860.                     template: null,
  861.                     dismissible: false,
  862.                     containerClasses: ''
  863.                 },
  864.                 params
  865.             );
  866.             delete _params.code;
  867.             _.extend( this, _params );
  868.         },
  869.  
  870.         /**
  871.          * Render the notification.
  872.          *
  873.          * @since 4.9.0
  874.          *
  875.          * @returns {jQuery} Notification container element.
  876.          */
  877.         render: function() {
  878.             var notification = this, container, data;
  879.             if ( ! notification.template ) {
  880.                 notification.template = wp.template( notification.templateId );
  881.             }
  882.             data = _.extend( {}, notification, {
  883.                 alt: notification.parent && notification.parent.alt
  884.             } );
  885.             container = $( notification.template( data ) );
  886.  
  887.             if ( notification.dismissible ) {
  888.                 container.find( '.notice-dismiss' ).on( 'click keydown', function( event ) {
  889.                     if ( 'keydown' === event.type && 13 !== event.which ) {
  890.                         return;
  891.                     }
  892.  
  893.                     if ( notification.parent ) {
  894.                         notification.parent.remove( notification.code );
  895.                     } else {
  896.                         container.remove();
  897.                     }
  898.                 });
  899.             }
  900.  
  901.             return container;
  902.         }
  903.     });
  904.  
  905.     // The main API object is also a collection of all customizer settings.
  906.     api = $.extend( new api.Values(), api );
  907.  
  908.     /**
  909.      * Get all customize settings.
  910.      *
  911.      * @memberOf wp.customize
  912.      *
  913.      * @return {object}
  914.      */
  915.     api.get = function() {
  916.         var result = {};
  917.  
  918.         this.each( function( obj, key ) {
  919.             result[ key ] = obj.get();
  920.         });
  921.  
  922.         return result;
  923.     };
  924.  
  925.     /**
  926.      * Utility function namespace
  927.      *
  928.      * @namespace wp.customize.utils
  929.      */
  930.     api.utils = {};
  931.  
  932.     /**
  933.      * Parse query string.
  934.      *
  935.      * @since 4.7.0
  936.      * @access public
  937.      * @memberOf wp.customize.utils
  938.      *
  939.      * @param {string} queryString Query string.
  940.      * @returns {object} Parsed query string.
  941.      */
  942.     api.utils.parseQueryString = function parseQueryString( queryString ) {
  943.         var queryParams = {};
  944.         _.each( queryString.split( '&' ), function( pair ) {
  945.             var parts, key, value;
  946.             parts = pair.split( '=', 2 );
  947.             if ( ! parts[0] ) {
  948.                 return;
  949.             }
  950.             key = decodeURIComponent( parts[0].replace( /\+/g, ' ' ) );
  951.             key = key.replace( / /g, '_' ); // What PHP does.
  952.             if ( _.isUndefined( parts[1] ) ) {
  953.                 value = null;
  954.             } else {
  955.                 value = decodeURIComponent( parts[1].replace( /\+/g, ' ' ) );
  956.             }
  957.             queryParams[ key ] = value;
  958.         } );
  959.         return queryParams;
  960.     };
  961.  
  962.     /**
  963.      * Expose the API publicly on window.wp.customize
  964.      *
  965.      * @namespace wp.customize
  966.      */
  967.     exports.customize = api;
  968. })( wp, jQuery );
  969.