[ Index ]

WordPress Cross Reference

title

Body

[close]

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

   1  window.wp = window.wp || {};
   2  
   3  (function( exports, $ ){
   4      var api, extend, ctor, inherits,
   5          slice = Array.prototype.slice;
   6  
   7      /* =====================================================================
   8       * Micro-inheritance - thank you, backbone.js.
   9       * ===================================================================== */
  10  
  11      extend = function( protoProps, classProps ) {
  12          var child = inherits( this, protoProps, classProps );
  13          child.extend = this.extend;
  14          return child;
  15      };
  16  
  17      // Shared empty constructor function to aid in prototype-chain creation.
  18      ctor = function() {};
  19  
  20      // Helper function to correctly set up the prototype chain, for subclasses.
  21      // Similar to `goog.inherits`, but uses a hash of prototype properties and
  22      // class properties to be extended.
  23      inherits = function( parent, protoProps, staticProps ) {
  24          var child;
  25  
  26          // The constructor function for the new subclass is either defined by you
  27          // (the "constructor" property in your `extend` definition), or defaulted
  28          // by us to simply call `super()`.
  29          if ( protoProps && protoProps.hasOwnProperty( 'constructor' ) ) {
  30              child = protoProps.constructor;
  31          } else {
  32              child = function() {
  33                  // Storing the result `super()` before returning the value
  34                  // prevents a bug in Opera where, if the constructor returns
  35                  // a function, Opera will reject the return value in favor of
  36                  // the original object. This causes all sorts of trouble.
  37                  var result = parent.apply( this, arguments );
  38                  return result;
  39              };
  40          }
  41  
  42          // Inherit class (static) properties from parent.
  43          $.extend( child, parent );
  44  
  45          // Set the prototype chain to inherit from `parent`, without calling
  46          // `parent`'s constructor function.
  47          ctor.prototype  = parent.prototype;
  48          child.prototype = new ctor();
  49  
  50          // Add prototype properties (instance properties) to the subclass,
  51          // if supplied.
  52          if ( protoProps )
  53              $.extend( child.prototype, protoProps );
  54  
  55          // Add static properties to the constructor function, if supplied.
  56          if ( staticProps )
  57              $.extend( child, staticProps );
  58  
  59          // Correctly set child's `prototype.constructor`.
  60          child.prototype.constructor = child;
  61  
  62          // Set a convenience property in case the parent's prototype is needed later.
  63          child.__super__ = parent.prototype;
  64  
  65          return child;
  66      };
  67  
  68      api = {};
  69  
  70      /* =====================================================================
  71       * Base class.
  72       * ===================================================================== */
  73  
  74      api.Class = function( applicator, argsArray, options ) {
  75          var magic, args = arguments;
  76  
  77          if ( applicator && argsArray && api.Class.applicator === applicator ) {
  78              args = argsArray;
  79              $.extend( this, options || {} );
  80          }
  81  
  82          magic = this;
  83          if ( this.instance ) {
  84              magic = function() {
  85                  return magic.instance.apply( magic, arguments );
  86              };
  87  
  88              $.extend( magic, this );
  89          }
  90  
  91          magic.initialize.apply( magic, args );
  92          return magic;
  93      };
  94  
  95      api.Class.applicator = {};
  96  
  97      api.Class.prototype.initialize = function() {};
  98  
  99      /*
 100       * Checks whether a given instance extended a constructor.
 101       *
 102       * The magic surrounding the instance parameter causes the instanceof
 103       * keyword to return inaccurate results; it defaults to the function's
 104       * prototype instead of the constructor chain. Hence this function.
 105       */
 106      api.Class.prototype.extended = function( constructor ) {
 107          var proto = this;
 108  
 109          while ( typeof proto.constructor !== 'undefined' ) {
 110              if ( proto.constructor === constructor )
 111                  return true;
 112              if ( typeof proto.constructor.__super__ === 'undefined' )
 113                  return false;
 114              proto = proto.constructor.__super__;
 115          }
 116          return false;
 117      };
 118  
 119      api.Class.extend = extend;
 120  
 121      /* =====================================================================
 122       * Events mixin.
 123       * ===================================================================== */
 124  
 125      api.Events = {
 126          trigger: function( id ) {
 127              if ( this.topics && this.topics[ id ] )
 128                  this.topics[ id ].fireWith( this, slice.call( arguments, 1 ) );
 129              return this;
 130          },
 131  
 132          bind: function( id ) {
 133              this.topics = this.topics || {};
 134              this.topics[ id ] = this.topics[ id ] || $.Callbacks();
 135              this.topics[ id ].add.apply( this.topics[ id ], slice.call( arguments, 1 ) );
 136              return this;
 137          },
 138  
 139          unbind: function( id ) {
 140              if ( this.topics && this.topics[ id ] )
 141                  this.topics[ id ].remove.apply( this.topics[ id ], slice.call( arguments, 1 ) );
 142              return this;
 143          }
 144      };
 145  
 146      /* =====================================================================
 147       * Observable values that support two-way binding.
 148       * ===================================================================== */
 149  
 150      api.Value = api.Class.extend({
 151          initialize: function( initial, options ) {
 152              this._value = initial; // @todo: potentially change this to a this.set() call.
 153              this.callbacks = $.Callbacks();
 154  
 155              $.extend( this, options || {} );
 156  
 157              this.set = $.proxy( this.set, this );
 158          },
 159  
 160          /*
 161           * Magic. Returns a function that will become the instance.
 162           * Set to null to prevent the instance from extending a function.
 163           */
 164          instance: function() {
 165              return arguments.length ? this.set.apply( this, arguments ) : this.get();
 166          },
 167  
 168          get: function() {
 169              return this._value;
 170          },
 171  
 172          set: function( to ) {
 173              var from = this._value;
 174  
 175              to = this._setter.apply( this, arguments );
 176              to = this.validate( to );
 177  
 178              // Bail if the sanitized value is null or unchanged.
 179              if ( null === to || this._value === to )
 180                  return this;
 181  
 182              this._value = to;
 183  
 184              this.callbacks.fireWith( this, [ to, from ] );
 185  
 186              return this;
 187          },
 188  
 189          _setter: function( to ) {
 190              return to;
 191          },
 192  
 193          setter: function( callback ) {
 194              var from = this.get();
 195              this._setter = callback;
 196              // Temporarily clear value so setter can decide if it's valid.
 197              this._value = null;
 198              this.set( from );
 199              return this;
 200          },
 201  
 202          resetSetter: function() {
 203              this._setter = this.constructor.prototype._setter;
 204              this.set( this.get() );
 205              return this;
 206          },
 207  
 208          validate: function( value ) {
 209              return value;
 210          },
 211  
 212          bind: function() {
 213              this.callbacks.add.apply( this.callbacks, arguments );
 214              return this;
 215          },
 216  
 217          unbind: function() {
 218              this.callbacks.remove.apply( this.callbacks, arguments );
 219              return this;
 220          },
 221  
 222          link: function() { // values*
 223              var set = this.set;
 224              $.each( arguments, function() {
 225                  this.bind( set );
 226              });
 227              return this;
 228          },
 229  
 230          unlink: function() { // values*
 231              var set = this.set;
 232              $.each( arguments, function() {
 233                  this.unbind( set );
 234              });
 235              return this;
 236          },
 237  
 238          sync: function() { // values*
 239              var that = this;
 240              $.each( arguments, function() {
 241                  that.link( this );
 242                  this.link( that );
 243              });
 244              return this;
 245          },
 246  
 247          unsync: function() { // values*
 248              var that = this;
 249              $.each( arguments, function() {
 250                  that.unlink( this );
 251                  this.unlink( that );
 252              });
 253              return this;
 254          }
 255      });
 256  
 257      /* =====================================================================
 258       * A collection of observable values.
 259       * ===================================================================== */
 260  
 261      api.Values = api.Class.extend({
 262          defaultConstructor: api.Value,
 263  
 264          initialize: function( options ) {
 265              $.extend( this, options || {} );
 266  
 267              this._value = {};
 268              this._deferreds = {};
 269          },
 270  
 271          instance: function( id ) {
 272              if ( arguments.length === 1 )
 273                  return this.value( id );
 274  
 275              return this.when.apply( this, arguments );
 276          },
 277  
 278          value: function( id ) {
 279              return this._value[ id ];
 280          },
 281  
 282          has: function( id ) {
 283              return typeof this._value[ id ] !== 'undefined';
 284          },
 285  
 286          add: function( id, value ) {
 287              if ( this.has( id ) )
 288                  return this.value( id );
 289  
 290              this._value[ id ] = value;
 291              value.parent = this;
 292              if ( value.extended( api.Value ) )
 293                  value.bind( this._change );
 294  
 295              this.trigger( 'add', value );
 296  
 297              if ( this._deferreds[ id ] )
 298                  this._deferreds[ id ].resolve();
 299  
 300              return this._value[ id ];
 301          },
 302  
 303          create: function( id ) {
 304              return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) );
 305          },
 306  
 307          each: function( callback, context ) {
 308              context = typeof context === 'undefined' ? this : context;
 309  
 310              $.each( this._value, function( key, obj ) {
 311                  callback.call( context, obj, key );
 312              });
 313          },
 314  
 315          remove: function( id ) {
 316              var value;
 317  
 318              if ( this.has( id ) ) {
 319                  value = this.value( id );
 320                  this.trigger( 'remove', value );
 321                  if ( value.extended( api.Value ) )
 322                      value.unbind( this._change );
 323                  delete value.parent;
 324              }
 325  
 326              delete this._value[ id ];
 327              delete this._deferreds[ id ];
 328          },
 329  
 330          /**
 331           * Runs a callback once all requested values exist.
 332           *
 333           * when( ids*, [callback] );
 334           *
 335           * For example:
 336           *     when( id1, id2, id3, function( value1, value2, value3 ) {} );
 337           *
 338           * @returns $.Deferred.promise();
 339           */
 340          when: function() {
 341              var self = this,
 342                  ids  = slice.call( arguments ),
 343                  dfd  = $.Deferred();
 344  
 345              // If the last argument is a callback, bind it to .done()
 346              if ( $.isFunction( ids[ ids.length - 1 ] ) )
 347                  dfd.done( ids.pop() );
 348  
 349              $.when.apply( $, $.map( ids, function( id ) {
 350                  if ( self.has( id ) )
 351                      return;
 352  
 353                  return self._deferreds[ id ] = self._deferreds[ id ] || $.Deferred();
 354              })).done( function() {
 355                  var values = $.map( ids, function( id ) {
 356                          return self( id );
 357                      });
 358  
 359                  // If a value is missing, we've used at least one expired deferred.
 360                  // Call Values.when again to generate a new deferred.
 361                  if ( values.length !== ids.length ) {
 362                      // ids.push( callback );
 363                      self.when.apply( self, ids ).done( function() {
 364                          dfd.resolveWith( self, values );
 365                      });
 366                      return;
 367                  }
 368  
 369                  dfd.resolveWith( self, values );
 370              });
 371  
 372              return dfd.promise();
 373          },
 374  
 375          _change: function() {
 376              this.parent.trigger( 'change', this );
 377          }
 378      });
 379  
 380      $.extend( api.Values.prototype, api.Events );
 381  
 382      /* =====================================================================
 383       * An observable value that syncs with an element.
 384       *
 385       * Handles inputs, selects, and textareas by default.
 386       * ===================================================================== */
 387  
 388      api.ensure = function( element ) {
 389          return typeof element == 'string' ? $( element ) : element;
 390      };
 391  
 392      api.Element = api.Value.extend({
 393          initialize: function( element, options ) {
 394              var self = this,
 395                  synchronizer = api.Element.synchronizer.html,
 396                  type, update, refresh;
 397  
 398              this.element = api.ensure( element );
 399              this.events = '';
 400  
 401              if ( this.element.is('input, select, textarea') ) {
 402                  this.events += 'change';
 403                  synchronizer = api.Element.synchronizer.val;
 404  
 405                  if ( this.element.is('input') ) {
 406                      type = this.element.prop('type');
 407                      if ( api.Element.synchronizer[ type ] )
 408                          synchronizer = api.Element.synchronizer[ type ];
 409                      if ( 'text' === type || 'password' === type )
 410                          this.events += ' keyup';
 411                  } else if ( this.element.is('textarea') ) {
 412                      this.events += ' keyup';
 413                  }
 414              }
 415  
 416              api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) );
 417              this._value = this.get();
 418  
 419              update  = this.update;
 420              refresh = this.refresh;
 421  
 422              this.update = function( to ) {
 423                  if ( to !== refresh.call( self ) )
 424                      update.apply( this, arguments );
 425              };
 426              this.refresh = function() {
 427                  self.set( refresh.call( self ) );
 428              };
 429  
 430              this.bind( this.update );
 431              this.element.bind( this.events, this.refresh );
 432          },
 433  
 434          find: function( selector ) {
 435              return $( selector, this.element );
 436          },
 437  
 438          refresh: function() {},
 439  
 440          update: function() {}
 441      });
 442  
 443      api.Element.synchronizer = {};
 444  
 445      $.each( [ 'html', 'val' ], function( i, method ) {
 446          api.Element.synchronizer[ method ] = {
 447              update: function( to ) {
 448                  this.element[ method ]( to );
 449              },
 450              refresh: function() {
 451                  return this.element[ method ]();
 452              }
 453          };
 454      });
 455  
 456      api.Element.synchronizer.checkbox = {
 457          update: function( to ) {
 458              this.element.prop( 'checked', to );
 459          },
 460          refresh: function() {
 461              return this.element.prop( 'checked' );
 462          }
 463      };
 464  
 465      api.Element.synchronizer.radio = {
 466          update: function( to ) {
 467              this.element.filter( function() {
 468                  return this.value === to;
 469              }).prop( 'checked', true );
 470          },
 471          refresh: function() {
 472              return this.element.filter( ':checked' ).val();
 473          }
 474      };
 475  
 476      /* =====================================================================
 477       * Messenger for postMessage.
 478       * ===================================================================== */
 479  
 480      $.support.postMessage = !! window.postMessage;
 481  
 482      api.Messenger = api.Class.extend({
 483          add: function( key, initial, options ) {
 484              return this[ key ] = new api.Value( initial, options );
 485          },
 486  
 487          /**
 488           * Initialize Messenger.
 489           *
 490           * @param  {object} params        Parameters to configure the messenger.
 491           *         {string} .url          The URL to communicate with.
 492           *         {window} .targetWindow The window instance to communicate with. Default window.parent.
 493           *         {string} .channel      If provided, will send the channel with each message and only accept messages a matching channel.
 494           * @param  {object} options       Extend any instance parameter or method with this object.
 495           */
 496          initialize: function( params, options ) {
 497              // Target the parent frame by default, but only if a parent frame exists.
 498              var defaultTarget = window.parent == window ? null : window.parent;
 499  
 500              $.extend( this, options || {} );
 501  
 502              this.add( 'channel', params.channel );
 503              this.add( 'url', params.url || '' );
 504              this.add( 'targetWindow', params.targetWindow || defaultTarget );
 505              this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) {
 506                  return to.replace( /([^:]+:\/\/[^\/]+).*/, '$1' );
 507              });
 508  
 509              // Since we want jQuery to treat the receive function as unique
 510              // to this instance, we give the function a new guid.
 511              //
 512              // This will prevent every Messenger's receive function from being
 513              // unbound when calling $.off( 'message', this.receive );
 514              this.receive = $.proxy( this.receive, this );
 515              this.receive.guid = $.guid++;
 516  
 517              $( window ).on( 'message', this.receive );
 518          },
 519  
 520          destroy: function() {
 521              $( window ).off( 'message', this.receive );
 522          },
 523  
 524          receive: function( event ) {
 525              var message;
 526  
 527              event = event.originalEvent;
 528  
 529              if ( ! this.targetWindow() )
 530                  return;
 531  
 532              // Check to make sure the origin is valid.
 533              if ( this.origin() && event.origin !== this.origin() )
 534                  return;
 535  
 536              // Ensure we have a string that's JSON.parse-able
 537              if ( typeof event.data !== 'string' || event.data[0] !== '{' ) {
 538                  return;
 539              }
 540  
 541              message = JSON.parse( event.data );
 542  
 543              // Check required message properties.
 544              if ( ! message || ! message.id || typeof message.data === 'undefined' )
 545                  return;
 546  
 547              // Check if channel names match.
 548              if ( ( message.channel || this.channel() ) && this.channel() !== message.channel )
 549                  return;
 550  
 551              this.trigger( message.id, message.data );
 552          },
 553  
 554          send: function( id, data ) {
 555              var message;
 556  
 557              data = typeof data === 'undefined' ? null : data;
 558  
 559              if ( ! this.url() || ! this.targetWindow() )
 560                  return;
 561  
 562              message = { id: id, data: data };
 563              if ( this.channel() )
 564                  message.channel = this.channel();
 565  
 566              this.targetWindow().postMessage( JSON.stringify( message ), this.origin() );
 567          }
 568      });
 569  
 570      // Add the Events mixin to api.Messenger.
 571      $.extend( api.Messenger.prototype, api.Events );
 572  
 573      /* =====================================================================
 574       * Core customize object.
 575       * ===================================================================== */
 576  
 577      api = $.extend( new api.Values(), api );
 578      api.get = function() {
 579          var result = {};
 580  
 581          this.each( function( obj, key ) {
 582              result[ key ] = obj.get();
 583          });
 584  
 585          return result;
 586      };
 587  
 588      // Expose the API to the world.
 589      exports.customize = api;
 590  })( wp, jQuery );


Generated: Tue Mar 25 01:41:18 2014 WordPress honlapkészítés: online1.hu