| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Sep 12 08:20:32 2026 | Cross-referenced by PHPXref |