wp/wp-includes/js/customize-base.js
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
       
     1 /** @namespace wp */
     1 window.wp = window.wp || {};
     2 window.wp = window.wp || {};
     2 
     3 
     3 (function( exports, $ ){
     4 (function( exports, $ ){
     4 	var api = {}, ctor, inherits,
     5 	var api = {}, ctor, inherits,
     5 		slice = Array.prototype.slice;
     6 		slice = Array.prototype.slice;
    72 			args = argsArray;
    73 			args = argsArray;
    73 			$.extend( this, options || {} );
    74 			$.extend( this, options || {} );
    74 		}
    75 		}
    75 
    76 
    76 		magic = this;
    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 		 */
    77 		if ( this.instance ) {
    86 		if ( this.instance ) {
    78 			magic = function() {
    87 			magic = function() {
    79 				return magic.instance.apply( magic, arguments );
    88 				return magic.instance.apply( magic, arguments );
    80 			};
    89 			};
    81 
    90 
    99 		return child;
   108 		return child;
   100 	};
   109 	};
   101 
   110 
   102 	api.Class.applicator = {};
   111 	api.Class.applicator = {};
   103 
   112 
       
   113 	/**
       
   114 	 * Initialize a class instance.
       
   115 	 *
       
   116 	 * Override this function in a subclass as needed.
       
   117 	 */
   104 	api.Class.prototype.initialize = function() {};
   118 	api.Class.prototype.initialize = function() {};
   105 
   119 
   106 	/*
   120 	/*
   107 	 * Checks whether a given instance extended a constructor.
   121 	 * Checks whether a given instance extended a constructor.
   108 	 *
   122 	 *
   150 	};
   164 	};
   151 
   165 
   152 	/**
   166 	/**
   153 	 * Observable values that support two-way binding.
   167 	 * Observable values that support two-way binding.
   154 	 *
   168 	 *
   155 	 * @constuctor
   169 	 * @memberOf wp.customize
   156 	 */
   170 	 * @alias wp.customize.Value
   157 	api.Value = api.Class.extend({
   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 		 */
   158 		initialize: function( initial, options ) {
   179 		initialize: function( initial, options ) {
   159 			this._value = initial; // @todo: potentially change this to a this.set() call.
   180 			this._value = initial; // @todo: potentially change this to a this.set() call.
   160 			this.callbacks = $.Callbacks();
   181 			this.callbacks = $.Callbacks();
   161 			this._dirty = false;
   182 			this._dirty = false;
   162 
   183 
   171 		 */
   192 		 */
   172 		instance: function() {
   193 		instance: function() {
   173 			return arguments.length ? this.set.apply( this, arguments ) : this.get();
   194 			return arguments.length ? this.set.apply( this, arguments ) : this.get();
   174 		},
   195 		},
   175 
   196 
       
   197 		/**
       
   198 		 * Get the value.
       
   199 		 *
       
   200 		 * @return {mixed}
       
   201 		 */
   176 		get: function() {
   202 		get: function() {
   177 			return this._value;
   203 			return this._value;
   178 		},
   204 		},
   179 
   205 
       
   206 		/**
       
   207 		 * Set the value and trigger all bound callbacks.
       
   208 		 *
       
   209 		 * @param {object} to New value.
       
   210 		 */
   180 		set: function( to ) {
   211 		set: function( to ) {
   181 			var from = this._value;
   212 			var from = this._value;
   182 
   213 
   183 			to = this._setter.apply( this, arguments );
   214 			to = this._setter.apply( this, arguments );
   184 			to = this.validate( to );
   215 			to = this.validate( to );
   217 
   248 
   218 		validate: function( value ) {
   249 		validate: function( value ) {
   219 			return value;
   250 			return value;
   220 		},
   251 		},
   221 
   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 		 */
   222 		bind: function() {
   258 		bind: function() {
   223 			this.callbacks.add.apply( this.callbacks, arguments );
   259 			this.callbacks.add.apply( this.callbacks, arguments );
   224 			return this;
   260 			return this;
   225 		},
   261 		},
   226 
   262 
       
   263 		/**
       
   264 		 * Unbind a previously bound function.
       
   265 		 *
       
   266 		 * @param {...Function} A function, or multiple functions, to remove from the callback stack.
       
   267 		 */
   227 		unbind: function() {
   268 		unbind: function() {
   228 			this.callbacks.remove.apply( this.callbacks, arguments );
   269 			this.callbacks.remove.apply( this.callbacks, arguments );
   229 			return this;
   270 			return this;
   230 		},
   271 		},
   231 
   272 
   265 	});
   306 	});
   266 
   307 
   267 	/**
   308 	/**
   268 	 * A collection of observable values.
   309 	 * A collection of observable values.
   269 	 *
   310 	 *
   270 	 * @constuctor
   311 	 * @memberOf wp.customize
       
   312 	 * @alias wp.customize.Values
       
   313 	 *
       
   314 	 * @constructor
   271 	 * @augments wp.customize.Class
   315 	 * @augments wp.customize.Class
   272 	 * @mixes wp.customize.Events
   316 	 * @mixes wp.customize.Events
   273 	 */
   317 	 */
   274 	api.Values = api.Class.extend({
   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 		 */
   275 		defaultConstructor: api.Value,
   325 		defaultConstructor: api.Value,
   276 
   326 
   277 		initialize: function( options ) {
   327 		initialize: function( options ) {
   278 			$.extend( this, options || {} );
   328 			$.extend( this, options || {} );
   279 
   329 
   280 			this._value = {};
   330 			this._value = {};
   281 			this._deferreds = {};
   331 			this._deferreds = {};
   282 		},
   332 		},
   283 
   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 		 */
   284 		instance: function( id ) {
   349 		instance: function( id ) {
   285 			if ( arguments.length === 1 )
   350 			if ( arguments.length === 1 )
   286 				return this.value( id );
   351 				return this.value( id );
   287 
   352 
   288 			return this.when.apply( this, arguments );
   353 			return this.when.apply( this, arguments );
   289 		},
   354 		},
   290 
   355 
       
   356 		/**
       
   357 		 * Get the instance of an item.
       
   358 		 *
       
   359 		 * @param  {string} id The ID of the item.
       
   360 		 * @return {[type]}    [description]
       
   361 		 */
   291 		value: function( id ) {
   362 		value: function( id ) {
   292 			return this._value[ id ];
   363 			return this._value[ id ];
   293 		},
   364 		},
   294 
   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 		 */
   295 		has: function( id ) {
   372 		has: function( id ) {
   296 			return typeof this._value[ id ] !== 'undefined';
   373 			return typeof this._value[ id ] !== 'undefined';
   297 		},
   374 		},
   298 
   375 
   299 		add: function( id, value ) {
   376 		/**
   300 			if ( this.has( id ) )
   377 		 * Add an item to the collection.
   301 				return this.value( id );
   378 		 *
   302 
   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.
   303 			this._value[ id ] = value;
   380 		 * @param {wp.customize.Class}        [itemObject] - The item instance when the first argument is a ID string.
   304 			value.parent = this;
   381 		 * @return {wp.customize.Class} The new item's instance, or an existing instance if already added.
   305 			if ( value.extended( api.Value ) )
   382 		 */
   306 				value.bind( this._change );
   383 		add: function( item, itemObject ) {
   307 
   384 			var collection = this, id, instance;
   308 			this.trigger( 'add', value );
   385 			if ( 'string' === typeof item ) {
   309 
   386 				id = item;
   310 			if ( this._deferreds[ id ] )
   387 				instance = itemObject;
   311 				this._deferreds[ id ].resolve();
   388 			} else {
   312 
   389 				if ( 'string' !== typeof item.id ) {
   313 			return this._value[ id ];
   390 					throw new Error( 'Unknown key' );
   314 		},
   391 				}
   315 
   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 		 */
   316 		create: function( id ) {
   427 		create: function( id ) {
   317 			return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) );
   428 			return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) );
   318 		},
   429 		},
   319 
   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 		 */
   320 		each: function( callback, context ) {
   437 		each: function( callback, context ) {
   321 			context = typeof context === 'undefined' ? this : context;
   438 			context = typeof context === 'undefined' ? this : context;
   322 
   439 
   323 			$.each( this._value, function( key, obj ) {
   440 			$.each( this._value, function( key, obj ) {
   324 				callback.call( context, obj, key );
   441 				callback.call( context, obj, key );
   325 			});
   442 			});
   326 		},
   443 		},
   327 
   444 
       
   445 		/**
       
   446 		 * Remove an item from the collection.
       
   447 		 *
       
   448 		 * @param  {string} id The ID of the item to remove.
       
   449 		 */
   328 		remove: function( id ) {
   450 		remove: function( id ) {
   329 			var value;
   451 			var value = this.value( id );
   330 
   452 
   331 			if ( this.has( id ) ) {
   453 			if ( value ) {
   332 				value = this.value( id );
   454 
       
   455 				// Trigger event right before the element is removed from the collection.
   333 				this.trigger( 'remove', value );
   456 				this.trigger( 'remove', value );
   334 				if ( value.extended( api.Value ) )
   457 
       
   458 				if ( value.extended( api.Value ) ) {
   335 					value.unbind( this._change );
   459 					value.unbind( this._change );
       
   460 				}
   336 				delete value.parent;
   461 				delete value.parent;
   337 			}
   462 			}
   338 
   463 
   339 			delete this._value[ id ];
   464 			delete this._value[ id ];
   340 			delete this._deferreds[ 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 			}
   341 		},
   471 		},
   342 
   472 
   343 		/**
   473 		/**
   344 		 * Runs a callback once all requested values exist.
   474 		 * Runs a callback once all requested values exist.
   345 		 *
   475 		 *
   357 
   487 
   358 			// If the last argument is a callback, bind it to .done()
   488 			// If the last argument is a callback, bind it to .done()
   359 			if ( $.isFunction( ids[ ids.length - 1 ] ) )
   489 			if ( $.isFunction( ids[ ids.length - 1 ] ) )
   360 				dfd.done( ids.pop() );
   490 				dfd.done( ids.pop() );
   361 
   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 			 */
   362 			$.when.apply( $, $.map( ids, function( id ) {
   496 			$.when.apply( $, $.map( ids, function( id ) {
   363 				if ( self.has( id ) )
   497 				if ( self.has( id ) )
   364 					return;
   498 					return;
   365 
   499 
       
   500 				/*
       
   501 				 * The requested item is not available yet, create a deferred
       
   502 				 * object to resolve when it becomes available.
       
   503 				 */
   366 				return self._deferreds[ id ] = self._deferreds[ id ] || $.Deferred();
   504 				return self._deferreds[ id ] = self._deferreds[ id ] || $.Deferred();
   367 			})).done( function() {
   505 			})).done( function() {
   368 				var values = $.map( ids, function( id ) {
   506 				var values = $.map( ids, function( id ) {
   369 						return self( id );
   507 						return self( id );
   370 					});
   508 					});
   383 			});
   521 			});
   384 
   522 
   385 			return dfd.promise();
   523 			return dfd.promise();
   386 		},
   524 		},
   387 
   525 
       
   526 		/**
       
   527 		 * A helper function to propagate a 'change' event from an item
       
   528 		 * to the collection itself.
       
   529 		 */
   388 		_change: function() {
   530 		_change: function() {
   389 			this.parent.trigger( 'change', this );
   531 			this.parent.trigger( 'change', this );
   390 		}
   532 		}
   391 	});
   533 	});
   392 
   534 
       
   535 	// Create a global events bus on the Customizer.
   393 	$.extend( api.Values.prototype, api.Events );
   536 	$.extend( api.Values.prototype, api.Events );
   394 
   537 
   395 
   538 
   396 	/**
   539 	/**
   397 	 * Cast a string to a jQuery collection if it isn't already.
   540 	 * Cast a string to a jQuery collection if it isn't already.
   405 	/**
   548 	/**
   406 	 * An observable value that syncs with an element.
   549 	 * An observable value that syncs with an element.
   407 	 *
   550 	 *
   408 	 * Handles inputs, selects, and textareas by default.
   551 	 * Handles inputs, selects, and textareas by default.
   409 	 *
   552 	 *
   410 	 * @constuctor
   553 	 * @memberOf wp.customize
       
   554 	 * @alias wp.customize.Element
       
   555 	 *
       
   556 	 * @constructor
   411 	 * @augments wp.customize.Value
   557 	 * @augments wp.customize.Value
   412 	 * @augments wp.customize.Class
   558 	 * @augments wp.customize.Class
   413 	 */
   559 	 */
   414 	api.Element = api.Value.extend({
   560 	api.Element = api.Value.extend(/** @lends wp.customize.Element */{
   415 		initialize: function( element, options ) {
   561 		initialize: function( element, options ) {
   416 			var self = this,
   562 			var self = this,
   417 				synchronizer = api.Element.synchronizer.html,
   563 				synchronizer = api.Element.synchronizer.html,
   418 				type, update, refresh;
   564 				type, update, refresh;
   419 
   565 
   420 			this.element = api.ensure( element );
   566 			this.element = api.ensure( element );
   421 			this.events = '';
   567 			this.events = '';
   422 
   568 
   423 			if ( this.element.is('input, select, textarea') ) {
   569 			if ( this.element.is( 'input, select, textarea' ) ) {
   424 				this.events += 'change';
   570 				type = this.element.prop( 'type' );
       
   571 				this.events += ' change input';
   425 				synchronizer = api.Element.synchronizer.val;
   572 				synchronizer = api.Element.synchronizer.val;
   426 
   573 
   427 				if ( this.element.is('input') ) {
   574 				if ( this.element.is( 'input' ) && api.Element.synchronizer[ type ] ) {
   428 					type = this.element.prop('type');
   575 					synchronizer = api.Element.synchronizer[ type ];
   429 					if ( api.Element.synchronizer[ type ] ) {
       
   430 						synchronizer = api.Element.synchronizer[ type ];
       
   431 					}
       
   432 					if ( 'text' === type || 'password' === type ) {
       
   433 						this.events += ' keyup';
       
   434 					} else if ( 'range' === type ) {
       
   435 						this.events += ' input propertychange';
       
   436 					}
       
   437 				} else if ( this.element.is('textarea') ) {
       
   438 					this.events += ' keyup';
       
   439 				}
   576 				}
   440 			}
   577 			}
   441 
   578 
   442 			api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) );
   579 			api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) );
   443 			this._value = this.get();
   580 			this._value = this.get();
   444 
   581 
   445 			update  = this.update;
   582 			update = this.update;
   446 			refresh = this.refresh;
   583 			refresh = this.refresh;
   447 
   584 
   448 			this.update = function( to ) {
   585 			this.update = function( to ) {
   449 				if ( to !== refresh.call( self ) )
   586 				if ( to !== refresh.call( self ) ) {
   450 					update.apply( this, arguments );
   587 					update.apply( this, arguments );
       
   588 				}
   451 			};
   589 			};
   452 			this.refresh = function() {
   590 			this.refresh = function() {
   453 				self.set( refresh.call( self ) );
   591 				self.set( refresh.call( self ) );
   454 			};
   592 			};
   455 
   593 
   500 	};
   638 	};
   501 
   639 
   502 	$.support.postMessage = !! window.postMessage;
   640 	$.support.postMessage = !! window.postMessage;
   503 
   641 
   504 	/**
   642 	/**
   505 	 * Messenger for postMessage.
   643 	 * A communicator for sending data from one window to another over postMessage.
   506 	 *
   644 	 *
   507 	 * @constuctor
   645 	 * @memberOf wp.customize
       
   646 	 * @alias wp.customize.Messenger
       
   647 	 *
       
   648 	 * @constructor
   508 	 * @augments wp.customize.Class
   649 	 * @augments wp.customize.Class
   509 	 * @mixes wp.customize.Events
   650 	 * @mixes wp.customize.Events
   510 	 */
   651 	 */
   511 	api.Messenger = api.Class.extend({
   652 	api.Messenger = api.Class.extend(/** @lends wp.customize.Messenger.prototype */{
   512 		/**
   653 		/**
   513 		 * Create a new Value.
   654 		 * Create a new Value.
   514 		 *
   655 		 *
   515 		 * @param  {string} key     Unique identifier.
   656 		 * @param  {string} key     Unique identifier.
   516 		 * @param  {mixed}  initial Initial value.
   657 		 * @param  {mixed}  initial Initial value.
   522 		},
   663 		},
   523 
   664 
   524 		/**
   665 		/**
   525 		 * Initialize Messenger.
   666 		 * Initialize Messenger.
   526 		 *
   667 		 *
   527 		 * @param  {object} params        Parameters to configure the messenger.
   668 		 * @param  {object} params - Parameters to configure the messenger.
   528 		 *         {string} .url          The URL to communicate with.
   669 		 *         {string} params.url - The URL to communicate with.
   529 		 *         {window} .targetWindow The window instance to communicate with. Default window.parent.
   670 		 *         {window} params.targetWindow - The window instance to communicate with. Default window.parent.
   530 		 *         {string} .channel      If provided, will send the channel with each message and only accept messages a matching channel.
   671 		 *         {string} params.channel - If provided, will send the channel with each message and only accept messages a matching channel.
   531 		 * @param  {object} options       Extend any instance parameter or method with this object.
   672 		 * @param  {object} options - Extend any instance parameter or method with this object.
   532 		 */
   673 		 */
   533 		initialize: function( params, options ) {
   674 		initialize: function( params, options ) {
   534 			// Target the parent frame by default, but only if a parent frame exists.
   675 			// Target the parent frame by default, but only if a parent frame exists.
   535 			var defaultTarget = window.parent == window ? null : window.parent;
   676 			var defaultTarget = window.parent === window ? null : window.parent;
   536 
   677 
   537 			$.extend( this, options || {} );
   678 			$.extend( this, options || {} );
   538 
   679 
   539 			this.add( 'channel', params.channel );
   680 			this.add( 'channel', params.channel );
   540 			this.add( 'url', params.url || '' );
   681 			this.add( 'url', params.url || '' );
   541 			this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) {
   682 			this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) {
   542 				return to.replace( /([^:]+:\/\/[^\/]+).*/, '$1' );
   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)$/, '' );
   543 			});
   687 			});
   544 
   688 
   545 			// first add with no value
   689 			// first add with no value
   546 			this.add( 'targetWindow', null );
   690 			this.add( 'targetWindow', null );
   547 			// This avoids SecurityErrors when setting a window object in x-origin iframe'd scenarios.
   691 			// This avoids SecurityErrors when setting a window object in x-origin iframe'd scenarios.
   579 
   723 
   580 		destroy: function() {
   724 		destroy: function() {
   581 			$( window ).off( 'message', this.receive );
   725 			$( window ).off( 'message', this.receive );
   582 		},
   726 		},
   583 
   727 
       
   728 		/**
       
   729 		 * Receive data from the other window.
       
   730 		 *
       
   731 		 * @param  {jQuery.Event} event Event with embedded data.
       
   732 		 */
   584 		receive: function( event ) {
   733 		receive: function( event ) {
   585 			var message;
   734 			var message;
   586 
   735 
   587 			event = event.originalEvent;
   736 			event = event.originalEvent;
   588 
   737 
   589 			if ( ! this.targetWindow() )
   738 			if ( ! this.targetWindow || ! this.targetWindow() ) {
   590 				return;
   739 				return;
       
   740 			}
   591 
   741 
   592 			// Check to make sure the origin is valid.
   742 			// Check to make sure the origin is valid.
   593 			if ( this.origin() && event.origin !== this.origin() )
   743 			if ( this.origin() && event.origin !== this.origin() )
   594 				return;
   744 				return;
   595 
   745 
   609 				return;
   759 				return;
   610 
   760 
   611 			this.trigger( message.id, message.data );
   761 			this.trigger( message.id, message.data );
   612 		},
   762 		},
   613 
   763 
       
   764 		/**
       
   765 		 * Send data to the other window.
       
   766 		 *
       
   767 		 * @param  {string} id   The event name.
       
   768 		 * @param  {object} data Data.
       
   769 		 */
   614 		send: function( id, data ) {
   770 		send: function( id, data ) {
   615 			var message;
   771 			var message;
   616 
   772 
   617 			data = typeof data === 'undefined' ? null : data;
   773 			data = typeof data === 'undefined' ? null : data;
   618 
   774 
   628 	});
   784 	});
   629 
   785 
   630 	// Add the Events mixin to api.Messenger.
   786 	// Add the Events mixin to api.Messenger.
   631 	$.extend( api.Messenger.prototype, api.Events );
   787 	$.extend( api.Messenger.prototype, api.Events );
   632 
   788 
   633 	// Core customize object.
   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.
   634 	api = $.extend( new api.Values(), api );
   906 	api = $.extend( new api.Values(), api );
       
   907 
       
   908 	/**
       
   909 	 * Get all customize settings.
       
   910 	 *
       
   911 	 * @memberOf wp.customize
       
   912 	 *
       
   913 	 * @return {object}
       
   914 	 */
   635 	api.get = function() {
   915 	api.get = function() {
   636 		var result = {};
   916 		var result = {};
   637 
   917 
   638 		this.each( function( obj, key ) {
   918 		this.each( function( obj, key ) {
   639 			result[ key ] = obj.get();
   919 			result[ key ] = obj.get();
   640 		});
   920 		});
   641 
   921 
   642 		return result;
   922 		return result;
   643 	};
   923 	};
   644 
   924 
   645 	// Expose the API publicly on window.wp.customize
   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 	 */
   646 	exports.customize = api;
   967 	exports.customize = api;
   647 })( wp, jQuery );
   968 })( wp, jQuery );