You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

994 lines
25 KiB

1 year ago
  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.
  339. * When an ID string is supplied, then itemObject must be provided.
  340. * @param {wp.customize.Class} [itemObject] - The item instance when the first argument is an ID string.
  341. * @return {wp.customize.Class} The new item's instance, or an existing instance if already added.
  342. */
  343. add: function( item, itemObject ) {
  344. var collection = this, id, instance;
  345. if ( 'string' === typeof item ) {
  346. id = item;
  347. instance = itemObject;
  348. } else {
  349. if ( 'string' !== typeof item.id ) {
  350. throw new Error( 'Unknown key' );
  351. }
  352. id = item.id;
  353. instance = item;
  354. }
  355. if ( collection.has( id ) ) {
  356. return collection.value( id );
  357. }
  358. collection._value[ id ] = instance;
  359. instance.parent = collection;
  360. // Propagate a 'change' event on an item up to the collection.
  361. if ( instance.extended( api.Value ) ) {
  362. instance.bind( collection._change );
  363. }
  364. collection.trigger( 'add', instance );
  365. // If a deferred object exists for this item,
  366. // resolve it.
  367. if ( collection._deferreds[ id ] ) {
  368. collection._deferreds[ id ].resolve();
  369. }
  370. return collection._value[ id ];
  371. },
  372. /**
  373. * Create a new item of the collection using the collection's default constructor
  374. * and store it in the collection.
  375. *
  376. * @param {string} id The ID of the item.
  377. * @param {mixed} value Any extra arguments are passed into the item's initialize method.
  378. * @return {mixed} The new item's instance.
  379. */
  380. create: function( id ) {
  381. return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) );
  382. },
  383. /**
  384. * Iterate over all items in the collection invoking the provided callback.
  385. *
  386. * @param {Function} callback Function to invoke.
  387. * @param {Object} context Object context to invoke the function with. Optional.
  388. */
  389. each: function( callback, context ) {
  390. context = typeof context === 'undefined' ? this : context;
  391. $.each( this._value, function( key, obj ) {
  392. callback.call( context, obj, key );
  393. });
  394. },
  395. /**
  396. * Remove an item from the collection.
  397. *
  398. * @param {string} id The ID of the item to remove.
  399. */
  400. remove: function( id ) {
  401. var value = this.value( id );
  402. if ( value ) {
  403. // Trigger event right before the element is removed from the collection.
  404. this.trigger( 'remove', value );
  405. if ( value.extended( api.Value ) ) {
  406. value.unbind( this._change );
  407. }
  408. delete value.parent;
  409. }
  410. delete this._value[ id ];
  411. delete this._deferreds[ id ];
  412. // Trigger removed event after the item has been eliminated from the collection.
  413. if ( value ) {
  414. this.trigger( 'removed', value );
  415. }
  416. },
  417. /**
  418. * Runs a callback once all requested values exist.
  419. *
  420. * when( ids*, [callback] );
  421. *
  422. * For example:
  423. * when( id1, id2, id3, function( value1, value2, value3 ) {} );
  424. *
  425. * @return $.Deferred.promise();
  426. */
  427. when: function() {
  428. var self = this,
  429. ids = slice.call( arguments ),
  430. dfd = $.Deferred();
  431. // If the last argument is a callback, bind it to .done().
  432. if ( typeof ids[ ids.length - 1 ] === 'function' ) {
  433. dfd.done( ids.pop() );
  434. }
  435. /*
  436. * Create a stack of deferred objects for each item that is not
  437. * yet available, and invoke the supplied callback when they are.
  438. */
  439. $.when.apply( $, $.map( ids, function( id ) {
  440. if ( self.has( id ) ) {
  441. return;
  442. }
  443. /*
  444. * The requested item is not available yet, create a deferred
  445. * object to resolve when it becomes available.
  446. */
  447. return self._deferreds[ id ] = self._deferreds[ id ] || $.Deferred();
  448. })).done( function() {
  449. var values = $.map( ids, function( id ) {
  450. return self( id );
  451. });
  452. // If a value is missing, we've used at least one expired deferred.
  453. // Call Values.when again to generate a new deferred.
  454. if ( values.length !== ids.length ) {
  455. // ids.push( callback );
  456. self.when.apply( self, ids ).done( function() {
  457. dfd.resolveWith( self, values );
  458. });
  459. return;
  460. }
  461. dfd.resolveWith( self, values );
  462. });
  463. return dfd.promise();
  464. },
  465. /**
  466. * A helper function to propagate a 'change' event from an item
  467. * to the collection itself.
  468. */
  469. _change: function() {
  470. this.parent.trigger( 'change', this );
  471. }
  472. });
  473. // Create a global events bus on the Customizer.
  474. $.extend( api.Values.prototype, api.Events );
  475. /**
  476. * Cast a string to a jQuery collection if it isn't already.
  477. *
  478. * @param {string|jQuery collection} element
  479. */
  480. api.ensure = function( element ) {
  481. return typeof element === 'string' ? $( element ) : element;
  482. };
  483. /**
  484. * An observable value that syncs with an element.
  485. *
  486. * Handles inputs, selects, and textareas by default.
  487. *
  488. * @memberOf wp.customize
  489. * @alias wp.customize.Element
  490. *
  491. * @constructor
  492. * @augments wp.customize.Value
  493. * @augments wp.customize.Class
  494. */
  495. api.Element = api.Value.extend(/** @lends wp.customize.Element */{
  496. initialize: function( element, options ) {
  497. var self = this,
  498. synchronizer = api.Element.synchronizer.html,
  499. type, update, refresh;
  500. this.element = api.ensure( element );
  501. this.events = '';
  502. if ( this.element.is( 'input, select, textarea' ) ) {
  503. type = this.element.prop( 'type' );
  504. this.events += ' change input';
  505. synchronizer = api.Element.synchronizer.val;
  506. if ( this.element.is( 'input' ) && api.Element.synchronizer[ type ] ) {
  507. synchronizer = api.Element.synchronizer[ type ];
  508. }
  509. }
  510. api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) );
  511. this._value = this.get();
  512. update = this.update;
  513. refresh = this.refresh;
  514. this.update = function( to ) {
  515. if ( to !== refresh.call( self ) ) {
  516. update.apply( this, arguments );
  517. }
  518. };
  519. this.refresh = function() {
  520. self.set( refresh.call( self ) );
  521. };
  522. this.bind( this.update );
  523. this.element.on( this.events, this.refresh );
  524. },
  525. find: function( selector ) {
  526. return $( selector, this.element );
  527. },
  528. refresh: function() {},
  529. update: function() {}
  530. });
  531. api.Element.synchronizer = {};
  532. $.each( [ 'html', 'val' ], function( index, method ) {
  533. api.Element.synchronizer[ method ] = {
  534. update: function( to ) {
  535. this.element[ method ]( to );
  536. },
  537. refresh: function() {
  538. return this.element[ method ]();
  539. }
  540. };
  541. });
  542. api.Element.synchronizer.checkbox = {
  543. update: function( to ) {
  544. this.element.prop( 'checked', to );
  545. },
  546. refresh: function() {
  547. return this.element.prop( 'checked' );
  548. }
  549. };
  550. api.Element.synchronizer.radio = {
  551. update: function( to ) {
  552. this.element.filter( function() {
  553. return this.value === to;
  554. }).prop( 'checked', true );
  555. },
  556. refresh: function() {
  557. return this.element.filter( ':checked' ).val();
  558. }
  559. };
  560. $.support.postMessage = !! window.postMessage;
  561. /**
  562. * A communicator for sending data from one window to another over postMessage.
  563. *
  564. * @memberOf wp.customize
  565. * @alias wp.customize.Messenger
  566. *
  567. * @constructor
  568. * @augments wp.customize.Class
  569. * @mixes wp.customize.Events
  570. */
  571. api.Messenger = api.Class.extend(/** @lends wp.customize.Messenger.prototype */{
  572. /**
  573. * Create a new Value.
  574. *
  575. * @param {string} key Unique identifier.
  576. * @param {mixed} initial Initial value.
  577. * @param {mixed} options Options hash. Optional.
  578. * @return {Value} Class instance of the Value.
  579. */
  580. add: function( key, initial, options ) {
  581. return this[ key ] = new api.Value( initial, options );
  582. },
  583. /**
  584. * Initialize Messenger.
  585. *
  586. * @param {Object} params - Parameters to configure the messenger.
  587. * {string} params.url - The URL to communicate with.
  588. * {window} params.targetWindow - The window instance to communicate with. Default window.parent.
  589. * {string} params.channel - If provided, will send the channel with each message and only accept messages a matching channel.
  590. * @param {Object} options - Extend any instance parameter or method with this object.
  591. */
  592. initialize: function( params, options ) {
  593. // Target the parent frame by default, but only if a parent frame exists.
  594. var defaultTarget = window.parent === window ? null : window.parent;
  595. $.extend( this, options || {} );
  596. this.add( 'channel', params.channel );
  597. this.add( 'url', params.url || '' );
  598. this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) {
  599. var urlParser = document.createElement( 'a' );
  600. urlParser.href = to;
  601. // Port stripping needed by IE since it adds to host but not to event.origin.
  602. return urlParser.protocol + '//' + urlParser.host.replace( /:(80|443)$/, '' );
  603. });
  604. // First add with no value.
  605. this.add( 'targetWindow', null );
  606. // This avoids SecurityErrors when setting a window object in x-origin iframe'd scenarios.
  607. this.targetWindow.set = function( to ) {
  608. var from = this._value;
  609. to = this._setter.apply( this, arguments );
  610. to = this.validate( to );
  611. if ( null === to || from === to ) {
  612. return this;
  613. }
  614. this._value = to;
  615. this._dirty = true;
  616. this.callbacks.fireWith( this, [ to, from ] );
  617. return this;
  618. };
  619. // Now set it.
  620. this.targetWindow( params.targetWindow || defaultTarget );
  621. /*
  622. * Since we want jQuery to treat the receive function as unique
  623. * to this instance, we give the function a new guid.
  624. *
  625. * This will prevent every Messenger's receive function from being
  626. * unbound when calling $.off( 'message', this.receive );
  627. */
  628. this.receive = this.receive.bind( this );
  629. this.receive.guid = $.guid++;
  630. $( window ).on( 'message', this.receive );
  631. },
  632. destroy: function() {
  633. $( window ).off( 'message', this.receive );
  634. },
  635. /**
  636. * Receive data from the other window.
  637. *
  638. * @param {jQuery.Event} event Event with embedded data.
  639. */
  640. receive: function( event ) {
  641. var message;
  642. event = event.originalEvent;
  643. if ( ! this.targetWindow || ! this.targetWindow() ) {
  644. return;
  645. }
  646. // Check to make sure the origin is valid.
  647. if ( this.origin() && event.origin !== this.origin() ) {
  648. return;
  649. }
  650. // Ensure we have a string that's JSON.parse-able.
  651. if ( typeof event.data !== 'string' || event.data[0] !== '{' ) {
  652. return;
  653. }
  654. message = JSON.parse( event.data );
  655. // Check required message properties.
  656. if ( ! message || ! message.id || typeof message.data === 'undefined' ) {
  657. return;
  658. }
  659. // Check if channel names match.
  660. if ( ( message.channel || this.channel() ) && this.channel() !== message.channel ) {
  661. return;
  662. }
  663. this.trigger( message.id, message.data );
  664. },
  665. /**
  666. * Send data to the other window.
  667. *
  668. * @param {string} id The event name.
  669. * @param {Object} data Data.
  670. */
  671. send: function( id, data ) {
  672. var message;
  673. data = typeof data === 'undefined' ? null : data;
  674. if ( ! this.url() || ! this.targetWindow() ) {
  675. return;
  676. }
  677. message = { id: id, data: data };
  678. if ( this.channel() ) {
  679. message.channel = this.channel();
  680. }
  681. this.targetWindow().postMessage( JSON.stringify( message ), this.origin() );
  682. }
  683. });
  684. // Add the Events mixin to api.Messenger.
  685. $.extend( api.Messenger.prototype, api.Events );
  686. /**
  687. * Notification.
  688. *
  689. * @class
  690. * @augments wp.customize.Class
  691. * @since 4.6.0
  692. *
  693. * @memberOf wp.customize
  694. * @alias wp.customize.Notification
  695. *
  696. * @param {string} code - The error code.
  697. * @param {object} params - Params.
  698. * @param {string} params.message=null - The error message.
  699. * @param {string} [params.type=error] - The notification type.
  700. * @param {boolean} [params.fromServer=false] - Whether the notification was server-sent.
  701. * @param {string} [params.setting=null] - The setting ID that the notification is related to.
  702. * @param {*} [params.data=null] - Any additional data.
  703. */
  704. api.Notification = api.Class.extend(/** @lends wp.customize.Notification.prototype */{
  705. /**
  706. * Template function for rendering the notification.
  707. *
  708. * This will be populated with template option or else it will be populated with template from the ID.
  709. *
  710. * @since 4.9.0
  711. * @var {Function}
  712. */
  713. template: null,
  714. /**
  715. * ID for the template to render the notification.
  716. *
  717. * @since 4.9.0
  718. * @var {string}
  719. */
  720. templateId: 'customize-notification',
  721. /**
  722. * Additional class names to add to the notification container.
  723. *
  724. * @since 4.9.0
  725. * @var {string}
  726. */
  727. containerClasses: '',
  728. /**
  729. * Initialize notification.
  730. *
  731. * @since 4.9.0
  732. *
  733. * @param {string} code - Notification code.
  734. * @param {Object} params - Notification parameters.
  735. * @param {string} params.message - Message.
  736. * @param {string} [params.type=error] - Type.
  737. * @param {string} [params.setting] - Related setting ID.
  738. * @param {Function} [params.template] - Function for rendering template. If not provided, this will come from templateId.
  739. * @param {string} [params.templateId] - ID for template to render the notification.
  740. * @param {string} [params.containerClasses] - Additional class names to add to the notification container.
  741. * @param {boolean} [params.dismissible] - Whether the notification can be dismissed.
  742. */
  743. initialize: function( code, params ) {
  744. var _params;
  745. this.code = code;
  746. _params = _.extend(
  747. {
  748. message: null,
  749. type: 'error',
  750. fromServer: false,
  751. data: null,
  752. setting: null,
  753. template: null,
  754. dismissible: false,
  755. containerClasses: ''
  756. },
  757. params
  758. );
  759. delete _params.code;
  760. _.extend( this, _params );
  761. },
  762. /**
  763. * Render the notification.
  764. *
  765. * @since 4.9.0
  766. *
  767. * @return {jQuery} Notification container element.
  768. */
  769. render: function() {
  770. var notification = this, container, data;
  771. if ( ! notification.template ) {
  772. notification.template = wp.template( notification.templateId );
  773. }
  774. data = _.extend( {}, notification, {
  775. alt: notification.parent && notification.parent.alt
  776. } );
  777. container = $( notification.template( data ) );
  778. if ( notification.dismissible ) {
  779. container.find( '.notice-dismiss' ).on( 'click keydown', function( event ) {
  780. if ( 'keydown' === event.type && 13 !== event.which ) {
  781. return;
  782. }
  783. if ( notification.parent ) {
  784. notification.parent.remove( notification.code );
  785. } else {
  786. container.remove();
  787. }
  788. });
  789. }
  790. return container;
  791. }
  792. });
  793. // The main API object is also a collection of all customizer settings.
  794. api = $.extend( new api.Values(), api );
  795. /**
  796. * Get all customize settings.
  797. *
  798. * @alias wp.customize.get
  799. *
  800. * @return {Object}
  801. */
  802. api.get = function() {
  803. var result = {};
  804. this.each( function( obj, key ) {
  805. result[ key ] = obj.get();
  806. });
  807. return result;
  808. };
  809. /**
  810. * Utility function namespace
  811. *
  812. * @namespace wp.customize.utils
  813. */
  814. api.utils = {};
  815. /**
  816. * Parse query string.
  817. *
  818. * @since 4.7.0
  819. * @access public
  820. *
  821. * @alias wp.customize.utils.parseQueryString
  822. *
  823. * @param {string} queryString Query string.
  824. * @return {Object} Parsed query string.
  825. */
  826. api.utils.parseQueryString = function parseQueryString( queryString ) {
  827. var queryParams = {};
  828. _.each( queryString.split( '&' ), function( pair ) {
  829. var parts, key, value;
  830. parts = pair.split( '=', 2 );
  831. if ( ! parts[0] ) {
  832. return;
  833. }
  834. key = decodeURIComponent( parts[0].replace( /\+/g, ' ' ) );
  835. key = key.replace( / /g, '_' ); // What PHP does.
  836. if ( _.isUndefined( parts[1] ) ) {
  837. value = null;
  838. } else {
  839. value = decodeURIComponent( parts[1].replace( /\+/g, ' ' ) );
  840. }
  841. queryParams[ key ] = value;
  842. } );
  843. return queryParams;
  844. };
  845. /**
  846. * Expose the API publicly on window.wp.customize
  847. *
  848. * @namespace wp.customize
  849. */
  850. exports.customize = api;
  851. })( wp, jQuery );