2012-10-05 20:43:36 -04:00
|
|
|
window.wp = window.wp || {};
|
2012-08-22 20:04:18 -04:00
|
|
|
|
|
|
|
(function( exports, $ ){
|
2014-08-08 18:10:15 -04:00
|
|
|
var api = {}, ctor, inherits,
|
2012-08-22 20:04:18 -04:00
|
|
|
slice = Array.prototype.slice;
|
|
|
|
|
|
|
|
// Shared empty constructor function to aid in prototype-chain creation.
|
|
|
|
ctor = function() {};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
|
|
|
* Helper function to correctly set up the prototype chain, for subclasses.
|
|
|
|
* Similar to `goog.inherits`, but uses a hash of prototype properties and
|
|
|
|
* class properties to be extended.
|
|
|
|
*
|
|
|
|
* @param object parent Parent class constructor to inherit from.
|
|
|
|
* @param object protoProps Properties to apply to the prototype for use as class instance properties.
|
|
|
|
* @param object staticProps Properties to apply directly to the class constructor.
|
|
|
|
* @return child The subclassed constructor.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
inherits = function( parent, protoProps, staticProps ) {
|
|
|
|
var child;
|
|
|
|
|
|
|
|
// The constructor function for the new subclass is either defined by you
|
|
|
|
// (the "constructor" property in your `extend` definition), or defaulted
|
|
|
|
// by us to simply call `super()`.
|
|
|
|
if ( protoProps && protoProps.hasOwnProperty( 'constructor' ) ) {
|
|
|
|
child = protoProps.constructor;
|
|
|
|
} else {
|
|
|
|
child = function() {
|
|
|
|
// Storing the result `super()` before returning the value
|
|
|
|
// prevents a bug in Opera where, if the constructor returns
|
|
|
|
// a function, Opera will reject the return value in favor of
|
|
|
|
// the original object. This causes all sorts of trouble.
|
|
|
|
var result = parent.apply( this, arguments );
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inherit class (static) properties from parent.
|
|
|
|
$.extend( child, parent );
|
|
|
|
|
|
|
|
// Set the prototype chain to inherit from `parent`, without calling
|
|
|
|
// `parent`'s constructor function.
|
|
|
|
ctor.prototype = parent.prototype;
|
|
|
|
child.prototype = new ctor();
|
|
|
|
|
|
|
|
// Add prototype properties (instance properties) to the subclass,
|
|
|
|
// if supplied.
|
|
|
|
if ( protoProps )
|
|
|
|
$.extend( child.prototype, protoProps );
|
|
|
|
|
|
|
|
// Add static properties to the constructor function, if supplied.
|
|
|
|
if ( staticProps )
|
|
|
|
$.extend( child, staticProps );
|
|
|
|
|
|
|
|
// Correctly set child's `prototype.constructor`.
|
|
|
|
child.prototype.constructor = child;
|
|
|
|
|
|
|
|
// Set a convenience property in case the parent's prototype is needed later.
|
|
|
|
child.__super__ = parent.prototype;
|
|
|
|
|
|
|
|
return child;
|
|
|
|
};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
|
|
|
* Base class for object inheritance.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Class = function( applicator, argsArray, options ) {
|
|
|
|
var magic, args = arguments;
|
|
|
|
|
|
|
|
if ( applicator && argsArray && api.Class.applicator === applicator ) {
|
|
|
|
args = argsArray;
|
|
|
|
$.extend( this, options || {} );
|
|
|
|
}
|
|
|
|
|
|
|
|
magic = this;
|
2015-09-01 00:15:22 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the class has a method called "instance",
|
|
|
|
* the return value from the class' constructor will be a function that
|
2015-09-05 15:53:24 -04:00
|
|
|
* calls the "instance" method.
|
|
|
|
*
|
|
|
|
* It is also an object that has properties and methods inside it.
|
2015-09-01 00:15:22 -04:00
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
if ( this.instance ) {
|
|
|
|
magic = function() {
|
|
|
|
return magic.instance.apply( magic, arguments );
|
|
|
|
};
|
|
|
|
|
|
|
|
$.extend( magic, this );
|
|
|
|
}
|
|
|
|
|
|
|
|
magic.initialize.apply( magic, args );
|
|
|
|
return magic;
|
|
|
|
};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
|
|
|
* Creates a subclass of the class.
|
|
|
|
*
|
|
|
|
* @param object protoProps Properties to apply to the prototype.
|
|
|
|
* @param object staticProps Properties to apply directly to the class.
|
|
|
|
* @return child The subclass.
|
|
|
|
*/
|
|
|
|
api.Class.extend = function( protoProps, classProps ) {
|
|
|
|
var child = inherits( this, protoProps, classProps );
|
|
|
|
child.extend = this.extend;
|
|
|
|
return child;
|
|
|
|
};
|
|
|
|
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Class.applicator = {};
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/**
|
|
|
|
* Initialize a class instance.
|
|
|
|
*
|
|
|
|
* Override this function in a subclass as needed.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Class.prototype.initialize = function() {};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Checks whether a given instance extended a constructor.
|
|
|
|
*
|
|
|
|
* The magic surrounding the instance parameter causes the instanceof
|
|
|
|
* keyword to return inaccurate results; it defaults to the function's
|
|
|
|
* prototype instead of the constructor chain. Hence this function.
|
|
|
|
*/
|
|
|
|
api.Class.prototype.extended = function( constructor ) {
|
|
|
|
var proto = this;
|
|
|
|
|
|
|
|
while ( typeof proto.constructor !== 'undefined' ) {
|
|
|
|
if ( proto.constructor === constructor )
|
|
|
|
return true;
|
|
|
|
if ( typeof proto.constructor.__super__ === 'undefined' )
|
|
|
|
return false;
|
|
|
|
proto = proto.constructor.__super__;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
|
|
|
* An events manager object, offering the ability to bind to and trigger events.
|
|
|
|
*
|
|
|
|
* Used as a mixin.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Events = {
|
|
|
|
trigger: function( id ) {
|
|
|
|
if ( this.topics && this.topics[ id ] )
|
|
|
|
this.topics[ id ].fireWith( this, slice.call( arguments, 1 ) );
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2013-11-14 23:42:09 -05:00
|
|
|
bind: function( id ) {
|
2012-08-22 20:04:18 -04:00
|
|
|
this.topics = this.topics || {};
|
|
|
|
this.topics[ id ] = this.topics[ id ] || $.Callbacks();
|
|
|
|
this.topics[ id ].add.apply( this.topics[ id ], slice.call( arguments, 1 ) );
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2013-11-14 23:42:09 -05:00
|
|
|
unbind: function( id ) {
|
2012-08-22 20:04:18 -04:00
|
|
|
if ( this.topics && this.topics[ id ] )
|
|
|
|
this.topics[ id ].remove.apply( this.topics[ id ], slice.call( arguments, 1 ) );
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
2012-08-22 20:04:18 -04:00
|
|
|
* Observable values that support two-way binding.
|
2014-08-08 18:10:15 -04:00
|
|
|
*
|
|
|
|
* @constuctor
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Value = api.Class.extend({
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* @param {mixed} initial The initial value.
|
|
|
|
* @param {object} options
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
initialize: function( initial, options ) {
|
|
|
|
this._value = initial; // @todo: potentially change this to a this.set() call.
|
|
|
|
this.callbacks = $.Callbacks();
|
2014-10-15 13:50:18 -04:00
|
|
|
this._dirty = false;
|
2012-08-22 20:04:18 -04:00
|
|
|
|
|
|
|
$.extend( this, options || {} );
|
|
|
|
|
|
|
|
this.set = $.proxy( this.set, this );
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Magic. Returns a function that will become the instance.
|
|
|
|
* Set to null to prevent the instance from extending a function.
|
|
|
|
*/
|
|
|
|
instance: function() {
|
|
|
|
return arguments.length ? this.set.apply( this, arguments ) : this.get();
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* Get the value.
|
|
|
|
*
|
|
|
|
* @return {mixed}
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
get: function() {
|
|
|
|
return this._value;
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* Set the value and trigger all bound callbacks.
|
|
|
|
*
|
|
|
|
* @param {object} to New value.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
set: function( to ) {
|
|
|
|
var from = this._value;
|
|
|
|
|
|
|
|
to = this._setter.apply( this, arguments );
|
|
|
|
to = this.validate( to );
|
|
|
|
|
|
|
|
// Bail if the sanitized value is null or unchanged.
|
Improve/introduce Customizer JavaScript models for Controls, Sections, and Panels.
* Introduce models for panels and sections.
* Introduce API to expand and focus a control, section or panel.
* Allow deep-linking to panels, sections, and controls inside of the Customizer.
* Clean up `accordion.js`, removing all Customizer-specific logic.
* Add initial unit tests for `wp.customize.Class` in `customize-base.js`.
https://make.wordpress.org/core/2014/10/27/toward-a-complete-javascript-api-for-the-customizer/ provides an overview of how to use the JavaScript API.
props westonruter, celloexpressions, ryankienstra.
see #28032, #28579, #28580, #28650, #28709, #29758.
fixes #29529.
Built from https://develop.svn.wordpress.org/trunk@30102
git-svn-id: http://core.svn.wordpress.org/trunk@30102 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2014-10-29 18:51:22 -04:00
|
|
|
if ( null === to || _.isEqual( from, to ) ) {
|
2012-08-22 20:04:18 -04:00
|
|
|
return this;
|
Improve/introduce Customizer JavaScript models for Controls, Sections, and Panels.
* Introduce models for panels and sections.
* Introduce API to expand and focus a control, section or panel.
* Allow deep-linking to panels, sections, and controls inside of the Customizer.
* Clean up `accordion.js`, removing all Customizer-specific logic.
* Add initial unit tests for `wp.customize.Class` in `customize-base.js`.
https://make.wordpress.org/core/2014/10/27/toward-a-complete-javascript-api-for-the-customizer/ provides an overview of how to use the JavaScript API.
props westonruter, celloexpressions, ryankienstra.
see #28032, #28579, #28580, #28650, #28709, #29758.
fixes #29529.
Built from https://develop.svn.wordpress.org/trunk@30102
git-svn-id: http://core.svn.wordpress.org/trunk@30102 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2014-10-29 18:51:22 -04:00
|
|
|
}
|
2012-08-22 20:04:18 -04:00
|
|
|
|
|
|
|
this._value = to;
|
2014-10-15 13:50:18 -04:00
|
|
|
this._dirty = true;
|
2012-08-22 20:04:18 -04:00
|
|
|
|
|
|
|
this.callbacks.fireWith( this, [ to, from ] );
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
_setter: function( to ) {
|
|
|
|
return to;
|
|
|
|
},
|
|
|
|
|
|
|
|
setter: function( callback ) {
|
|
|
|
var from = this.get();
|
|
|
|
this._setter = callback;
|
|
|
|
// Temporarily clear value so setter can decide if it's valid.
|
|
|
|
this._value = null;
|
|
|
|
this.set( from );
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
resetSetter: function() {
|
|
|
|
this._setter = this.constructor.prototype._setter;
|
|
|
|
this.set( this.get() );
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
validate: function( value ) {
|
|
|
|
return value;
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* Bind a function to be invoked whenever the value changes.
|
|
|
|
*
|
|
|
|
* @param {...Function} A function, or multiple functions, to add to the callback stack.
|
|
|
|
*/
|
2013-11-14 23:42:09 -05:00
|
|
|
bind: function() {
|
2012-08-22 20:04:18 -04:00
|
|
|
this.callbacks.add.apply( this.callbacks, arguments );
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* Unbind a previously bound function.
|
|
|
|
*
|
|
|
|
* @param {...Function} A function, or multiple functions, to remove from the callback stack.
|
|
|
|
*/
|
2013-11-14 23:42:09 -05:00
|
|
|
unbind: function() {
|
2012-08-22 20:04:18 -04:00
|
|
|
this.callbacks.remove.apply( this.callbacks, arguments );
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
link: function() { // values*
|
|
|
|
var set = this.set;
|
|
|
|
$.each( arguments, function() {
|
|
|
|
this.bind( set );
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
unlink: function() { // values*
|
|
|
|
var set = this.set;
|
|
|
|
$.each( arguments, function() {
|
|
|
|
this.unbind( set );
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
sync: function() { // values*
|
|
|
|
var that = this;
|
|
|
|
$.each( arguments, function() {
|
|
|
|
that.link( this );
|
|
|
|
this.link( that );
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
unsync: function() { // values*
|
|
|
|
var that = this;
|
|
|
|
$.each( arguments, function() {
|
|
|
|
that.unlink( this );
|
|
|
|
this.unlink( that );
|
|
|
|
});
|
|
|
|
return this;
|
2013-12-04 15:18:11 -05:00
|
|
|
}
|
2012-08-22 20:04:18 -04:00
|
|
|
});
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
2012-08-22 20:04:18 -04:00
|
|
|
* A collection of observable values.
|
2014-08-08 18:10:15 -04:00
|
|
|
*
|
|
|
|
* @constuctor
|
|
|
|
* @augments wp.customize.Class
|
|
|
|
* @mixes wp.customize.Events
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Values = api.Class.extend({
|
2015-09-05 15:53:24 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The default constructor for items of the collection.
|
|
|
|
*
|
|
|
|
* @type {object}
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
defaultConstructor: api.Value,
|
|
|
|
|
|
|
|
initialize: function( options ) {
|
|
|
|
$.extend( this, options || {} );
|
|
|
|
|
|
|
|
this._value = {};
|
|
|
|
this._deferreds = {};
|
|
|
|
},
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/**
|
|
|
|
* Get the instance of an item from the collection if only ID is specified.
|
|
|
|
*
|
|
|
|
* If more than one argument is supplied, all are expected to be IDs and
|
|
|
|
* the last to be a function callback that will be invoked when the requested
|
|
|
|
* items are available.
|
|
|
|
*
|
|
|
|
* @see {api.Values.when}
|
|
|
|
*
|
|
|
|
* @param {string} id ID of the item.
|
|
|
|
* @param {...} Zero or more IDs of items to wait for and a callback
|
|
|
|
* function to invoke when they're available. Optional.
|
|
|
|
* @return {mixed} The item instance if only one ID was supplied.
|
|
|
|
* A Deferred Promise object if a callback function is supplied.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
instance: function( id ) {
|
|
|
|
if ( arguments.length === 1 )
|
|
|
|
return this.value( id );
|
|
|
|
|
|
|
|
return this.when.apply( this, arguments );
|
|
|
|
},
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/**
|
|
|
|
* Get the instance of an item.
|
|
|
|
*
|
|
|
|
* @param {string} id The ID of the item.
|
|
|
|
* @return {[type]} [description]
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
value: function( id ) {
|
|
|
|
return this._value[ id ];
|
|
|
|
},
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/**
|
|
|
|
* Whether the collection has an item with the given ID.
|
|
|
|
*
|
|
|
|
* @param {string} id The ID of the item to look for.
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
has: function( id ) {
|
|
|
|
return typeof this._value[ id ] !== 'undefined';
|
|
|
|
},
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/**
|
|
|
|
* Add an item to the collection.
|
|
|
|
*
|
|
|
|
* @param {string} id The ID of the item.
|
|
|
|
* @param {mixed} value The item instance.
|
|
|
|
* @return {mixed} The new item's instance.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
add: function( id, value ) {
|
|
|
|
if ( this.has( id ) )
|
|
|
|
return this.value( id );
|
|
|
|
|
|
|
|
this._value[ id ] = value;
|
|
|
|
value.parent = this;
|
2015-09-05 15:53:24 -04:00
|
|
|
|
|
|
|
// Propagate a 'change' event on an item up to the collection.
|
2012-08-22 20:04:18 -04:00
|
|
|
if ( value.extended( api.Value ) )
|
|
|
|
value.bind( this._change );
|
|
|
|
|
|
|
|
this.trigger( 'add', value );
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
// If a deferred object exists for this item,
|
|
|
|
// resolve it.
|
2012-08-22 20:04:18 -04:00
|
|
|
if ( this._deferreds[ id ] )
|
|
|
|
this._deferreds[ id ].resolve();
|
|
|
|
|
|
|
|
return this._value[ id ];
|
|
|
|
},
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/**
|
|
|
|
* Create a new item of the collection using the collection's default constructor
|
|
|
|
* and store it in the collection.
|
|
|
|
*
|
|
|
|
* @param {string} id The ID of the item.
|
|
|
|
* @param {mixed} value Any extra arguments are passed into the item's initialize method.
|
|
|
|
* @return {mixed} The new item's instance.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
create: function( id ) {
|
|
|
|
return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) );
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* Iterate over all items in the collection invoking the provided callback.
|
|
|
|
*
|
|
|
|
* @param {Function} callback Function to invoke.
|
|
|
|
* @param {object} context Object context to invoke the function with. Optional.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
each: function( callback, context ) {
|
|
|
|
context = typeof context === 'undefined' ? this : context;
|
|
|
|
|
|
|
|
$.each( this._value, function( key, obj ) {
|
|
|
|
callback.call( context, obj, key );
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/**
|
|
|
|
* Remove an item from the collection.
|
|
|
|
*
|
|
|
|
* @param {string} id The ID of the item to remove.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
remove: function( id ) {
|
|
|
|
var value;
|
|
|
|
|
|
|
|
if ( this.has( id ) ) {
|
|
|
|
value = this.value( id );
|
|
|
|
this.trigger( 'remove', value );
|
|
|
|
if ( value.extended( api.Value ) )
|
|
|
|
value.unbind( this._change );
|
|
|
|
delete value.parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete this._value[ id ];
|
|
|
|
delete this._deferreds[ id ];
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Runs a callback once all requested values exist.
|
|
|
|
*
|
|
|
|
* when( ids*, [callback] );
|
|
|
|
*
|
|
|
|
* For example:
|
|
|
|
* when( id1, id2, id3, function( value1, value2, value3 ) {} );
|
|
|
|
*
|
|
|
|
* @returns $.Deferred.promise();
|
|
|
|
*/
|
|
|
|
when: function() {
|
|
|
|
var self = this,
|
|
|
|
ids = slice.call( arguments ),
|
|
|
|
dfd = $.Deferred();
|
|
|
|
|
|
|
|
// If the last argument is a callback, bind it to .done()
|
|
|
|
if ( $.isFunction( ids[ ids.length - 1 ] ) )
|
|
|
|
dfd.done( ids.pop() );
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/*
|
|
|
|
* Create a stack of deferred objects for each item that is not
|
|
|
|
* yet available, and invoke the supplied callback when they are.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
$.when.apply( $, $.map( ids, function( id ) {
|
|
|
|
if ( self.has( id ) )
|
|
|
|
return;
|
|
|
|
|
2015-09-01 00:15:22 -04:00
|
|
|
/*
|
|
|
|
* The requested item is not available yet, create a deferred
|
|
|
|
* object to resolve when it becomes available.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
return self._deferreds[ id ] = self._deferreds[ id ] || $.Deferred();
|
|
|
|
})).done( function() {
|
|
|
|
var values = $.map( ids, function( id ) {
|
|
|
|
return self( id );
|
|
|
|
});
|
|
|
|
|
|
|
|
// If a value is missing, we've used at least one expired deferred.
|
|
|
|
// Call Values.when again to generate a new deferred.
|
|
|
|
if ( values.length !== ids.length ) {
|
|
|
|
// ids.push( callback );
|
|
|
|
self.when.apply( self, ids ).done( function() {
|
|
|
|
dfd.resolveWith( self, values );
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dfd.resolveWith( self, values );
|
|
|
|
});
|
|
|
|
|
|
|
|
return dfd.promise();
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* A helper function to propagate a 'change' event from an item
|
|
|
|
* to the collection itself.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
_change: function() {
|
|
|
|
this.parent.trigger( 'change', this );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
// Create a global events bus on the Customizer.
|
2012-08-22 20:04:18 -04:00
|
|
|
$.extend( api.Values.prototype, api.Events );
|
|
|
|
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
|
|
|
* Cast a string to a jQuery collection if it isn't already.
|
|
|
|
*
|
|
|
|
* @param {string|jQuery collection} element
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.ensure = function( element ) {
|
|
|
|
return typeof element == 'string' ? $( element ) : element;
|
|
|
|
};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
|
|
|
* An observable value that syncs with an element.
|
|
|
|
*
|
|
|
|
* Handles inputs, selects, and textareas by default.
|
|
|
|
*
|
|
|
|
* @constuctor
|
|
|
|
* @augments wp.customize.Value
|
|
|
|
* @augments wp.customize.Class
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Element = api.Value.extend({
|
|
|
|
initialize: function( element, options ) {
|
|
|
|
var self = this,
|
|
|
|
synchronizer = api.Element.synchronizer.html,
|
|
|
|
type, update, refresh;
|
|
|
|
|
|
|
|
this.element = api.ensure( element );
|
|
|
|
this.events = '';
|
|
|
|
|
|
|
|
if ( this.element.is('input, select, textarea') ) {
|
|
|
|
this.events += 'change';
|
|
|
|
synchronizer = api.Element.synchronizer.val;
|
|
|
|
|
|
|
|
if ( this.element.is('input') ) {
|
|
|
|
type = this.element.prop('type');
|
2014-11-03 16:49:23 -05:00
|
|
|
if ( api.Element.synchronizer[ type ] ) {
|
2012-08-22 20:04:18 -04:00
|
|
|
synchronizer = api.Element.synchronizer[ type ];
|
2014-11-03 16:49:23 -05:00
|
|
|
}
|
|
|
|
if ( 'text' === type || 'password' === type ) {
|
2012-08-22 20:04:18 -04:00
|
|
|
this.events += ' keyup';
|
2014-11-03 16:49:23 -05:00
|
|
|
} else if ( 'range' === type ) {
|
|
|
|
this.events += ' input propertychange';
|
|
|
|
}
|
2012-08-22 20:04:18 -04:00
|
|
|
} else if ( this.element.is('textarea') ) {
|
|
|
|
this.events += ' keyup';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) );
|
|
|
|
this._value = this.get();
|
|
|
|
|
|
|
|
update = this.update;
|
|
|
|
refresh = this.refresh;
|
|
|
|
|
|
|
|
this.update = function( to ) {
|
|
|
|
if ( to !== refresh.call( self ) )
|
|
|
|
update.apply( this, arguments );
|
|
|
|
};
|
|
|
|
this.refresh = function() {
|
|
|
|
self.set( refresh.call( self ) );
|
|
|
|
};
|
|
|
|
|
|
|
|
this.bind( this.update );
|
|
|
|
this.element.bind( this.events, this.refresh );
|
|
|
|
},
|
|
|
|
|
|
|
|
find: function( selector ) {
|
|
|
|
return $( selector, this.element );
|
|
|
|
},
|
|
|
|
|
|
|
|
refresh: function() {},
|
|
|
|
|
|
|
|
update: function() {}
|
|
|
|
});
|
|
|
|
|
|
|
|
api.Element.synchronizer = {};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
$.each( [ 'html', 'val' ], function( index, method ) {
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Element.synchronizer[ method ] = {
|
|
|
|
update: function( to ) {
|
|
|
|
this.element[ method ]( to );
|
|
|
|
},
|
|
|
|
refresh: function() {
|
|
|
|
return this.element[ method ]();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
api.Element.synchronizer.checkbox = {
|
|
|
|
update: function( to ) {
|
|
|
|
this.element.prop( 'checked', to );
|
|
|
|
},
|
|
|
|
refresh: function() {
|
|
|
|
return this.element.prop( 'checked' );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
api.Element.synchronizer.radio = {
|
|
|
|
update: function( to ) {
|
|
|
|
this.element.filter( function() {
|
|
|
|
return this.value === to;
|
|
|
|
}).prop( 'checked', true );
|
|
|
|
},
|
|
|
|
refresh: function() {
|
|
|
|
return this.element.filter( ':checked' ).val();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
$.support.postMessage = !! window.postMessage;
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
2015-09-05 15:53:24 -04:00
|
|
|
* A communicator for sending data from one window to another over postMessage.
|
2014-08-08 18:10:15 -04:00
|
|
|
*
|
|
|
|
* @constuctor
|
|
|
|
* @augments wp.customize.Class
|
|
|
|
* @mixes wp.customize.Events
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.Messenger = api.Class.extend({
|
2014-08-08 18:10:15 -04:00
|
|
|
/**
|
|
|
|
* Create a new Value.
|
|
|
|
*
|
|
|
|
* @param {string} key Unique identifier.
|
|
|
|
* @param {mixed} initial Initial value.
|
|
|
|
* @param {mixed} options Options hash. Optional.
|
|
|
|
* @return {Value} Class instance of the Value.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
add: function( key, initial, options ) {
|
|
|
|
return this[ key ] = new api.Value( initial, options );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize Messenger.
|
|
|
|
*
|
|
|
|
* @param {object} params Parameters to configure the messenger.
|
|
|
|
* {string} .url The URL to communicate with.
|
|
|
|
* {window} .targetWindow The window instance to communicate with. Default window.parent.
|
|
|
|
* {string} .channel If provided, will send the channel with each message and only accept messages a matching channel.
|
|
|
|
* @param {object} options Extend any instance parameter or method with this object.
|
|
|
|
*/
|
|
|
|
initialize: function( params, options ) {
|
|
|
|
// Target the parent frame by default, but only if a parent frame exists.
|
|
|
|
var defaultTarget = window.parent == window ? null : window.parent;
|
|
|
|
|
|
|
|
$.extend( this, options || {} );
|
|
|
|
|
|
|
|
this.add( 'channel', params.channel );
|
|
|
|
this.add( 'url', params.url || '' );
|
|
|
|
this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) {
|
|
|
|
return to.replace( /([^:]+:\/\/[^\/]+).*/, '$1' );
|
|
|
|
});
|
|
|
|
|
2015-03-25 19:36:27 -04:00
|
|
|
// first add with no value
|
|
|
|
this.add( 'targetWindow', null );
|
|
|
|
// This avoids SecurityErrors when setting a window object in x-origin iframe'd scenarios.
|
|
|
|
this.targetWindow.set = function( to ) {
|
|
|
|
var from = this._value;
|
|
|
|
|
|
|
|
to = this._setter.apply( this, arguments );
|
|
|
|
to = this.validate( to );
|
|
|
|
|
|
|
|
if ( null === to || from === to ) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._value = to;
|
|
|
|
this._dirty = true;
|
|
|
|
|
|
|
|
this.callbacks.fireWith( this, [ to, from ] );
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
// now set it
|
|
|
|
this.targetWindow( params.targetWindow || defaultTarget );
|
|
|
|
|
|
|
|
|
2012-08-22 20:04:18 -04:00
|
|
|
// Since we want jQuery to treat the receive function as unique
|
|
|
|
// to this instance, we give the function a new guid.
|
|
|
|
//
|
|
|
|
// This will prevent every Messenger's receive function from being
|
|
|
|
// unbound when calling $.off( 'message', this.receive );
|
|
|
|
this.receive = $.proxy( this.receive, this );
|
|
|
|
this.receive.guid = $.guid++;
|
|
|
|
|
|
|
|
$( window ).on( 'message', this.receive );
|
|
|
|
},
|
|
|
|
|
|
|
|
destroy: function() {
|
|
|
|
$( window ).off( 'message', this.receive );
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* Receive data from the other window.
|
|
|
|
*
|
|
|
|
* @param {jQuery.Event} event Event with embedded data.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
receive: function( event ) {
|
|
|
|
var message;
|
|
|
|
|
|
|
|
event = event.originalEvent;
|
|
|
|
|
2016-02-18 22:47:27 -05:00
|
|
|
if ( ! this.targetWindow || ! this.targetWindow() ) {
|
2012-08-22 20:04:18 -04:00
|
|
|
return;
|
2016-02-18 22:47:27 -05:00
|
|
|
}
|
2012-08-22 20:04:18 -04:00
|
|
|
|
|
|
|
// Check to make sure the origin is valid.
|
|
|
|
if ( this.origin() && event.origin !== this.origin() )
|
|
|
|
return;
|
|
|
|
|
2013-12-02 17:49:09 -05:00
|
|
|
// Ensure we have a string that's JSON.parse-able
|
|
|
|
if ( typeof event.data !== 'string' || event.data[0] !== '{' ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-08-22 20:04:18 -04:00
|
|
|
message = JSON.parse( event.data );
|
|
|
|
|
|
|
|
// Check required message properties.
|
|
|
|
if ( ! message || ! message.id || typeof message.data === 'undefined' )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Check if channel names match.
|
|
|
|
if ( ( message.channel || this.channel() ) && this.channel() !== message.channel )
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.trigger( message.id, message.data );
|
|
|
|
},
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
/**
|
|
|
|
* Send data to the other window.
|
|
|
|
*
|
|
|
|
* @param {string} id The event name.
|
|
|
|
* @param {object} data Data.
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
send: function( id, data ) {
|
|
|
|
var message;
|
|
|
|
|
|
|
|
data = typeof data === 'undefined' ? null : data;
|
|
|
|
|
|
|
|
if ( ! this.url() || ! this.targetWindow() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
message = { id: id, data: data };
|
|
|
|
if ( this.channel() )
|
|
|
|
message.channel = this.channel();
|
|
|
|
|
|
|
|
this.targetWindow().postMessage( JSON.stringify( message ), this.origin() );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Add the Events mixin to api.Messenger.
|
|
|
|
$.extend( api.Messenger.prototype, api.Events );
|
|
|
|
|
2015-09-05 15:53:24 -04:00
|
|
|
// The main API object is also a collection of all customizer settings.
|
2012-08-22 20:04:18 -04:00
|
|
|
api = $.extend( new api.Values(), api );
|
2015-09-05 15:53:24 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all customize settings.
|
|
|
|
*
|
|
|
|
* @return {object}
|
|
|
|
*/
|
2012-08-22 20:04:18 -04:00
|
|
|
api.get = function() {
|
|
|
|
var result = {};
|
|
|
|
|
|
|
|
this.each( function( obj, key ) {
|
|
|
|
result[ key ] = obj.get();
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
2014-08-08 18:10:15 -04:00
|
|
|
// Expose the API publicly on window.wp.customize
|
2012-08-22 20:04:18 -04:00
|
|
|
exports.customize = api;
|
|
|
|
})( wp, jQuery );
|