ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

customize-base.js (25758B)


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