customize-base.js 25 KB

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