[ Index ]

WordPress Cross Reference

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              api.Value.prototype.initialize.call( this, value, options );
  12  
  13              this.id = id;
  14              this.transport = this.transport || 'refresh';
  15  
  16              this.bind( this.preview );
  17          },
  18          preview: function() {
  19              switch ( this.transport ) {
  20                  case 'refresh':
  21                      return this.previewer.refresh();
  22                  case 'postMessage':
  23                      return this.previewer.send( 'setting', [ this.id, this() ] );
  24              }
  25          }
  26      });
  27  
  28      api.Control = api.Class.extend({
  29          initialize: function( id, options ) {
  30              var control = this,
  31                  nodes, radios, settings;
  32  
  33              this.params = {};
  34              $.extend( this, options || {} );
  35  
  36              this.id = id;
  37              this.selector = '#customize-control-' + id.replace( /\]/g, '' ).replace( /\[/g, '-' );
  38              this.container = $( this.selector );
  39  
  40              settings = $.map( this.params.settings, function( value ) {
  41                  return value;
  42              });
  43  
  44              api.apply( api, settings.concat( function() {
  45                  var key;
  46  
  47                  control.settings = {};
  48                  for ( key in control.params.settings ) {
  49                      control.settings[ key ] = api( control.params.settings[ key ] );
  50                  }
  51  
  52                  control.setting = control.settings['default'] || null;
  53                  control.ready();
  54              }) );
  55  
  56              control.elements = [];
  57  
  58              nodes  = this.container.find('[data-customize-setting-link]');
  59              radios = {};
  60  
  61              nodes.each( function() {
  62                  var node = $(this),
  63                      name;
  64  
  65                  if ( node.is(':radio') ) {
  66                      name = node.prop('name');
  67                      if ( radios[ name ] )
  68                          return;
  69  
  70                      radios[ name ] = true;
  71                      node = nodes.filter( '[name="' + name + '"]' );
  72                  }
  73  
  74                  api( node.data('customizeSettingLink'), function( setting ) {
  75                      var element = new api.Element( node );
  76                      control.elements.push( element );
  77                      element.sync( setting );
  78                      element.set( setting() );
  79                  });
  80              });
  81          },
  82  
  83          ready: function() {},
  84  
  85          dropdownInit: function() {
  86              var control      = this,
  87                  statuses     = this.container.find('.dropdown-status'),
  88                  params       = this.params,
  89                  toggleFreeze = false,
  90                  update       = function( to ) {
  91                      if ( typeof to === 'string' && params.statuses && params.statuses[ to ] )
  92                          statuses.html( params.statuses[ to ] ).show();
  93                      else
  94                          statuses.hide();
  95                  };
  96  
  97              // Support the .dropdown class to open/close complex elements
  98              this.container.on( 'click keydown', '.dropdown', function( event ) {
  99                  if ( event.type === 'keydown' &&  13 !== event.which ) // enter
 100                      return;
 101  
 102                  event.preventDefault();
 103  
 104                  if (!toggleFreeze)
 105                      control.container.toggleClass('open');
 106  
 107                  if ( control.container.hasClass('open') )
 108                      control.container.parent().parent().find('li.library-selected').focus();
 109  
 110                  // Don't want to fire focus and click at same time
 111                  toggleFreeze = true;
 112                  setTimeout(function () {
 113                      toggleFreeze = false;
 114                  }, 400);
 115              });
 116  
 117              this.setting.bind( update );
 118              update( this.setting() );
 119          }
 120      });
 121  
 122      api.ColorControl = api.Control.extend({
 123          ready: function() {
 124              var control = this,
 125                  picker = this.container.find('.color-picker-hex');
 126  
 127              picker.val( control.setting() ).wpColorPicker({
 128                  change: function() {
 129                      control.setting.set( picker.wpColorPicker('color') );
 130                  },
 131                  clear: function() {
 132                      control.setting.set( false );
 133                  }
 134              });
 135          }
 136      });
 137  
 138      api.UploadControl = api.Control.extend({
 139          ready: function() {
 140              var control = this;
 141  
 142              this.params.removed = this.params.removed || '';
 143  
 144              this.success = $.proxy( this.success, this );
 145  
 146              this.uploader = $.extend({
 147                  container: this.container,
 148                  browser:   this.container.find('.upload'),
 149                  dropzone:  this.container.find('.upload-dropzone'),
 150                  success:   this.success,
 151                  plupload:  {},
 152                  params:    {}
 153              }, this.uploader || {} );
 154  
 155              if ( control.params.extensions ) {
 156                  control.uploader.plupload.filters = [{
 157                      title:      api.l10n.allowedFiles,
 158                      extensions: control.params.extensions
 159                  }];
 160              }
 161  
 162              if ( control.params.context )
 163                  control.uploader.params['post_data[context]'] = this.params.context;
 164  
 165              if ( api.settings.theme.stylesheet )
 166                  control.uploader.params['post_data[theme]'] = api.settings.theme.stylesheet;
 167  
 168              this.uploader = new wp.Uploader( this.uploader );
 169  
 170              this.remover = this.container.find('.remove');
 171              this.remover.on( 'click keydown', function( event ) {
 172                  if ( event.type === 'keydown' &&  13 !== event.which ) // enter
 173                      return;
 174  
 175                  control.setting.set( control.params.removed );
 176                  event.preventDefault();
 177              });
 178  
 179              this.removerVisibility = $.proxy( this.removerVisibility, this );
 180              this.setting.bind( this.removerVisibility );
 181              this.removerVisibility( this.setting.get() );
 182          },
 183          success: function( attachment ) {
 184              this.setting.set( attachment.get('url') );
 185          },
 186          removerVisibility: function( to ) {
 187              this.remover.toggle( to != this.params.removed );
 188          }
 189      });
 190  
 191      api.ImageControl = api.UploadControl.extend({
 192          ready: function() {
 193              var control = this,
 194                  panels;
 195  
 196              this.uploader = {
 197                  init: function() {
 198                      var fallback, button;
 199  
 200                      if ( this.supports.dragdrop )
 201                          return;
 202  
 203                      // Maintain references while wrapping the fallback button.
 204                      fallback = control.container.find( '.upload-fallback' );
 205                      button   = fallback.children().detach();
 206  
 207                      this.browser.detach().empty().append( button );
 208                      fallback.append( this.browser ).show();
 209                  }
 210              };
 211  
 212              api.UploadControl.prototype.ready.call( this );
 213  
 214              this.thumbnail    = this.container.find('.preview-thumbnail img');
 215              this.thumbnailSrc = $.proxy( this.thumbnailSrc, this );
 216              this.setting.bind( this.thumbnailSrc );
 217  
 218              this.library = this.container.find('.library');
 219  
 220              // Generate tab objects
 221              this.tabs = {};
 222              panels    = this.library.find('.library-content');
 223  
 224              this.library.children('ul').children('li').each( function() {
 225                  var link  = $(this),
 226                      id    = link.data('customizeTab'),
 227                      panel = panels.filter('[data-customize-tab="' + id + '"]');
 228  
 229                  control.tabs[ id ] = {
 230                      both:  link.add( panel ),
 231                      link:  link,
 232                      panel: panel
 233                  };
 234              });
 235  
 236              // Bind tab switch events
 237              this.library.children('ul').on( 'click keydown', 'li', function( event ) {
 238                  if ( event.type === 'keydown' &&  13 !== event.which ) // enter
 239                      return;
 240  
 241                  var id  = $(this).data('customizeTab'),
 242                      tab = control.tabs[ id ];
 243  
 244                  event.preventDefault();
 245  
 246                  if ( tab.link.hasClass('library-selected') )
 247                      return;
 248  
 249                  control.selected.both.removeClass('library-selected');
 250                  control.selected = tab;
 251                  control.selected.both.addClass('library-selected');
 252              });
 253  
 254              // Bind events to switch image urls.
 255              this.library.on( 'click keydown', 'a', function( event ) {
 256                  if ( event.type === 'keydown' && 13 !== event.which ) // enter
 257                      return;
 258  
 259                  var value = $(this).data('customizeImageValue');
 260  
 261                  if ( value ) {
 262                      control.setting.set( value );
 263                      event.preventDefault();
 264                  }
 265              });
 266  
 267              if ( this.tabs.uploaded ) {
 268                  this.tabs.uploaded.target = this.library.find('.uploaded-target');
 269                  if ( ! this.tabs.uploaded.panel.find('.thumbnail').length )
 270                      this.tabs.uploaded.both.addClass('hidden');
 271              }
 272  
 273              // Select a tab
 274              panels.each( function() {
 275                  var tab = control.tabs[ $(this).data('customizeTab') ];
 276  
 277                  // Select the first visible tab.
 278                  if ( ! tab.link.hasClass('hidden') ) {
 279                      control.selected = tab;
 280                      tab.both.addClass('library-selected');
 281                      return false;
 282                  }
 283              });
 284  
 285              this.dropdownInit();
 286          },
 287          success: function( attachment ) {
 288              api.UploadControl.prototype.success.call( this, attachment );
 289  
 290              // Add the uploaded image to the uploaded tab.
 291              if ( this.tabs.uploaded && this.tabs.uploaded.target.length ) {
 292                  this.tabs.uploaded.both.removeClass('hidden');
 293  
 294                  // @todo: Do NOT store this on the attachment model. That is bad.
 295                  attachment.element = $( '<a href="#" class="thumbnail"></a>' )
 296                      .data( 'customizeImageValue', attachment.get('url') )
 297                      .append( '<img src="' +  attachment.get('url')+ '" />' )
 298                      .appendTo( this.tabs.uploaded.target );
 299              }
 300          },
 301          thumbnailSrc: function( to ) {
 302              if ( /^(https?:)?\/\//.test( to ) )
 303                  this.thumbnail.prop( 'src', to ).show();
 304              else
 305                  this.thumbnail.hide();
 306          }
 307      });
 308  
 309      // Change objects contained within the main customize object to Settings.
 310      api.defaultConstructor = api.Setting;
 311  
 312      // Create the collection of Control objects.
 313      api.control = new api.Values({ defaultConstructor: api.Control });
 314  
 315      api.PreviewFrame = api.Messenger.extend({
 316          sensitivity: 2000,
 317  
 318          initialize: function( params, options ) {
 319              var deferred = $.Deferred();
 320  
 321              // This is the promise object.
 322              deferred.promise( this );
 323  
 324              this.container = params.container;
 325              this.signature = params.signature;
 326  
 327              $.extend( params, { channel: api.PreviewFrame.uuid() });
 328  
 329              api.Messenger.prototype.initialize.call( this, params, options );
 330  
 331              this.add( 'previewUrl', params.previewUrl );
 332  
 333              this.query = $.extend( params.query || {}, { customize_messenger_channel: this.channel() });
 334  
 335              this.run( deferred );
 336          },
 337  
 338          run: function( deferred ) {
 339              var self   = this,
 340                  loaded = false,
 341                  ready  = false;
 342  
 343              if ( this._ready )
 344                  this.unbind( 'ready', this._ready );
 345  
 346              this._ready = function() {
 347                  ready = true;
 348  
 349                  if ( loaded )
 350                      deferred.resolveWith( self );
 351              };
 352  
 353              this.bind( 'ready', this._ready );
 354  
 355              this.request = $.ajax( this.previewUrl(), {
 356                  type: 'POST',
 357                  data: this.query,
 358                  xhrFields: {
 359                      withCredentials: true
 360                  }
 361              } );
 362  
 363              this.request.fail( function() {
 364                  deferred.rejectWith( self, [ 'request failure' ] );
 365              });
 366  
 367              this.request.done( function( response ) {
 368                  var location = self.request.getResponseHeader('Location'),
 369                      signature = self.signature,
 370                      index;
 371  
 372                  // Check if the location response header differs from the current URL.
 373                  // If so, the request was redirected; try loading the requested page.
 374                  if ( location && location != self.previewUrl() ) {
 375                      deferred.rejectWith( self, [ 'redirect', location ] );
 376                      return;
 377                  }
 378  
 379                  // Check if the user is not logged in.
 380                  if ( '0' === response ) {
 381                      self.login( deferred );
 382                      return;
 383                  }
 384  
 385                  // Check for cheaters.
 386                  if ( '-1' === response ) {
 387                      deferred.rejectWith( self, [ 'cheatin' ] );
 388                      return;
 389                  }
 390  
 391                  // Check for a signature in the request.
 392                  index = response.lastIndexOf( signature );
 393                  if ( -1 === index || index < response.lastIndexOf('</html>') ) {
 394                      deferred.rejectWith( self, [ 'unsigned' ] );
 395                      return;
 396                  }
 397  
 398                  // Strip the signature from the request.
 399                  response = response.slice( 0, index ) + response.slice( index + signature.length );
 400  
 401                  // Create the iframe and inject the html content.
 402                  self.iframe = $('<iframe />').appendTo( self.container );
 403  
 404                  // Bind load event after the iframe has been added to the page;
 405                  // otherwise it will fire when injected into the DOM.
 406                  self.iframe.one( 'load', function() {
 407                      loaded = true;
 408  
 409                      if ( ready ) {
 410                          deferred.resolveWith( self );
 411                      } else {
 412                          setTimeout( function() {
 413                              deferred.rejectWith( self, [ 'ready timeout' ] );
 414                          }, self.sensitivity );
 415                      }
 416                  });
 417  
 418                  self.targetWindow( self.iframe[0].contentWindow );
 419  
 420                  self.targetWindow().document.open();
 421                  self.targetWindow().document.write( response );
 422                  self.targetWindow().document.close();
 423              });
 424          },
 425  
 426          login: function( deferred ) {
 427              var self = this,
 428                  reject;
 429  
 430              reject = function() {
 431                  deferred.rejectWith( self, [ 'logged out' ] );
 432              };
 433  
 434              if ( this.triedLogin )
 435                  return reject();
 436  
 437              // Check if we have an admin cookie.
 438              $.get( api.settings.url.ajax, {
 439                  action: 'logged-in'
 440              }).fail( reject ).done( function( response ) {
 441                  var iframe;
 442  
 443                  if ( '1' !== response )
 444                      reject();
 445  
 446                  iframe = $('<iframe src="' + self.previewUrl() + '" />').hide();
 447                  iframe.appendTo( self.container );
 448                  iframe.load( function() {
 449                      self.triedLogin = true;
 450  
 451                      iframe.remove();
 452                      self.run( deferred );
 453                  });
 454              });
 455          },
 456  
 457          destroy: function() {
 458              api.Messenger.prototype.destroy.call( this );
 459              this.request.abort();
 460  
 461              if ( this.iframe )
 462                  this.iframe.remove();
 463  
 464              delete this.request;
 465              delete this.iframe;
 466              delete this.targetWindow;
 467          }
 468      });
 469  
 470      (function(){
 471          var uuid = 0;
 472          api.PreviewFrame.uuid = function() {
 473              return 'preview-' + uuid++;
 474          };
 475      }());
 476  
 477      api.Previewer = api.Messenger.extend({
 478          refreshBuffer: 250,
 479  
 480          /**
 481           * Requires params:
 482           *  - container  - a selector or jQuery element
 483           *  - previewUrl - the URL of preview frame
 484           */
 485          initialize: function( params, options ) {
 486              var self = this,
 487                  rscheme = /^https?/;
 488  
 489              $.extend( this, options || {} );
 490  
 491              /*
 492               * Wrap this.refresh to prevent it from hammering the servers:
 493               *
 494               * If refresh is called once and no other refresh requests are
 495               * loading, trigger the request immediately.
 496               *
 497               * If refresh is called while another refresh request is loading,
 498               * debounce the refresh requests:
 499               * 1. Stop the loading request (as it is instantly outdated).
 500               * 2. Trigger the new request once refresh hasn't been called for
 501               *    self.refreshBuffer milliseconds.
 502               */
 503              this.refresh = (function( self ) {
 504                  var refresh  = self.refresh,
 505                      callback = function() {
 506                          timeout = null;
 507                          refresh.call( self );
 508                      },
 509                      timeout;
 510  
 511                  return function() {
 512                      if ( typeof timeout !== 'number' ) {
 513                          if ( self.loading ) {
 514                              self.abort();
 515                          } else {
 516                              return callback();
 517                          }
 518                      }
 519  
 520                      clearTimeout( timeout );
 521                      timeout = setTimeout( callback, self.refreshBuffer );
 522                  };
 523              })( this );
 524  
 525              this.container   = api.ensure( params.container );
 526              this.allowedUrls = params.allowedUrls;
 527              this.signature   = params.signature;
 528  
 529              params.url = window.location.href;
 530  
 531              api.Messenger.prototype.initialize.call( this, params );
 532  
 533              this.add( 'scheme', this.origin() ).link( this.origin ).setter( function( to ) {
 534                  var match = to.match( rscheme );
 535                  return match ? match[0] : '';
 536              });
 537  
 538              // Limit the URL to internal, front-end links.
 539              //
 540              // If the frontend and the admin are served from the same domain, load the
 541              // preview over ssl if the customizer is being loaded over ssl. This avoids
 542              // insecure content warnings. This is not attempted if the admin and frontend
 543              // are on different domains to avoid the case where the frontend doesn't have
 544              // ssl certs.
 545  
 546              this.add( 'previewUrl', params.previewUrl ).setter( function( to ) {
 547                  var result;
 548  
 549                  // Check for URLs that include "/wp-admin/" or end in "/wp-admin".
 550                  // Strip hashes and query strings before testing.
 551                  if ( /\/wp-admin(\/|$)/.test( to.replace( /[#?].*$/, '' ) ) )
 552                      return null;
 553  
 554                  // Attempt to match the URL to the control frame's scheme
 555                  // and check if it's allowed. If not, try the original URL.
 556                  $.each([ to.replace( rscheme, self.scheme() ), to ], function( i, url ) {
 557                      $.each( self.allowedUrls, function( i, allowed ) {
 558                          var path;
 559  
 560                          allowed = allowed.replace( /\/+$/, '' );
 561                          path = url.replace( allowed, '' );
 562  
 563                          if ( 0 === url.indexOf( allowed ) && /^([/#?]|$)/.test( path ) ) {
 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 previewer, parent, topFocus,
 705              body = $( document.body ),
 706              overlay = body.children('.wp-full-overlay');
 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                  this.click();
 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() {
 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          topFocus = $('.back');
 997          topFocus.focus();
 998          setTimeout(function () {
 999              topFocus.focus();
1000          }, 200);
1001  
1002      });
1003  
1004  })( wp, jQuery );


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