[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/ -> media-editor.js (source)

   1  /**
   2   * @output wp-includes/js/media-editor.js
   3   */
   4  
   5  /* global getUserSetting, tinymce, QTags */
   6  
   7  /**
   8   * Handles the initialization, refreshing and rendering of media editor components.
   9   *
  10   * @param {JQueryStatic}       $ The jQuery object.
  11   * @param {_.UnderscoreStatic} _ The Underscore.js object.
  12   */
  13  (function($, _){
  14      /**
  15       * Stores the editors' `wp.media.controller.Frame` instances.
  16       *
  17       * @static
  18       */
  19      var workflows = {};
  20  
  21      /**
  22       * A helper mixin function to avoid truthy and falsey values being
  23       * passed as an input that expects booleans. If key is undefined in the map,
  24       * but has a default value, set it.
  25       *
  26       * @param {Object} attrs Map of props from a shortcode or settings.
  27       * @param {string} key   The key within the passed map to check for a value.
  28       * @return {mixed|undefined} The original or coerced value of key within attrs.
  29       */
  30      wp.media.coerce = function ( attrs, key ) {
  31          if ( _.isUndefined( attrs[ key ] ) && ! _.isUndefined( this.defaults[ key ] ) ) {
  32              attrs[ key ] = this.defaults[ key ];
  33          } else if ( 'true' === attrs[ key ] ) {
  34              attrs[ key ] = true;
  35          } else if ( 'false' === attrs[ key ] ) {
  36              attrs[ key ] = false;
  37          }
  38          return attrs[ key ];
  39      };
  40  
  41      /** @namespace wp.media.string */
  42      wp.media.string = {
  43          /**
  44           * Joins the `props` and `attachment` objects,
  45           * outputting the proper object format based on the
  46           * attachment's type.
  47           *
  48           * @param {Object} [props={}] Attachment details (align, link, size, etc).
  49           * @param {Object} attachment The attachment object, media version of Post.
  50           * @return {Object} Joined props
  51           */
  52          props: function( props, attachment ) {
  53              var link, linkUrl, size, sizes,
  54                  defaultProps = wp.media.view.settings.defaultProps;
  55  
  56              props = props ? _.clone( props ) : {};
  57  
  58              if ( attachment && attachment.type ) {
  59                  props.type = attachment.type;
  60              }
  61  
  62              if ( 'image' === props.type ) {
  63                  props = _.defaults( props || {}, {
  64                      align:   defaultProps.align || getUserSetting( 'align', 'none' ),
  65                      size:    defaultProps.size  || getUserSetting( 'imgsize', 'medium' ),
  66                      url:     '',
  67                      classes: []
  68                  });
  69              }
  70  
  71              // All attachment-specific settings follow.
  72              if ( ! attachment ) {
  73                  return props;
  74              }
  75  
  76              props.title = props.title || attachment.title;
  77  
  78              link = props.link || defaultProps.link || getUserSetting( 'urlbutton', 'file' );
  79              if ( 'file' === link || 'embed' === link ) {
  80                  linkUrl = attachment.url;
  81              } else if ( 'post' === link ) {
  82                  linkUrl = attachment.link;
  83              } else if ( 'custom' === link ) {
  84                  linkUrl = props.linkUrl;
  85              }
  86              props.linkUrl = linkUrl || '';
  87  
  88              // Format properties for images.
  89              if ( 'image' === attachment.type ) {
  90                  props.classes.push( 'wp-image-' + attachment.id );
  91  
  92                  sizes = attachment.sizes;
  93                  size = sizes && sizes[ props.size ] ? sizes[ props.size ] : attachment;
  94  
  95                  _.extend( props, _.pick( attachment, 'align', 'caption', 'alt' ), {
  96                      width:     size.width,
  97                      height:    size.height,
  98                      src:       size.url,
  99                      captionId: 'attachment_' + attachment.id
 100                  });
 101              } else if ( 'video' === attachment.type || 'audio' === attachment.type ) {
 102                  _.extend( props, _.pick( attachment, 'title', 'type', 'icon', 'mime' ) );
 103              // Format properties for non-images.
 104              } else {
 105                  props.title = props.title || attachment.filename;
 106                  props.rel = props.rel || 'attachment wp-att-' + attachment.id;
 107              }
 108  
 109              return props;
 110          },
 111          /**
 112           * Create link markup that is suitable for passing to the editor
 113           *
 114           * @param {Object} props      Attachment details (align, link, size, etc).
 115           * @param {Object} attachment The attachment object, media version of Post.
 116           * @return {string} The link markup
 117           */
 118          link: function( props, attachment ) {
 119              var options;
 120  
 121              props = wp.media.string.props( props, attachment );
 122  
 123              options = {
 124                  tag:     'a',
 125                  content: props.title,
 126                  attrs:   {
 127                      href: props.linkUrl
 128                  }
 129              };
 130  
 131              if ( props.rel ) {
 132                  options.attrs.rel = props.rel;
 133              }
 134  
 135              return wp.html.string( options );
 136          },
 137          /**
 138           * Create an Audio shortcode string that is suitable for passing to the editor
 139           *
 140           * @param {Object} props      Attachment details (align, link, size, etc).
 141           * @param {Object} attachment The attachment object, media version of Post.
 142           * @return {string} The audio shortcode
 143           */
 144          audio: function( props, attachment ) {
 145              return wp.media.string._audioVideo( 'audio', props, attachment );
 146          },
 147          /**
 148           * Create a Video shortcode string that is suitable for passing to the editor
 149           *
 150           * @param {Object} props      Attachment details (align, link, size, etc).
 151           * @param {Object} attachment The attachment object, media version of Post.
 152           * @return {string} The video shortcode
 153           */
 154          video: function( props, attachment ) {
 155              return wp.media.string._audioVideo( 'video', props, attachment );
 156          },
 157          /**
 158           * Helper function to create a media shortcode string
 159           *
 160           * @access private
 161           *
 162           * @param {string} type       The shortcode tag name: 'audio' or 'video'.
 163           * @param {Object} props      Attachment details (align, link, size, etc).
 164           * @param {Object} attachment The attachment object, media version of Post.
 165           * @return {string} The media shortcode
 166           */
 167          _audioVideo: function( type, props, attachment ) {
 168              var shortcode, html, extension;
 169  
 170              props = wp.media.string.props( props, attachment );
 171              if ( props.link !== 'embed' ) {
 172                  return wp.media.string.link( props );
 173              }
 174  
 175              shortcode = {};
 176  
 177              if ( 'video' === type ) {
 178                  if ( attachment.image && -1 === attachment.image.src.indexOf( attachment.icon ) ) {
 179                      shortcode.poster = attachment.image.src;
 180                  }
 181  
 182                  if ( attachment.width ) {
 183                      shortcode.width = attachment.width;
 184                  }
 185  
 186                  if ( attachment.height ) {
 187                      shortcode.height = attachment.height;
 188                  }
 189              }
 190  
 191              extension = attachment.filename.split('.').pop();
 192  
 193              if ( _.contains( wp.media.view.settings.embedExts, extension ) ) {
 194                  shortcode[extension] = attachment.url;
 195              } else {
 196                  // Render unsupported audio and video files as links.
 197                  return wp.media.string.link( props );
 198              }
 199  
 200              html = wp.shortcode.string({
 201                  tag:     type,
 202                  attrs:   shortcode
 203              });
 204  
 205              return html;
 206          },
 207          /**
 208           * Create image markup, optionally with a link and/or wrapped in a caption shortcode,
 209           * that is suitable for passing to the editor
 210           *
 211           * @param {Object} props      Attachment details (align, link, size, etc).
 212           * @param {Object} attachment The attachment object, media version of Post.
 213           * @return {string} The image markup.
 214           */
 215          image: function( props, attachment ) {
 216              var img = {},
 217                  options, classes, shortcode, html;
 218  
 219              props.type = 'image';
 220              props = wp.media.string.props( props, attachment );
 221              classes = props.classes || [];
 222  
 223              img.src = ! _.isUndefined( attachment ) ? attachment.url : props.url;
 224              _.extend( img, _.pick( props, 'width', 'height', 'alt' ) );
 225  
 226              // Only assign the align class to the image if we're not printing
 227              // a caption, since the alignment is sent to the shortcode.
 228              if ( props.align && ! props.caption ) {
 229                  classes.push( 'align' + props.align );
 230              }
 231  
 232              if ( props.size ) {
 233                  classes.push( 'size-' + props.size );
 234              }
 235  
 236              img['class'] = _.compact( classes ).join(' ');
 237  
 238              // Generate `img` tag options.
 239              options = {
 240                  tag:    'img',
 241                  attrs:  img,
 242                  single: true
 243              };
 244  
 245              // Generate the `a` element options, if they exist.
 246              if ( props.linkUrl ) {
 247                  options = {
 248                      tag:   'a',
 249                      attrs: {
 250                          href: props.linkUrl
 251                      },
 252                      content: options
 253                  };
 254              }
 255  
 256              html = wp.html.string( options );
 257  
 258              // Generate the caption shortcode.
 259              if ( props.caption ) {
 260                  shortcode = {};
 261  
 262                  if ( img.width ) {
 263                      shortcode.width = img.width;
 264                  }
 265  
 266                  if ( props.captionId ) {
 267                      shortcode.id = props.captionId;
 268                  }
 269  
 270                  if ( props.align ) {
 271                      shortcode.align = 'align' + props.align;
 272                  }
 273  
 274                  html = wp.shortcode.string({
 275                      tag:     'caption',
 276                      attrs:   shortcode,
 277                      content: html + ' ' + props.caption
 278                  });
 279              }
 280  
 281              return html;
 282          }
 283      };
 284  
 285      wp.media.embed = {
 286          coerce : wp.media.coerce,
 287  
 288          defaults : {
 289              url : '',
 290              width: '',
 291              height: ''
 292          },
 293  
 294          edit : function( data, isURL ) {
 295              var frame, props = {}, shortcode;
 296  
 297              if ( isURL ) {
 298                  props.url = data.replace(/<[^>]+>/g, '');
 299              } else {
 300                  shortcode = wp.shortcode.next( 'embed', data ).shortcode;
 301  
 302                  props = _.defaults( shortcode.attrs.named, this.defaults );
 303                  if ( shortcode.content ) {
 304                      props.url = shortcode.content;
 305                  }
 306              }
 307  
 308              frame = wp.media({
 309                  frame: 'post',
 310                  state: 'embed',
 311                  metadata: props
 312              });
 313  
 314              return frame;
 315          },
 316  
 317          shortcode : function( model ) {
 318              var self = this, content;
 319  
 320              _.each( this.defaults, function( value, key ) {
 321                  model[ key ] = self.coerce( model, key );
 322  
 323                  if ( value === model[ key ] ) {
 324                      delete model[ key ];
 325                  }
 326              });
 327  
 328              content = model.url;
 329              delete model.url;
 330  
 331              return new wp.shortcode({
 332                  tag: 'embed',
 333                  attrs: model,
 334                  content: content
 335              });
 336          }
 337      };
 338  
 339      /**
 340       * Factory function that creates a media collection controller for managing gallery, playlist, and other media shortcodes.
 341       *
 342       * @class wp.media.collection
 343       *
 344       * @param {Object} attributes The attributes for the media collection.
 345       */
 346      wp.media.collection = function(attributes) {
 347          var collections = {};
 348  
 349          return _.extend(/** @lends wp.media.collection.prototype */{
 350              coerce : wp.media.coerce,
 351              /**
 352               * Retrieve attachments based on the properties of the passed shortcode
 353               *
 354               * @param {wp.shortcode} shortcode An instance of wp.shortcode().
 355               * @return {wp.media.model.Attachments} A Backbone.Collection containing
 356               *                                      the media items belonging to a collection.
 357               *                                      The query[ this.tag ] property is a Backbone.Model
 358               *                                      containing the 'props' for the collection.
 359               */
 360              attachments: function( shortcode ) {
 361                  var shortcodeString = shortcode.string(),
 362                      result = collections[ shortcodeString ],
 363                      attrs, args, query, others, self = this;
 364  
 365                  delete collections[ shortcodeString ];
 366                  if ( result ) {
 367                      return result;
 368                  }
 369                  // Fill the default shortcode attributes.
 370                  attrs = _.defaults( shortcode.attrs.named, this.defaults );
 371                  args  = _.pick( attrs, 'orderby', 'order' );
 372  
 373                  args.type    = this.type;
 374                  args.perPage = -1;
 375  
 376                  // Mark the `orderby` override attribute.
 377                  if ( undefined !== attrs.orderby ) {
 378                      attrs._orderByField = attrs.orderby;
 379                  }
 380  
 381                  if ( 'rand' === attrs.orderby ) {
 382                      attrs._orderbyRandom = true;
 383                  }
 384  
 385                  // Map the `orderby` attribute to the corresponding model property.
 386                  if ( ! attrs.orderby || /^menu_order(?: ID)?$/i.test( attrs.orderby ) ) {
 387                      args.orderby = 'menuOrder';
 388                  }
 389  
 390                  // Map the `ids` param to the correct query args.
 391                  if ( attrs.ids ) {
 392                      args.post__in = attrs.ids.split(',');
 393                      args.orderby  = 'post__in';
 394                  } else if ( attrs.include ) {
 395                      args.post__in = attrs.include.split(',');
 396                  }
 397  
 398                  if ( attrs.exclude ) {
 399                      args.post__not_in = attrs.exclude.split(',');
 400                  }
 401  
 402                  if ( ! args.post__in ) {
 403                      args.uploadedTo = attrs.id;
 404                  }
 405  
 406                  // Collect the attributes that were not included in `args`.
 407                  others = _.omit( attrs, 'id', 'ids', 'include', 'exclude', 'orderby', 'order' );
 408  
 409                  _.each( this.defaults, function( value, key ) {
 410                      others[ key ] = self.coerce( others, key );
 411                  });
 412  
 413                  query = wp.media.query( args );
 414                  query[ this.tag ] = new Backbone.Model( others );
 415                  return query;
 416              },
 417              /**
 418               * Triggered when clicking 'Insert {label}' or 'Update {label}'
 419               *
 420               * @param {wp.media.model.Attachments} attachments A Backbone.Collection containing
 421               *                                                 the media items belonging to a collection.
 422               *                                                 The query[ this.tag ] property is a Backbone.Model
 423               *                                                 containing the 'props' for the collection.
 424               * @return {wp.shortcode} A wp.shortcode instance representing the collection.
 425               */
 426              shortcode: function( attachments ) {
 427                  var props = attachments.props.toJSON(),
 428                      attrs = _.pick( props, 'orderby', 'order' ),
 429                      shortcode, clone;
 430  
 431                  if ( attachments.type ) {
 432                      attrs.type = attachments.type;
 433                      delete attachments.type;
 434                  }
 435  
 436                  if ( attachments[this.tag] ) {
 437                      _.extend( attrs, attachments[this.tag].toJSON() );
 438                  }
 439  
 440                  /*
 441                   * Convert all gallery shortcodes to use the `ids` property.
 442                   * Ignore `post__in` and `post__not_in`; the attachments in
 443                   * the collection will already reflect those properties.
 444                   */
 445                  attrs.ids = attachments.pluck('id');
 446  
 447                  // Copy the `uploadedTo` post ID.
 448                  if ( props.uploadedTo ) {
 449                      attrs.id = props.uploadedTo;
 450                  }
 451                  // Check if the gallery is randomly ordered.
 452                  delete attrs.orderby;
 453  
 454                  if ( attrs._orderbyRandom ) {
 455                      attrs.orderby = 'rand';
 456                  } else if ( attrs._orderByField && 'rand' !== attrs._orderByField ) {
 457                      attrs.orderby = attrs._orderByField;
 458                  }
 459  
 460                  delete attrs._orderbyRandom;
 461                  delete attrs._orderByField;
 462  
 463                  // If the `ids` attribute is set and `orderby` attribute
 464                  // is the default value, clear it for cleaner output.
 465                  if ( attrs.ids && 'post__in' === attrs.orderby ) {
 466                      delete attrs.orderby;
 467                  }
 468  
 469                  attrs = this.setDefaults( attrs );
 470  
 471                  shortcode = new wp.shortcode({
 472                      tag:    this.tag,
 473                      attrs:  attrs,
 474                      type:   'single'
 475                  });
 476  
 477                  // Use a cloned version of the gallery.
 478                  clone = new wp.media.model.Attachments( attachments.models, {
 479                      props: props
 480                  });
 481                  clone[ this.tag ] = attachments[ this.tag ];
 482                  collections[ shortcode.string() ] = clone;
 483  
 484                  return shortcode;
 485              },
 486              /**
 487               * Triggered when double-clicking a collection shortcode placeholder
 488               * in the editor
 489               *
 490               * @param {string} content Content that is searched for possible
 491               *                         shortcode markup matching the passed tag name,
 492               *
 493               * @this {wp.media.view.MediaFrame.Select}
 494               *
 495               * @return {void|wp.media.view.MediaFrame.Select} A media workflow.
 496               */
 497              edit: function( content ) {
 498                  var shortcode = wp.shortcode.next( this.tag, content ),
 499                      defaultPostId = this.defaults.id,
 500                      attachments, selection, state;
 501  
 502                  // Bail if we didn't match the shortcode or all of the content.
 503                  if ( ! shortcode || shortcode.content !== content ) {
 504                      return;
 505                  }
 506  
 507                  // Ignore the rest of the match object.
 508                  shortcode = shortcode.shortcode;
 509  
 510                  if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) ) {
 511                      shortcode.set( 'id', defaultPostId );
 512                  }
 513  
 514                  attachments = this.attachments( shortcode );
 515  
 516                  selection = new wp.media.model.Selection( attachments.models, {
 517                      props:    attachments.props.toJSON(),
 518                      multiple: true
 519                  });
 520  
 521                  selection[ this.tag ] = attachments[ this.tag ];
 522  
 523                  // Fetch the query's attachments, and then break ties from the
 524                  // query to allow for sorting.
 525                  selection.more().done( function() {
 526                      // Break ties with the query.
 527                      selection.props.set({ query: false });
 528                      selection.unmirror();
 529                      selection.props.unset('orderby');
 530                  });
 531  
 532                  // Destroy the previous gallery frame.
 533                  if ( this.frame ) {
 534                      this.frame.dispose();
 535                  }
 536  
 537                  if ( shortcode.attrs.named.type && 'video' === shortcode.attrs.named.type ) {
 538                      state = 'video-' + this.tag + '-edit';
 539                  } else {
 540                      state = this.tag + '-edit';
 541                  }
 542  
 543                  // Store the current frame.
 544                  this.frame = wp.media({
 545                      frame:     'post',
 546                      state:     state,
 547                      title:     this.editTitle,
 548                      editing:   true,
 549                      multiple:  true,
 550                      selection: selection
 551                  }).open();
 552  
 553                  return this.frame;
 554              },
 555  
 556              setDefaults: function( attrs ) {
 557                  var self = this;
 558                  // Remove default attributes from the shortcode.
 559                  _.each( this.defaults, function( value, key ) {
 560                      attrs[ key ] = self.coerce( attrs, key );
 561                      if ( value === attrs[ key ] ) {
 562                          delete attrs[ key ];
 563                      }
 564                  });
 565  
 566                  return attrs;
 567              }
 568          }, attributes );
 569      };
 570  
 571      wp.media._galleryDefaults = {
 572          itemtag: 'dl',
 573          icontag: 'dt',
 574          captiontag: 'dd',
 575          columns: '3',
 576          link: 'post',
 577          size: 'thumbnail',
 578          order: 'ASC',
 579          id: wp.media.view.settings.post && wp.media.view.settings.post.id,
 580          orderby : 'menu_order ID'
 581      };
 582  
 583      if ( wp.media.view.settings.galleryDefaults ) {
 584          wp.media.galleryDefaults = _.extend( {}, wp.media._galleryDefaults, wp.media.view.settings.galleryDefaults );
 585      } else {
 586          wp.media.galleryDefaults = wp.media._galleryDefaults;
 587      }
 588  
 589      wp.media.gallery = new wp.media.collection({
 590          tag: 'gallery',
 591          type : 'image',
 592          editTitle : wp.media.view.l10n.editGalleryTitle,
 593          defaults : wp.media.galleryDefaults,
 594  
 595          setDefaults: function( attrs ) {
 596              var self = this, changed = ! _.isEqual( wp.media.galleryDefaults, wp.media._galleryDefaults );
 597              _.each( this.defaults, function( value, key ) {
 598                  attrs[ key ] = self.coerce( attrs, key );
 599                  if ( value === attrs[ key ] && ( ! changed || value === wp.media._galleryDefaults[ key ] ) ) {
 600                      delete attrs[ key ];
 601                  }
 602              } );
 603              return attrs;
 604          }
 605      });
 606  
 607      /**
 608       * @namespace wp.media.featuredImage
 609       * @memberOf wp.media
 610       */
 611      wp.media.featuredImage = {
 612          /**
 613           * Get the featured image post ID
 614           *
 615           * @return {wp.media.view.settings.post.featuredImageId|number} The post ID of the featured image, or -1 if none is set.
 616           */
 617          get: function() {
 618              return wp.media.view.settings.post.featuredImageId;
 619          },
 620          /**
 621           * Sets the featured image ID property and sets the HTML in the post meta box to the new featured image.
 622           *
 623           * @param {number} id The post ID of the featured image, or -1 to unset it.
 624           */
 625          set: function( id ) {
 626              var settings = wp.media.view.settings;
 627  
 628              settings.post.featuredImageId = id;
 629  
 630              wp.media.post( 'get-post-thumbnail-html', {
 631                  post_id:      settings.post.id,
 632                  thumbnail_id: settings.post.featuredImageId,
 633                  _wpnonce:     settings.post.nonce
 634              }).done( function( html ) {
 635                  if ( '0' === html ) {
 636                      window.alert( wp.i18n.__( 'Could not set that as the thumbnail image. Try a different attachment.' ) );
 637                      return;
 638                  }
 639                  $( '.inside', '#postimagediv' ).html( html );
 640              });
 641          },
 642          /**
 643           * Remove the featured image id, save the post thumbnail data and
 644           * set the HTML in the post meta box to no featured image.
 645           */
 646          remove: function() {
 647              wp.media.featuredImage.set( -1 );
 648          },
 649          /**
 650           * The Featured Image workflow
 651           *
 652           * @this {wp.media.featuredImage}
 653           *
 654           * @return {wp.media.view.MediaFrame.Select} A media workflow.
 655           */
 656          frame: function() {
 657              if ( this._frame ) {
 658                  wp.media.frame = this._frame;
 659                  return this._frame;
 660              }
 661  
 662              this._frame = wp.media({
 663                  state: 'featured-image',
 664                  states: [ new wp.media.controller.FeaturedImage() , new wp.media.controller.EditImage() ]
 665              });
 666  
 667              this._frame.on( 'toolbar:create:featured-image', function( toolbar ) {
 668                  /**
 669                   * @this {wp.media.view.MediaFrame.Select}
 670                   */
 671                  this.createSelectToolbar( toolbar, {
 672                      text: wp.media.view.l10n.setFeaturedImage
 673                  });
 674              }, this._frame );
 675  
 676              this._frame.on( 'content:render:edit-image', function() {
 677                  var selection = this.state('featured-image').get('selection'),
 678                      view = new wp.media.view.EditImage( { model: selection.single(), controller: this } ).render();
 679  
 680                  this.content.set( view );
 681  
 682                  // After bringing in the frame, load the actual editor via an Ajax call.
 683                  view.loadEditor();
 684  
 685              }, this._frame );
 686  
 687              this._frame.state('featured-image').on( 'select', this.select );
 688              return this._frame;
 689          },
 690          /**
 691           * 'select' callback for Featured Image workflow, triggered when
 692           * the 'Set Featured Image' button is clicked in the media modal.
 693           *
 694           * @this {wp.media.controller.FeaturedImage}
 695           */
 696          select: function() {
 697              var selection = this.get('selection').single();
 698  
 699              if ( ! wp.media.view.settings.post.featuredImageId ) {
 700                  return;
 701              }
 702  
 703              wp.media.featuredImage.set( selection ? selection.id : -1 );
 704          },
 705          /**
 706           * Open the content media manager to the 'featured image' tab when
 707           * the post thumbnail is clicked.
 708           *
 709           * Update the featured image id when the 'remove' link is clicked.
 710           */
 711          init: function() {
 712              $('#postimagediv').on( 'click keyup keydown', '#set-post-thumbnail', function( event ) {
 713                  if ( ( event.type === 'keyup' && event.key === ' ' ) || ( event.type === 'keydown' && event.key === 'Enter' ) || event.type === 'click' ) {
 714                      event.preventDefault();
 715                      // Stop propagation to prevent thickbox from activating.
 716                      event.stopPropagation();
 717  
 718                      wp.media.featuredImage.frame().open();
 719                  }
 720              }).on( 'click keyup keydown', '#remove-post-thumbnail', function( event ) {
 721                  if ( ( event.type === 'keyup' && event.key === ' ' ) || ( event.type === 'keydown' && event.key === 'Enter' ) || event.type === 'click' ) {
 722                      wp.media.featuredImage.remove();
 723                      return false;
 724                  }
 725              });
 726          }
 727      };
 728  
 729      $( wp.media.featuredImage.init );
 730  
 731      /** @namespace wp.media.editor */
 732      wp.media.editor = {
 733          /**
 734           * Send content to the editor
 735           *
 736           * @param {string} html Content to send to the editor.
 737           * @return {void}
 738           */
 739          insert: function( html ) {
 740              var editor, wpActiveEditor,
 741                  hasTinymce = ! _.isUndefined( window.tinymce ),
 742                  hasQuicktags = ! _.isUndefined( window.QTags );
 743  
 744              if ( this.activeEditor ) {
 745                  wpActiveEditor = window.wpActiveEditor = this.activeEditor;
 746              } else {
 747                  wpActiveEditor = window.wpActiveEditor;
 748              }
 749  
 750              /*
 751               * Delegate to the global `send_to_editor` if it exists.
 752               * This attempts to play nice with any themes/plugins
 753               * that have overridden the insert functionality.
 754               */
 755              if ( window.send_to_editor ) {
 756                  return window.send_to_editor.apply( this, arguments );
 757              }
 758  
 759              if ( ! wpActiveEditor ) {
 760                  if ( hasTinymce && tinymce.activeEditor ) {
 761                      editor = tinymce.activeEditor;
 762                      wpActiveEditor = window.wpActiveEditor = editor.id;
 763                  } else if ( ! hasQuicktags ) {
 764                      return false;
 765                  }
 766              } else if ( hasTinymce ) {
 767                  editor = tinymce.get( wpActiveEditor );
 768              }
 769  
 770              if ( editor && ! editor.isHidden() ) {
 771                  editor.execCommand( 'mceInsertContent', false, html );
 772              } else if ( hasQuicktags ) {
 773                  QTags.insertContent( html );
 774              } else {
 775                  document.getElementById( wpActiveEditor ).value += html;
 776              }
 777  
 778              // If the old thickbox remove function exists, call it in case
 779              // a theme/plugin overloaded it.
 780              if ( window.tb_remove ) {
 781                  try { window.tb_remove(); } catch( e ) {}
 782              }
 783          },
 784  
 785          /**
 786           * Setup 'workflow' and add to the 'workflows' cache. 'open' can
 787           * subsequently be called upon it.
 788           *
 789           * @param {string} id           A slug used to identify the workflow.
 790           * @param {Object} [options={}] The options for the media workflow.
 791           *
 792           * @this {wp.media.editor}
 793           *
 794           * @return {wp.media.view.MediaFrame.Select} A media workflow.
 795           */
 796          add: function( id, options ) {
 797              var workflow = this.get( id );
 798  
 799              // Only add once: if exists return existing.
 800              if ( workflow ) {
 801                  return workflow;
 802              }
 803  
 804              workflow = workflows[ id ] = wp.media( _.defaults( options || {}, {
 805                  frame:    'post',
 806                  state:    'insert',
 807                  title:    wp.media.view.l10n.addMedia,
 808                  multiple: true
 809              } ) );
 810  
 811              workflow.on( 'insert', function( selection ) {
 812                  var state = workflow.state();
 813  
 814                  selection = selection || state.get('selection');
 815  
 816                  if ( ! selection ) {
 817                      return;
 818                  }
 819  
 820                  $.when.apply( $, selection.map( function( attachment ) {
 821                      var display = state.display( attachment ).toJSON();
 822                      /**
 823                       * @this {wp.media.editor}
 824                       */
 825                      return this.send.attachment( display, attachment.toJSON() );
 826                  }, this ) ).done( function() {
 827                      wp.media.editor.insert( _.toArray( arguments ).join('\n\n') );
 828                  });
 829              }, this );
 830  
 831              workflow.state('gallery-edit').on( 'update', function( selection ) {
 832                  /**
 833                   * @this {wp.media.editor}
 834                   */
 835                  this.insert( wp.media.gallery.shortcode( selection ).string() );
 836              }, this );
 837  
 838              workflow.state('playlist-edit').on( 'update', function( selection ) {
 839                  /**
 840                   * @this {wp.media.editor}
 841                   */
 842                  this.insert( wp.media.playlist.shortcode( selection ).string() );
 843              }, this );
 844  
 845              workflow.state('video-playlist-edit').on( 'update', function( selection ) {
 846                  /**
 847                   * @this {wp.media.editor}
 848                   */
 849                  this.insert( wp.media.playlist.shortcode( selection ).string() );
 850              }, this );
 851  
 852              workflow.state('embed').on( 'select', function() {
 853                  /**
 854                   * @this {wp.media.editor}
 855                   */
 856                  var state = workflow.state(),
 857                      type = state.get('type'),
 858                      embed = state.props.toJSON();
 859  
 860                  embed.url = embed.url || '';
 861  
 862                  if ( 'link' === type ) {
 863                      _.defaults( embed, {
 864                          linkText: embed.url,
 865                          linkUrl: embed.url
 866                      });
 867  
 868                      this.send.link( embed ).done( function( resp ) {
 869                          wp.media.editor.insert( resp );
 870                      });
 871  
 872                  } else if ( 'image' === type ) {
 873                      _.defaults( embed, {
 874                          title:   embed.url,
 875                          linkUrl: '',
 876                          align:   'none',
 877                          link:    'none'
 878                      });
 879  
 880                      if ( 'none' === embed.link ) {
 881                          embed.linkUrl = '';
 882                      } else if ( 'file' === embed.link ) {
 883                          embed.linkUrl = embed.url;
 884                      }
 885  
 886                      this.insert( wp.media.string.image( embed ) );
 887                  }
 888              }, this );
 889  
 890              workflow.state('featured-image').on( 'select', wp.media.featuredImage.select );
 891              workflow.setState( workflow.options.state );
 892              return workflow;
 893          },
 894          /**
 895           * Determines the proper current workflow id
 896           *
 897           * @param {string} [id=''] A slug used to identify the workflow.
 898           *
 899           * @return {wpActiveEditor|string|tinymce.activeEditor.id} The current workflow id.
 900           */
 901          id: function( id ) {
 902              if ( id ) {
 903                  return id;
 904              }
 905  
 906              // If an empty `id` is provided, default to `wpActiveEditor`.
 907              id = window.wpActiveEditor;
 908  
 909              // If that doesn't work, fall back to `tinymce.activeEditor.id`.
 910              if ( ! id && ! _.isUndefined( window.tinymce ) && tinymce.activeEditor ) {
 911                  id = tinymce.activeEditor.id;
 912              }
 913  
 914              // Last but not least, fall back to the empty string.
 915              id = id || '';
 916              return id;
 917          },
 918          /**
 919           * Return the workflow specified by id
 920           *
 921           * @param {string} id A slug used to identify the workflow.
 922           *
 923           * @this {wp.media.editor}
 924           *
 925           * @return {wp.media.view.MediaFrame} A media workflow.
 926           */
 927          get: function( id ) {
 928              id = this.id( id );
 929              return workflows[ id ];
 930          },
 931          /**
 932           * Remove the workflow represented by id from the workflow cache
 933           *
 934           * @param {string} id A slug used to identify the workflow.
 935           *
 936           * @this {wp.media.editor}
 937           */
 938          remove: function( id ) {
 939              id = this.id( id );
 940              delete workflows[ id ];
 941          },
 942          /** @namespace wp.media.editor.send */
 943          send: {
 944              /**
 945               * Called when sending an attachment to the editor
 946               * from the medial modal.
 947               *
 948               * @param {Object} props      Attachment details (align, link, size, etc).
 949               * @param {Object} attachment The attachment object, media version of Post.
 950               * @return {Promise} A promise that resolves when the attachment has been sent to the editor.
 951               */
 952              attachment: function( props, attachment ) {
 953                  var caption = attachment.caption,
 954                      options, html;
 955  
 956                  // If captions are disabled, clear the caption.
 957                  if ( ! wp.media.view.settings.captions ) {
 958                      delete attachment.caption;
 959                  }
 960  
 961                  props = wp.media.string.props( props, attachment );
 962  
 963                  options = {
 964                      id:           attachment.id,
 965                      post_content: attachment.description,
 966                      post_excerpt: caption
 967                  };
 968  
 969                  if ( props.linkUrl ) {
 970                      options.url = props.linkUrl;
 971                  }
 972  
 973                  if ( 'image' === attachment.type ) {
 974                      html = wp.media.string.image( props );
 975  
 976                      _.each({
 977                          align: 'align',
 978                          size:  'image-size',
 979                          alt:   'image_alt'
 980                      }, function( option, prop ) {
 981                          if ( props[ prop ] ) {
 982                              options[ option ] = props[ prop ];
 983                          }
 984                      });
 985                  } else if ( 'video' === attachment.type ) {
 986                      html = wp.media.string.video( props, attachment );
 987                  } else if ( 'audio' === attachment.type ) {
 988                      html = wp.media.string.audio( props, attachment );
 989                  } else {
 990                      html = wp.media.string.link( props );
 991                      options.post_title = props.title;
 992                  }
 993  
 994                  return wp.media.post( 'send-attachment-to-editor', {
 995                      nonce:      wp.media.view.settings.nonce.sendToEditor,
 996                      attachment: options,
 997                      html:       html,
 998                      post_id:    wp.media.view.settings.post.id
 999                  });
1000              },
1001              /**
1002               * Called when 'Insert From URL' source is not an image. Example: YouTube url.
1003               *
1004               * @param {Object} embed The embed object containing the link URL and link text.
1005               * @return {Promise} A promise that resolves when the link has been sent to the editor.
1006               */
1007              link: function( embed ) {
1008                  return wp.media.post( 'send-link-to-editor', {
1009                      nonce:     wp.media.view.settings.nonce.sendToEditor,
1010                      src:       embed.linkUrl,
1011                      link_text: embed.linkText,
1012                      html:      wp.media.string.link( embed ),
1013                      post_id:   wp.media.view.settings.post.id
1014                  });
1015              }
1016          },
1017          /**
1018           * Opens a workflow.
1019           *
1020           * @param {string} [id=undefined] Optional. A slug used to identify the workflow.
1021           * @param {Object} [options={}]   The options for the media workflow.
1022           *
1023           * @this {wp.media.editor}
1024           *
1025           * @return {wp.media.view.MediaFrame} A media workflow.
1026           */
1027          open: function( id, options ) {
1028              var workflow;
1029  
1030              options = options || {};
1031  
1032              id = this.id( id );
1033              this.activeEditor = id;
1034  
1035              workflow = this.get( id );
1036  
1037              // Redo workflow if state has changed.
1038              if ( ! workflow || ( workflow.options && options.state !== workflow.options.state ) ) {
1039                  workflow = this.add( id, options );
1040              }
1041  
1042              wp.media.frame = workflow;
1043  
1044              return workflow.open();
1045          },
1046  
1047          /**
1048           * Bind click event for .insert-media using event delegation
1049           */
1050          init: function() {
1051              $(document.body)
1052                  .on( 'click.add-media-button', '.insert-media', function( event ) {
1053                      var elem = $( event.currentTarget ),
1054                          editor = elem.data('editor'),
1055                          options = {
1056                              frame:    'post',
1057                              state:    'insert',
1058                              title:    wp.media.view.l10n.addMedia,
1059                              multiple: true
1060                          };
1061  
1062                      event.preventDefault();
1063  
1064                      if ( elem.hasClass( 'gallery' ) ) {
1065                          options.state = 'gallery';
1066                          options.title = wp.media.view.l10n.createGalleryTitle;
1067                      }
1068  
1069                      wp.media.editor.open( editor, options );
1070                  });
1071  
1072              // Initialize and render the Editor drag-and-drop uploader.
1073              new wp.media.view.EditorUploader().render();
1074          }
1075      };
1076  
1077      _.bindAll( wp.media.editor, 'open' );
1078      $( wp.media.editor.init );
1079  }(jQuery, _));


Generated : Thu Sep 24 08:20:34 2026 Cross-referenced by PHPXref