1 window.wp = window.wp || {}; |
1 window.wp = window.wp || {}; |
2 |
2 |
3 (function( exports, $ ){ |
3 (function( exports, $ ){ |
4 var api, extend, ctor, inherits, |
4 var api = {}, ctor, inherits, |
5 slice = Array.prototype.slice; |
5 slice = Array.prototype.slice; |
6 |
|
7 /* ===================================================================== |
|
8 * Micro-inheritance - thank you, backbone.js. |
|
9 * ===================================================================== */ |
|
10 |
|
11 extend = function( protoProps, classProps ) { |
|
12 var child = inherits( this, protoProps, classProps ); |
|
13 child.extend = this.extend; |
|
14 return child; |
|
15 }; |
|
16 |
6 |
17 // Shared empty constructor function to aid in prototype-chain creation. |
7 // Shared empty constructor function to aid in prototype-chain creation. |
18 ctor = function() {}; |
8 ctor = function() {}; |
19 |
9 |
20 // Helper function to correctly set up the prototype chain, for subclasses. |
10 /** |
21 // Similar to `goog.inherits`, but uses a hash of prototype properties and |
11 * Helper function to correctly set up the prototype chain, for subclasses. |
22 // class properties to be extended. |
12 * Similar to `goog.inherits`, but uses a hash of prototype properties and |
|
13 * class properties to be extended. |
|
14 * |
|
15 * @param object parent Parent class constructor to inherit from. |
|
16 * @param object protoProps Properties to apply to the prototype for use as class instance properties. |
|
17 * @param object staticProps Properties to apply directly to the class constructor. |
|
18 * @return child The subclassed constructor. |
|
19 */ |
23 inherits = function( parent, protoProps, staticProps ) { |
20 inherits = function( parent, protoProps, staticProps ) { |
24 var child; |
21 var child; |
25 |
22 |
26 // The constructor function for the new subclass is either defined by you |
23 // The constructor function for the new subclass is either defined by you |
27 // (the "constructor" property in your `extend` definition), or defaulted |
24 // (the "constructor" property in your `extend` definition), or defaulted |
63 child.__super__ = parent.prototype; |
60 child.__super__ = parent.prototype; |
64 |
61 |
65 return child; |
62 return child; |
66 }; |
63 }; |
67 |
64 |
68 api = {}; |
65 /** |
69 |
66 * Base class for object inheritance. |
70 /* ===================================================================== |
67 */ |
71 * Base class. |
|
72 * ===================================================================== */ |
|
73 |
|
74 api.Class = function( applicator, argsArray, options ) { |
68 api.Class = function( applicator, argsArray, options ) { |
75 var magic, args = arguments; |
69 var magic, args = arguments; |
76 |
70 |
77 if ( applicator && argsArray && api.Class.applicator === applicator ) { |
71 if ( applicator && argsArray && api.Class.applicator === applicator ) { |
78 args = argsArray; |
72 args = argsArray; |
88 $.extend( magic, this ); |
82 $.extend( magic, this ); |
89 } |
83 } |
90 |
84 |
91 magic.initialize.apply( magic, args ); |
85 magic.initialize.apply( magic, args ); |
92 return magic; |
86 return magic; |
|
87 }; |
|
88 |
|
89 /** |
|
90 * Creates a subclass of the class. |
|
91 * |
|
92 * @param object protoProps Properties to apply to the prototype. |
|
93 * @param object staticProps Properties to apply directly to the class. |
|
94 * @return child The subclass. |
|
95 */ |
|
96 api.Class.extend = function( protoProps, classProps ) { |
|
97 var child = inherits( this, protoProps, classProps ); |
|
98 child.extend = this.extend; |
|
99 return child; |
93 }; |
100 }; |
94 |
101 |
95 api.Class.applicator = {}; |
102 api.Class.applicator = {}; |
96 |
103 |
97 api.Class.prototype.initialize = function() {}; |
104 api.Class.prototype.initialize = function() {}; |
114 proto = proto.constructor.__super__; |
121 proto = proto.constructor.__super__; |
115 } |
122 } |
116 return false; |
123 return false; |
117 }; |
124 }; |
118 |
125 |
119 api.Class.extend = extend; |
126 /** |
120 |
127 * An events manager object, offering the ability to bind to and trigger events. |
121 /* ===================================================================== |
128 * |
122 * Events mixin. |
129 * Used as a mixin. |
123 * ===================================================================== */ |
130 */ |
124 |
|
125 api.Events = { |
131 api.Events = { |
126 trigger: function( id ) { |
132 trigger: function( id ) { |
127 if ( this.topics && this.topics[ id ] ) |
133 if ( this.topics && this.topics[ id ] ) |
128 this.topics[ id ].fireWith( this, slice.call( arguments, 1 ) ); |
134 this.topics[ id ].fireWith( this, slice.call( arguments, 1 ) ); |
129 return this; |
135 return this; |
130 }, |
136 }, |
131 |
137 |
132 bind: function( id, callback ) { |
138 bind: function( id ) { |
133 this.topics = this.topics || {}; |
139 this.topics = this.topics || {}; |
134 this.topics[ id ] = this.topics[ id ] || $.Callbacks(); |
140 this.topics[ id ] = this.topics[ id ] || $.Callbacks(); |
135 this.topics[ id ].add.apply( this.topics[ id ], slice.call( arguments, 1 ) ); |
141 this.topics[ id ].add.apply( this.topics[ id ], slice.call( arguments, 1 ) ); |
136 return this; |
142 return this; |
137 }, |
143 }, |
138 |
144 |
139 unbind: function( id, callback ) { |
145 unbind: function( id ) { |
140 if ( this.topics && this.topics[ id ] ) |
146 if ( this.topics && this.topics[ id ] ) |
141 this.topics[ id ].remove.apply( this.topics[ id ], slice.call( arguments, 1 ) ); |
147 this.topics[ id ].remove.apply( this.topics[ id ], slice.call( arguments, 1 ) ); |
142 return this; |
148 return this; |
143 } |
149 } |
144 }; |
150 }; |
145 |
151 |
146 /* ===================================================================== |
152 /** |
147 * Observable values that support two-way binding. |
153 * Observable values that support two-way binding. |
148 * ===================================================================== */ |
154 * |
149 |
155 * @constuctor |
|
156 */ |
150 api.Value = api.Class.extend({ |
157 api.Value = api.Class.extend({ |
151 initialize: function( initial, options ) { |
158 initialize: function( initial, options ) { |
152 this._value = initial; // @todo: potentially change this to a this.set() call. |
159 this._value = initial; // @todo: potentially change this to a this.set() call. |
153 this.callbacks = $.Callbacks(); |
160 this.callbacks = $.Callbacks(); |
|
161 this._dirty = false; |
154 |
162 |
155 $.extend( this, options || {} ); |
163 $.extend( this, options || {} ); |
156 |
164 |
157 this.set = $.proxy( this.set, this ); |
165 this.set = $.proxy( this.set, this ); |
158 }, |
166 }, |
174 |
182 |
175 to = this._setter.apply( this, arguments ); |
183 to = this._setter.apply( this, arguments ); |
176 to = this.validate( to ); |
184 to = this.validate( to ); |
177 |
185 |
178 // Bail if the sanitized value is null or unchanged. |
186 // Bail if the sanitized value is null or unchanged. |
179 if ( null === to || this._value === to ) |
187 if ( null === to || _.isEqual( from, to ) ) { |
180 return this; |
188 return this; |
|
189 } |
181 |
190 |
182 this._value = to; |
191 this._value = to; |
|
192 this._dirty = true; |
183 |
193 |
184 this.callbacks.fireWith( this, [ to, from ] ); |
194 this.callbacks.fireWith( this, [ to, from ] ); |
185 |
195 |
186 return this; |
196 return this; |
187 }, |
197 }, |
207 |
217 |
208 validate: function( value ) { |
218 validate: function( value ) { |
209 return value; |
219 return value; |
210 }, |
220 }, |
211 |
221 |
212 bind: function( callback ) { |
222 bind: function() { |
213 this.callbacks.add.apply( this.callbacks, arguments ); |
223 this.callbacks.add.apply( this.callbacks, arguments ); |
214 return this; |
224 return this; |
215 }, |
225 }, |
216 |
226 |
217 unbind: function( callback ) { |
227 unbind: function() { |
218 this.callbacks.remove.apply( this.callbacks, arguments ); |
228 this.callbacks.remove.apply( this.callbacks, arguments ); |
219 return this; |
229 return this; |
220 }, |
230 }, |
221 |
231 |
222 link: function() { // values* |
232 link: function() { // values* |
252 }); |
262 }); |
253 return this; |
263 return this; |
254 } |
264 } |
255 }); |
265 }); |
256 |
266 |
257 /* ===================================================================== |
267 /** |
258 * A collection of observable values. |
268 * A collection of observable values. |
259 * ===================================================================== */ |
269 * |
260 |
270 * @constuctor |
|
271 * @augments wp.customize.Class |
|
272 * @mixes wp.customize.Events |
|
273 */ |
261 api.Values = api.Class.extend({ |
274 api.Values = api.Class.extend({ |
262 defaultConstructor: api.Value, |
275 defaultConstructor: api.Value, |
263 |
276 |
264 initialize: function( options ) { |
277 initialize: function( options ) { |
265 $.extend( this, options || {} ); |
278 $.extend( this, options || {} ); |
377 } |
390 } |
378 }); |
391 }); |
379 |
392 |
380 $.extend( api.Values.prototype, api.Events ); |
393 $.extend( api.Values.prototype, api.Events ); |
381 |
394 |
382 /* ===================================================================== |
395 |
383 * An observable value that syncs with an element. |
396 /** |
384 * |
397 * Cast a string to a jQuery collection if it isn't already. |
385 * Handles inputs, selects, and textareas by default. |
398 * |
386 * ===================================================================== */ |
399 * @param {string|jQuery collection} element |
387 |
400 */ |
388 api.ensure = function( element ) { |
401 api.ensure = function( element ) { |
389 return typeof element == 'string' ? $( element ) : element; |
402 return typeof element == 'string' ? $( element ) : element; |
390 }; |
403 }; |
391 |
404 |
|
405 /** |
|
406 * An observable value that syncs with an element. |
|
407 * |
|
408 * Handles inputs, selects, and textareas by default. |
|
409 * |
|
410 * @constuctor |
|
411 * @augments wp.customize.Value |
|
412 * @augments wp.customize.Class |
|
413 */ |
392 api.Element = api.Value.extend({ |
414 api.Element = api.Value.extend({ |
393 initialize: function( element, options ) { |
415 initialize: function( element, options ) { |
394 var self = this, |
416 var self = this, |
395 synchronizer = api.Element.synchronizer.html, |
417 synchronizer = api.Element.synchronizer.html, |
396 type, update, refresh; |
418 type, update, refresh; |
402 this.events += 'change'; |
424 this.events += 'change'; |
403 synchronizer = api.Element.synchronizer.val; |
425 synchronizer = api.Element.synchronizer.val; |
404 |
426 |
405 if ( this.element.is('input') ) { |
427 if ( this.element.is('input') ) { |
406 type = this.element.prop('type'); |
428 type = this.element.prop('type'); |
407 if ( api.Element.synchronizer[ type ] ) |
429 if ( api.Element.synchronizer[ type ] ) { |
408 synchronizer = api.Element.synchronizer[ type ]; |
430 synchronizer = api.Element.synchronizer[ type ]; |
409 if ( 'text' === type || 'password' === type ) |
431 } |
|
432 if ( 'text' === type || 'password' === type ) { |
410 this.events += ' keyup'; |
433 this.events += ' keyup'; |
|
434 } else if ( 'range' === type ) { |
|
435 this.events += ' input propertychange'; |
|
436 } |
411 } else if ( this.element.is('textarea') ) { |
437 } else if ( this.element.is('textarea') ) { |
412 this.events += ' keyup'; |
438 this.events += ' keyup'; |
413 } |
439 } |
414 } |
440 } |
415 |
441 |
440 update: function() {} |
466 update: function() {} |
441 }); |
467 }); |
442 |
468 |
443 api.Element.synchronizer = {}; |
469 api.Element.synchronizer = {}; |
444 |
470 |
445 $.each( [ 'html', 'val' ], function( i, method ) { |
471 $.each( [ 'html', 'val' ], function( index, method ) { |
446 api.Element.synchronizer[ method ] = { |
472 api.Element.synchronizer[ method ] = { |
447 update: function( to ) { |
473 update: function( to ) { |
448 this.element[ method ]( to ); |
474 this.element[ method ]( to ); |
449 }, |
475 }, |
450 refresh: function() { |
476 refresh: function() { |
471 refresh: function() { |
497 refresh: function() { |
472 return this.element.filter( ':checked' ).val(); |
498 return this.element.filter( ':checked' ).val(); |
473 } |
499 } |
474 }; |
500 }; |
475 |
501 |
476 /* ===================================================================== |
502 $.support.postMessage = !! window.postMessage; |
|
503 |
|
504 /** |
477 * Messenger for postMessage. |
505 * Messenger for postMessage. |
478 * ===================================================================== */ |
506 * |
479 |
507 * @constuctor |
480 $.support.postMessage = !! window.postMessage; |
508 * @augments wp.customize.Class |
481 |
509 * @mixes wp.customize.Events |
|
510 */ |
482 api.Messenger = api.Class.extend({ |
511 api.Messenger = api.Class.extend({ |
|
512 /** |
|
513 * Create a new Value. |
|
514 * |
|
515 * @param {string} key Unique identifier. |
|
516 * @param {mixed} initial Initial value. |
|
517 * @param {mixed} options Options hash. Optional. |
|
518 * @return {Value} Class instance of the Value. |
|
519 */ |
483 add: function( key, initial, options ) { |
520 add: function( key, initial, options ) { |
484 return this[ key ] = new api.Value( initial, options ); |
521 return this[ key ] = new api.Value( initial, options ); |
485 }, |
522 }, |
486 |
523 |
487 /** |
524 /** |
499 |
536 |
500 $.extend( this, options || {} ); |
537 $.extend( this, options || {} ); |
501 |
538 |
502 this.add( 'channel', params.channel ); |
539 this.add( 'channel', params.channel ); |
503 this.add( 'url', params.url || '' ); |
540 this.add( 'url', params.url || '' ); |
504 this.add( 'targetWindow', params.targetWindow || defaultTarget ); |
|
505 this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) { |
541 this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) { |
506 return to.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ); |
542 return to.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ); |
507 }); |
543 }); |
|
544 |
|
545 // first add with no value |
|
546 this.add( 'targetWindow', null ); |
|
547 // This avoids SecurityErrors when setting a window object in x-origin iframe'd scenarios. |
|
548 this.targetWindow.set = function( to ) { |
|
549 var from = this._value; |
|
550 |
|
551 to = this._setter.apply( this, arguments ); |
|
552 to = this.validate( to ); |
|
553 |
|
554 if ( null === to || from === to ) { |
|
555 return this; |
|
556 } |
|
557 |
|
558 this._value = to; |
|
559 this._dirty = true; |
|
560 |
|
561 this.callbacks.fireWith( this, [ to, from ] ); |
|
562 |
|
563 return this; |
|
564 }; |
|
565 // now set it |
|
566 this.targetWindow( params.targetWindow || defaultTarget ); |
|
567 |
508 |
568 |
509 // Since we want jQuery to treat the receive function as unique |
569 // Since we want jQuery to treat the receive function as unique |
510 // to this instance, we give the function a new guid. |
570 // to this instance, we give the function a new guid. |
511 // |
571 // |
512 // This will prevent every Messenger's receive function from being |
572 // This will prevent every Messenger's receive function from being |
531 |
591 |
532 // Check to make sure the origin is valid. |
592 // Check to make sure the origin is valid. |
533 if ( this.origin() && event.origin !== this.origin() ) |
593 if ( this.origin() && event.origin !== this.origin() ) |
534 return; |
594 return; |
535 |
595 |
|
596 // Ensure we have a string that's JSON.parse-able |
|
597 if ( typeof event.data !== 'string' || event.data[0] !== '{' ) { |
|
598 return; |
|
599 } |
|
600 |
536 message = JSON.parse( event.data ); |
601 message = JSON.parse( event.data ); |
537 |
602 |
538 // Check required message properties. |
603 // Check required message properties. |
539 if ( ! message || ! message.id || typeof message.data === 'undefined' ) |
604 if ( ! message || ! message.id || typeof message.data === 'undefined' ) |
540 return; |
605 return; |
563 }); |
628 }); |
564 |
629 |
565 // Add the Events mixin to api.Messenger. |
630 // Add the Events mixin to api.Messenger. |
566 $.extend( api.Messenger.prototype, api.Events ); |
631 $.extend( api.Messenger.prototype, api.Events ); |
567 |
632 |
568 /* ===================================================================== |
633 // Core customize object. |
569 * Core customize object. |
|
570 * ===================================================================== */ |
|
571 |
|
572 api = $.extend( new api.Values(), api ); |
634 api = $.extend( new api.Values(), api ); |
573 api.get = function() { |
635 api.get = function() { |
574 var result = {}; |
636 var result = {}; |
575 |
637 |
576 this.each( function( obj, key ) { |
638 this.each( function( obj, key ) { |