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


Generated : Sat Sep 12 08:20:32 2026 Cross-referenced by PHPXref