[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/ -> customize-base.js (source)

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


Generated : Mon May 13 08:20:01 2024 Cross-referenced by PHPXref