[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-admin/js/ -> customize-controls.js (source)

   1  (function( exports, $ ){
   2      var api = wp.customize;
   3  
   4      /*
   5       * @param options
   6       * - previewer - The Previewer instance to sync with.
   7       * - transport - The transport to use for previewing. Supports 'refresh' and 'postMessage'.
   8       */
   9      api.Setting = api.Value.extend({
  10          initialize: function( id, value, options ) {
  11              var element;
  12  
  13              api.Value.prototype.initialize.call( this, value, options );
  14  
  15              this.id = id;
  16              this.transport = this.transport || 'refresh';
  17  
  18              this.bind( this.preview );
  19          },
  20          preview: function() {
  21              switch ( this.transport ) {
  22                  case 'refresh':
  23                      return this.previewer.refresh();
  24                  case 'postMessage':
  25                      return this.previewer.send( 'setting', [ this.id, this() ] );
  26              }
  27          }
  28      });
  29  
  30      api.Control = api.Class.extend({
  31          initialize: function( id, options ) {
  32              var control = this,
  33                  nodes, radios, settings;
  34  
  35              this.params = {};
  36              $.extend( this, options || {} );
  37  
  38              this.id = id;
  39              this.selector = '#customize-control-' + id.replace( ']', '' ).replace( '[', '-' );
  40              this.container = $( this.selector );
  41  
  42              settings = $.map( this.params.settings, function( value ) {
  43                  return value;
  44              });
  45  
  46              api.apply( api, settings.concat( function() {
  47                  var key;
  48  
  49                  control.settings = {};
  50                  for ( key in control.params.settings ) {
  51                      control.settings[ key ] = api( control.params.settings[ key ] );
  52                  }
  53  
  54                  control.setting = control.settings['default'] || null;
  55                  control.ready();
  56              }) );
  57  
  58              control.elements = [];
  59  
  60              nodes  = this.container.find('[data-customize-setting-link]');
  61              radios = {};
  62  
  63              nodes.each( function() {
  64                  var node = $(this),
  65                      name;
  66  
  67                  if ( node.is(':radio') ) {
  68                      name = node.prop('name');
  69                      if ( radios[ name ] )
  70                          return;
  71  
  72                      radios[ name ] = true;
  73                      node = nodes.filter( '[name="' + name + '"]' );
  74                  }
  75  
  76                  api( node.data('customizeSettingLink'), function( setting ) {
  77                      var element = new api.Element( node );
  78                      control.elements.push( element );
  79                      element.sync( setting );
  80                      element.set( setting() );
  81                  });
  82              });
  83          },
  84  
  85          ready: function() {},
  86  
  87          dropdownInit: function() {
  88              var control  = this,
  89                  statuses = this.container.find('.dropdown-status'),
  90                  params   = this.params,
  91                  update   = function( to ) {
  92                      if ( typeof    to === 'string' && params.statuses && params.statuses[ to ] )
  93                          statuses.html( params.statuses[ to ] ).show();
  94                      else
  95                          statuses.hide();
  96                  };
  97  
  98              var toggleFreeze = false;
  99  
 100              // Support the .dropdown class to open/close complex elements
 101              this.container.on( 'click keydown', '.dropdown', function( event ) {
 102                  if ( event.type === 'keydown' &&  13 !== event.which ) // enter
 103                      return;
 104  
 105                  event.preventDefault();
 106  
 107                  if (!toggleFreeze)
 108                      control.container.toggleClass('open');
 109  
 110                  if ( control.container.hasClass('open') )
 111                      control.container.parent().parent().find('li.library-selected').focus();
 112  
 113                  // Don't want to fire focus and click at same time
 114                  toggleFreeze = true;
 115                  setTimeout(function () {
 116                      toggleFreeze = false;
 117                  }, 400);
 118              });
 119  
 120              this.setting.bind( update );
 121              update( this.setting() );
 122          }
 123      });
 124  
 125      api.ColorControl = api.Control.extend({
 126          ready: function() {
 127              var control = this,
 128                  picker = this.container.find('.color-picker-hex');
 129  
 130              picker.val( control.setting() ).wpColorPicker({
 131                  change: function( event, options ) {
 132                      control.setting.set( picker.wpColorPicker('color') );
 133                   },
 134                   clear: function() {
 135                       control.setting.set( false );
 136                   }
 137              });
 138          }
 139      });
 140  
 141      api.UploadControl = api.Control.extend({
 142          ready: function() {
 143              var control = this;
 144  
 145              this.params.removed = this.params.removed || '';
 146  
 147              this.success = $.proxy( this.success, this );
 148  
 149              this.uploader = $.extend({
 150                  container: this.container,
 151                  browser:   this.container.find('.upload'),
 152                  dropzone:  this.container.find('.upload-dropzone'),
 153                  success:   this.success,
 154                  plupload:  {},
 155                  params:    {}
 156              }, this.uploader || {} );
 157  
 158              if ( control.params.extensions ) {
 159                  control.uploader.plupload.filters = [{
 160                      title:      api.l10n.allowedFiles,
 161                      extensions: control.params.extensions
 162                  }];
 163              }
 164  
 165              if ( control.params.context )
 166                  control.uploader.params['post_data[context]'] = this.params.context;
 167  
 168              if ( api.settings.theme.stylesheet )
 169                  control.uploader.params['post_data[theme]'] = api.settings.theme.stylesheet;
 170  
 171              this.uploader = new wp.Uploader( this.uploader );
 172  
 173              this.remover = this.container.find('.remove');
 174              this.remover.on( 'click keydown', function( event ) {
 175                  if ( event.type === 'keydown' &&  13 !== event.which ) // enter
 176                      return;
 177  
 178                  control.setting.set( control.params.removed );
 179                  event.preventDefault();
 180              });
 181  
 182              this.removerVisibility = $.proxy( this.removerVisibility, this );
 183              this.setting.bind( this.removerVisibility );
 184              this.removerVisibility( this.setting.get() );
 185          },
 186          success: function( attachment ) {
 187              this.setting.set( attachment.get('url') );
 188          },
 189          removerVisibility: function( to ) {
 190              this.remover.toggle( to != this.params.removed );
 191          }
 192      });
 193  
 194      api.ImageControl = api.UploadControl.extend({
 195          ready: function() {
 196              var control = this,
 197                  panels;
 198  
 199              this.uploader = {
 200                  init: function( up ) {
 201                      var fallback, button;
 202  
 203                      if ( this.supports.dragdrop )
 204                          return;
 205  
 206                      // Maintain references while wrapping the fallback button.
 207                      fallback = control.container.find( '.upload-fallback' );
 208                      button   = fallback.children().detach();
 209  
 210                      this.browser.detach().empty().append( button );
 211                      fallback.append( this.browser ).show();
 212                  }
 213              };
 214  
 215              api.UploadControl.prototype.ready.call( this );
 216  
 217              this.thumbnail    = this.container.find('.preview-thumbnail img');
 218              this.thumbnailSrc = $.proxy( this.thumbnailSrc, this );
 219              this.setting.bind( this.thumbnailSrc );
 220  
 221              this.library = this.container.find('.library');
 222  
 223              // Generate tab objects
 224              this.tabs = {};
 225              panels    = this.library.find('.library-content');
 226  
 227              this.library.children('ul').children('li').each( function() {
 228                  var link  = $(this),
 229                      id    = link.data('customizeTab'),
 230                      panel = panels.filter('[data-customize-tab="' + id + '"]');
 231  
 232                  control.tabs[ id ] = {
 233                      both:  link.add( panel ),
 234                      link:  link,
 235                      panel: panel
 236                  };
 237              });
 238  
 239              // Bind tab switch events
 240              this.library.children('ul').on( 'click keydown', 'li', function( event ) {
 241                  if ( event.type === 'keydown' &&  13 !== event.which ) // enter
 242                      return;
 243  
 244                  var id  = $(this).data('customizeTab'),
 245                      tab = control.tabs[ id ];
 246  
 247                  event.preventDefault();
 248  
 249                  if ( tab.link.hasClass('library-selected') )
 250                      return;
 251  
 252                  control.selected.both.removeClass('library-selected');
 253                  control.selected = tab;
 254                  control.selected.both.addClass('library-selected');
 255              });
 256  
 257              // Bind events to switch image urls.
 258              this.library.on( 'click keydown', 'a', function( event ) {
 259                  if ( event.type === 'keydown' && 13 !== event.which ) // enter
 260                      return;
 261  
 262                  var value = $(this).data('customizeImageValue');
 263  
 264                  if ( value ) {
 265                      control.setting.set( value );
 266                      event.preventDefault();
 267                  }
 268              });
 269  
 270              if ( this.tabs.uploaded ) {
 271                  this.tabs.uploaded.target = this.library.find('.uploaded-target');
 272                  if ( ! this.tabs.uploaded.panel.find('.thumbnail').length )
 273                      this.tabs.uploaded.both.addClass('hidden');
 274              }
 275  
 276              // Select a tab
 277              panels.each( function() {
 278                  var tab = control.tabs[ $(this).data('customizeTab') ];
 279  
 280                  // Select the first visible tab.
 281                  if ( ! tab.link.hasClass('hidden') ) {
 282                      control.selected = tab;
 283                      tab.both.addClass('library-selected');
 284                      return false;
 285                  }
 286              });
 287  
 288              this.dropdownInit();
 289          },
 290          success: function( attachment ) {
 291              api.UploadControl.prototype.success.call( this, attachment );
 292  
 293              // Add the uploaded image to the uploaded tab.
 294              if ( this.tabs.uploaded && this.tabs.uploaded.target.length ) {
 295                  this.tabs.uploaded.both.removeClass('hidden');
 296  
 297                  // @todo: Do NOT store this on the attachment model. That is bad.
 298                  attachment.element = $( '<a href="#" class="thumbnail"></a>' )
 299                      .data( 'customizeImageValue', attachment.get('url') )
 300                      .append( '<img src="' +  attachment.get('url')+ '" />' )
 301                      .appendTo( this.tabs.uploaded.target );
 302              }
 303          },
 304          thumbnailSrc: function( to ) {
 305              if ( /^(https?:)?\/\//.test( to ) )
 306                  this.thumbnail.prop( 'src', to ).show();
 307              else
 308                  this.thumbnail.hide();
 309          }
 310      });
 311  
 312      // Change objects contained within the main customize object to Settings.
 313      api.defaultConstructor = api.Setting;
 314  
 315      // Create the collection of Control objects.
 316      api.control = new api.Values({ defaultConstructor: api.Control });
 317  
 318      api.PreviewFrame = api.Messenger.extend({
 319          sensitivity: 2000,
 320  
 321          initialize: function( params, options ) {
 322              var deferred = $.Deferred(),
 323                  self     = this;
 324  
 325              // This is the promise object.
 326              deferred.promise( this );
 327  
 328              this.container = params.container;
 329              this.signature = params.signature;
 330  
 331              $.extend( params, { channel: api.PreviewFrame.uuid() });
 332  
 333              api.Messenger.prototype.initialize.call( this, params, options );
 334  
 335              this.add( 'previewUrl', params.previewUrl );
 336  
 337              this.query = $.extend( params.query || {}, { customize_messenger_channel: this.channel() });
 338  
 339              this.run( deferred );
 340          },
 341  
 342          run: function( deferred ) {
 343              var self   = this,
 344                  loaded = false,
 345                  ready  = false;
 346  
 347              if ( this._ready )
 348                  this.unbind( 'ready', this._ready );
 349  
 350              this._ready = function() {
 351                  ready = true;
 352  
 353                  if ( loaded )
 354                      deferred.resolveWith( self );
 355              };
 356  
 357              this.bind( 'ready', this._ready );
 358  
 359              this.request = $.ajax( this.previewUrl(), {
 360                  type: 'POST',
 361                  data: this.query,
 362                  xhrFields: {
 363                      withCredentials: true
 364                  }
 365              } );
 366  
 367              this.request.fail( function() {
 368                  deferred.rejectWith( self, [ 'request failure' ] );
 369              });
 370  
 371              this.request.done( function( response ) {
 372                  var location = self.request.getResponseHeader('Location'),
 373                      signature = self.signature,
 374                      index;
 375  
 376                  // Check if the location response header differs from the current URL.
 377                  // If so, the request was redirected; try loading the requested page.
 378                  if ( location && location != self.previewUrl() ) {
 379                      deferred.rejectWith( self, [ 'redirect', location ] );
 380                      return;
 381                  }
 382  
 383                  // Check if the user is not logged in.
 384                  if ( '0' === response ) {
 385                      self.login( deferred );
 386                      return;
 387                  }
 388  
 389                  // Check for cheaters.
 390                  if ( '-1' === response ) {
 391                      deferred.rejectWith( self, [ 'cheatin' ] );
 392                      return;
 393                  }
 394  
 395                  // Check for a signature in the request.
 396                  index = response.lastIndexOf( signature );
 397                  if ( -1 === index || index < response.lastIndexOf('</html>') ) {
 398                      deferred.rejectWith( self, [ 'unsigned' ] );
 399                      return;
 400                  }
 401  
 402                  // Strip the signature from the request.
 403                  response = response.slice( 0, index ) + response.slice( index + signature.length );
 404  
 405                  // Create the iframe and inject the html content.
 406                  self.iframe = $('<iframe />').appendTo( self.container );
 407  
 408                  // Bind load event after the iframe has been added to the page;
 409                  // otherwise it will fire when injected into the DOM.
 410                  self.iframe.one( 'load', function() {
 411                      loaded = true;
 412  
 413                      if ( ready ) {
 414                          deferred.resolveWith( self );
 415                      } else {
 416                          setTimeout( function() {
 417                              deferred.rejectWith( self, [ 'ready timeout' ] );
 418                          }, self.sensitivity );
 419                      }
 420                  });
 421  
 422                  self.targetWindow( self.iframe[0].contentWindow );
 423  
 424                  self.targetWindow().document.open();
 425                  self.targetWindow().document.write( response );
 426                  self.targetWindow().document.close();
 427              });
 428          },
 429  
 430          login: function( deferred ) {
 431              var self = this,
 432                  reject;
 433  
 434              reject = function() {
 435                  deferred.rejectWith( self, [ 'logged out' ] );
 436              };
 437  
 438              if ( this.triedLogin )
 439                  return reject();
 440  
 441              // Check if we have an admin cookie.
 442              $.get( api.settings.url.ajax, {
 443                  action: 'logged-in'
 444              }).fail( reject ).done( function( response ) {
 445                  var iframe;
 446  
 447                  if ( '1' !== response )
 448                      reject();
 449  
 450                  iframe = $('<iframe src="' + self.previewUrl() + '" />').hide();
 451                  iframe.appendTo( self.container );
 452                  iframe.load( function() {
 453                      self.triedLogin = true;
 454  
 455                      iframe.remove();
 456                      self.run( deferred );
 457                  });
 458              });
 459          },
 460  
 461          destroy: function() {
 462              api.Messenger.prototype.destroy.call( this );
 463              this.request.abort();
 464  
 465              if ( this.iframe )
 466                  this.iframe.remove();
 467  
 468              delete this.request;
 469              delete this.iframe;
 470              delete this.targetWindow;
 471          }
 472      });
 473  
 474      (function(){
 475          var uuid = 0;
 476          api.PreviewFrame.uuid = function() {
 477              return 'preview-' + uuid++;
 478          };
 479      }());
 480  
 481      api.Previewer = api.Messenger.extend({
 482          refreshBuffer: 250,
 483  
 484          /**
 485           * Requires params:
 486           *  - container  - a selector or jQuery element
 487           *  - previewUrl - the URL of preview frame
 488           */
 489          initialize: function( params, options ) {
 490              var self = this,
 491                  rscheme = /^https?/,
 492                  url;
 493  
 494              $.extend( this, options || {} );
 495  
 496              /*
 497               * Wrap this.refresh to prevent it from hammering the servers:
 498               *
 499               * If refresh is called once and no other refresh requests are
 500               * loading, trigger the request immediately.
 501               *
 502               * If refresh is called while another refresh request is loading,
 503               * debounce the refresh requests:
 504               * 1. Stop the loading request (as it is instantly outdated).
 505               * 2. Trigger the new request once refresh hasn't been called for
 506               *    self.refreshBuffer milliseconds.
 507               */
 508              this.refresh = (function( self ) {
 509                  var refresh  = self.refresh,
 510                      callback = function() {
 511                          timeout = null;
 512                          refresh.call( self );
 513                      },
 514                      timeout;
 515  
 516                  return function() {
 517                      if ( typeof timeout !== 'number' ) {
 518                          if ( self.loading ) {
 519                              self.abort();
 520                          } else {
 521                              return callback();
 522                          }
 523                      }
 524  
 525                      clearTimeout( timeout );
 526                      timeout = setTimeout( callback, self.refreshBuffer );
 527                  };
 528              })( this );
 529  
 530              this.container   = api.ensure( params.container );
 531              this.allowedUrls = params.allowedUrls;
 532              this.signature   = params.signature;
 533  
 534              params.url = window.location.href;
 535  
 536              api.Messenger.prototype.initialize.call( this, params );
 537  
 538              this.add( 'scheme', this.origin() ).link( this.origin ).setter( function( to ) {
 539                  var match = to.match( rscheme );
 540                  return match ? match[0] : '';
 541              });
 542  
 543              // Limit the URL to internal, front-end links.
 544              //
 545              // If the frontend and the admin are served from the same domain, load the
 546              // preview over ssl if the customizer is being loaded over ssl. This avoids
 547              // insecure content warnings. This is not attempted if the admin and frontend
 548              // are on different domains to avoid the case where the frontend doesn't have
 549              // ssl certs.
 550  
 551              this.add( 'previewUrl', params.previewUrl ).setter( function( to ) {
 552                  var result;
 553  
 554                  // Check for URLs that include "/wp-admin/" or end in "/wp-admin".
 555                  // Strip hashes and query strings before testing.
 556                  if ( /\/wp-admin(\/|$)/.test( to.replace(/[#?].*$/, '') ) )
 557                      return null;
 558  
 559                  // Attempt to match the URL to the control frame's scheme
 560                  // and check if it's allowed. If not, try the original URL.
 561                  $.each([ to.replace( rscheme, self.scheme() ), to ], function( i, url ) {
 562                      $.each( self.allowedUrls, function( i, allowed ) {
 563                          if ( 0 === url.indexOf( allowed ) ) {
 564                              result = url;
 565                              return false;
 566                          }
 567                      });
 568                      if ( result )
 569                          return false;
 570                  });
 571  
 572                  // If we found a matching result, return it. If not, bail.
 573                  return result ? result : null;
 574              });
 575  
 576              // Refresh the preview when the URL is changed (but not yet).
 577              this.previewUrl.bind( this.refresh );
 578  
 579              this.scroll = 0;
 580              this.bind( 'scroll', function( distance ) {
 581                  this.scroll = distance;
 582              });
 583  
 584              // Update the URL when the iframe sends a URL message.
 585              this.bind( 'url', this.previewUrl );
 586          },
 587  
 588          query: function() {},
 589  
 590          abort: function() {
 591              if ( this.loading ) {
 592                  this.loading.destroy();
 593                  delete this.loading;
 594              }
 595          },
 596  
 597          refresh: function() {
 598              var self = this;
 599  
 600              this.abort();
 601  
 602              this.loading = new api.PreviewFrame({
 603                  url:        this.url(),
 604                  previewUrl: this.previewUrl(),
 605                  query:      this.query() || {},
 606                  container:  this.container,
 607                  signature:  this.signature
 608              });
 609  
 610              this.loading.done( function() {
 611                  // 'this' is the loading frame
 612                  this.bind( 'synced', function() {
 613                      if ( self.preview )
 614                          self.preview.destroy();
 615                      self.preview = this;
 616                      delete self.loading;
 617  
 618                      self.targetWindow( this.targetWindow() );
 619                      self.channel( this.channel() );
 620  
 621                      self.send( 'active' );
 622                  });
 623  
 624                  this.send( 'sync', {
 625                      scroll:   self.scroll,
 626                      settings: api.get()
 627                  });
 628              });
 629  
 630              this.loading.fail( function( reason, location ) {
 631                  if ( 'redirect' === reason && location )
 632                      self.previewUrl( location );
 633  
 634                  if ( 'logged out' === reason ) {
 635                      if ( self.preview ) {
 636                          self.preview.destroy();
 637                          delete self.preview;
 638                      }
 639  
 640                      self.login().done( self.refresh );
 641                  }
 642  
 643                  if ( 'cheatin' === reason )
 644                      self.cheatin();
 645              });
 646          },
 647  
 648          login: function() {
 649              var previewer = this,
 650                  deferred, messenger, iframe;
 651  
 652              if ( this._login )
 653                  return this._login;
 654  
 655              deferred = $.Deferred();
 656              this._login = deferred.promise();
 657  
 658              messenger = new api.Messenger({
 659                  channel: 'login',
 660                  url:     api.settings.url.login
 661              });
 662  
 663              iframe = $('<iframe src="' + api.settings.url.login + '" />').appendTo( this.container );
 664  
 665              messenger.targetWindow( iframe[0].contentWindow );
 666  
 667              messenger.bind( 'login', function() {
 668                  iframe.remove();
 669                  messenger.destroy();
 670                  delete previewer._login;
 671                  deferred.resolve();
 672              });
 673  
 674              return this._login;
 675          },
 676  
 677          cheatin: function() {
 678              $( document.body ).empty().addClass('cheatin').append( '<p>' + api.l10n.cheatin + '</p>' );
 679          }
 680      });
 681  
 682      /* =====================================================================
 683       * Ready.
 684       * ===================================================================== */
 685  
 686      api.controlConstructor = {
 687          color:  api.ColorControl,
 688          upload: api.UploadControl,
 689          image:  api.ImageControl
 690      };
 691  
 692      $( function() {
 693          api.settings = window._wpCustomizeSettings;
 694          api.l10n = window._wpCustomizeControlsL10n;
 695  
 696          // Check if we can run the customizer.
 697          if ( ! api.settings )
 698              return;
 699  
 700          // Redirect to the fallback preview if any incompatibilities are found.
 701          if ( ! $.support.postMessage || ( ! $.support.cors && api.settings.isCrossDomain ) )
 702              return window.location = api.settings.url.fallback;
 703  
 704          var body = $( document.body ),
 705              overlay = body.children('.wp-full-overlay'),
 706              query, previewer, parent;
 707  
 708          // Prevent the form from saving when enter is pressed.
 709          $('#customize-controls').on( 'keydown', function( e ) {
 710              if ( $( e.target ).is('textarea') )
 711                  return;
 712  
 713              if ( 13 === e.which ) // Enter
 714                  e.preventDefault();
 715          });
 716  
 717          // Initialize Previewer
 718          previewer = new api.Previewer({
 719              container:   '#customize-preview',
 720              form:        '#customize-controls',
 721              previewUrl:  api.settings.url.preview,
 722              allowedUrls: api.settings.url.allowed,
 723              signature:   'WP_CUSTOMIZER_SIGNATURE'
 724          }, {
 725  
 726              nonce: api.settings.nonce,
 727  
 728              query: function() {
 729                  return {
 730                      wp_customize: 'on',
 731                      theme:        api.settings.theme.stylesheet,
 732                      customized:   JSON.stringify( api.get() ),
 733                      nonce:        this.nonce.preview
 734                  };
 735              },
 736  
 737              save: function() {
 738                  var self  = this,
 739                      query = $.extend( this.query(), {
 740                          action: 'customize_save',
 741                          nonce:  this.nonce.save
 742                      }),
 743                      request = $.post( api.settings.url.ajax, query );
 744  
 745                  api.trigger( 'save', request );
 746  
 747                  body.addClass('saving');
 748  
 749                  request.always( function() {
 750                      body.removeClass('saving');
 751                  });
 752  
 753                  request.done( function( response ) {
 754                      // Check if the user is logged out.
 755                      if ( '0' === response ) {
 756                          self.preview.iframe.hide();
 757                          self.login().done( function() {
 758                              self.save();
 759                              self.preview.iframe.show();
 760                          });
 761                          return;
 762                      }
 763  
 764                      // Check for cheaters.
 765                      if ( '-1' === response ) {
 766                          self.cheatin();
 767                          return;
 768                      }
 769  
 770                      api.trigger( 'saved' );
 771                  });
 772              }
 773          });
 774  
 775          // Refresh the nonces if the preview sends updated nonces over.
 776           previewer.bind( 'nonce', function( nonce ) {
 777               $.extend( this.nonce, nonce );
 778           });
 779  
 780          $.each( api.settings.settings, function( id, data ) {
 781              api.create( id, id, data.value, {
 782                  transport: data.transport,
 783                  previewer: previewer
 784              } );
 785          });
 786  
 787          $.each( api.settings.controls, function( id, data ) {
 788              var constructor = api.controlConstructor[ data.type ] || api.Control,
 789                  control;
 790  
 791              control = api.control.add( id, new constructor( id, {
 792                  params: data,
 793                  previewer: previewer
 794              } ) );
 795          });
 796  
 797          // Check if preview url is valid and load the preview frame.
 798          if ( previewer.previewUrl() )
 799              previewer.refresh();
 800          else
 801              previewer.previewUrl( api.settings.url.home );
 802  
 803          // Save and activated states
 804          (function() {
 805              var state = new api.Values(),
 806                  saved = state.create('saved'),
 807                  activated = state.create('activated');
 808  
 809              state.bind( 'change', function() {
 810                  var save = $('#save'),
 811                      back = $('.back');
 812  
 813                  if ( ! activated() ) {
 814                      save.val( api.l10n.activate ).prop( 'disabled', false );
 815                      back.text( api.l10n.cancel );
 816  
 817                  } else if ( saved() ) {
 818                      save.val( api.l10n.saved ).prop( 'disabled', true );
 819                      back.text( api.l10n.close );
 820  
 821                  } else {
 822                      save.val( api.l10n.save ).prop( 'disabled', false );
 823                      back.text( api.l10n.cancel );
 824                  }
 825              });
 826  
 827              // Set default states.
 828              saved( true );
 829              activated( api.settings.theme.active );
 830  
 831              api.bind( 'change', function() {
 832                  state('saved').set( false );
 833              });
 834  
 835              api.bind( 'saved', function() {
 836                  state('saved').set( true );
 837                  state('activated').set( true );
 838              });
 839  
 840              activated.bind( function( to ) {
 841                  if ( to )
 842                      api.trigger( 'activated' );
 843              });
 844  
 845              // Expose states to the API.
 846              api.state = state;
 847          }());
 848  
 849          // Button bindings.
 850          $('#save').click( function( event ) {
 851              previewer.save();
 852              event.preventDefault();
 853          }).keydown( function( event ) {
 854              if ( 9 === event.which ) // tab
 855                  return;
 856              if ( 13 === event.which ) // enter
 857                  previewer.save();
 858              event.preventDefault();
 859          });
 860  
 861          $('.back').keydown( function( event ) {
 862              if ( 9 === event.which ) // tab
 863                  return;
 864              if ( 13 === event.which ) // enter
 865                  parent.send( 'close' );
 866              event.preventDefault();
 867          });
 868  
 869          $('.upload-dropzone a.upload').keydown( function( event ) {
 870              if ( 13 === event.which ) // enter
 871                  this.click();
 872          });
 873  
 874          $('.collapse-sidebar').on( 'click keydown', function( event ) {
 875              if ( event.type === 'keydown' &&  13 !== event.which ) // enter
 876                  return;
 877  
 878              overlay.toggleClass( 'collapsed' ).toggleClass( 'expanded' );
 879              event.preventDefault();
 880          });
 881  
 882          // Create a potential postMessage connection with the parent frame.
 883          parent = new api.Messenger({
 884              url: api.settings.url.parent,
 885              channel: 'loader'
 886          });
 887  
 888          // If we receive a 'back' event, we're inside an iframe.
 889          // Send any clicks to the 'Return' link to the parent page.
 890          parent.bind( 'back', function() {
 891              $('.back').on( 'click.back', function( event ) {
 892                  event.preventDefault();
 893                  parent.send( 'close' );
 894              });
 895          });
 896  
 897          // Pass events through to the parent.
 898          api.bind( 'saved', function() {
 899              parent.send( 'saved' );
 900          });
 901  
 902          // When activated, let the loader handle redirecting the page.
 903          // If no loader exists, redirect the page ourselves (if a url exists).
 904          api.bind( 'activated', function() {
 905              if ( parent.targetWindow() )
 906                  parent.send( 'activated', api.settings.url.activated );
 907              else if ( api.settings.url.activated )
 908                  window.location = api.settings.url.activated;
 909          });
 910  
 911          // Initialize the connection with the parent frame.
 912          parent.send( 'ready' );
 913  
 914          // Control visibility for default controls
 915          $.each({
 916              'background_image': {
 917                  controls: [ 'background_repeat', 'background_position_x', 'background_attachment' ],
 918                  callback: function( to ) { return !! to }
 919              },
 920              'show_on_front': {
 921                  controls: [ 'page_on_front', 'page_for_posts' ],
 922                  callback: function( to ) { return 'page' === to }
 923              },
 924              'header_textcolor': {
 925                  controls: [ 'header_textcolor' ],
 926                  callback: function( to ) { return 'blank' !== to }
 927              }
 928          }, function( settingId, o ) {
 929              api( settingId, function( setting ) {
 930                  $.each( o.controls, function( i, controlId ) {
 931                      api.control( controlId, function( control ) {
 932                          var visibility = function( to ) {
 933                              control.container.toggle( o.callback( to ) );
 934                          };
 935  
 936                          visibility( setting.get() );
 937                          setting.bind( visibility );
 938                      });
 939                  });
 940              });
 941          });
 942  
 943          // Juggle the two controls that use header_textcolor
 944          api.control( 'display_header_text', function( control ) {
 945              var last = '';
 946  
 947              control.elements[0].unsync( api( 'header_textcolor' ) );
 948  
 949              control.element = new api.Element( control.container.find('input') );
 950              control.element.set( 'blank' !== control.setting() );
 951  
 952              control.element.bind( function( to ) {
 953                  if ( ! to )
 954                      last = api( 'header_textcolor' ).get();
 955  
 956                  control.setting.set( to ? last : 'blank' );
 957              });
 958  
 959              control.setting.bind( function( to ) {
 960                  control.element.set( 'blank' !== to );
 961              });
 962          });
 963  
 964          // Handle header image data
 965          api.control( 'header_image', function( control ) {
 966              control.setting.bind( function( to ) {
 967                  if ( to === control.params.removed )
 968                      control.settings.data.set( false );
 969              });
 970  
 971              control.library.on( 'click', 'a', function( event ) {
 972                  control.settings.data.set( $(this).data('customizeHeaderImageData') );
 973              });
 974  
 975              control.uploader.success = function( attachment ) {
 976                  var data;
 977  
 978                  api.ImageControl.prototype.success.call( control, attachment );
 979  
 980                  data = {
 981                      attachment_id: attachment.get('id'),
 982                      url:           attachment.get('url'),
 983                      thumbnail_url: attachment.get('url'),
 984                      height:        attachment.get('height'),
 985                      width:         attachment.get('width')
 986                  };
 987  
 988                  attachment.element.data( 'customizeHeaderImageData', data );
 989                  control.settings.data.set( data );
 990              };
 991          });
 992  
 993          api.trigger( 'ready' );
 994  
 995          // Make sure left column gets focus
 996          var topFocus = $('.back');
 997          topFocus.focus();
 998          setTimeout(function () {
 999              topFocus.focus();
1000          }, 200);
1001  
1002      });
1003  
1004  })( wp, jQuery );


Generated: Wed Jun 19 03:56:30 2013 Hosted by follow the white rabbit.