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


Generated : Sun Jun 14 08:20:09 2026 Cross-referenced by PHPXref