[ 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', '#set-post-thumbnail', function( event ) {
 707                  event.preventDefault();
 708                  // Stop propagation to prevent thickbox from activating.
 709                  event.stopPropagation();
 710  
 711                  wp.media.featuredImage.frame().open();
 712              }).on( 'click', '#remove-post-thumbnail', function() {
 713                  wp.media.featuredImage.remove();
 714                  return false;
 715              });
 716          }
 717      };
 718  
 719      $( wp.media.featuredImage.init );
 720  
 721      /** @namespace wp.media.editor */
 722      wp.media.editor = {
 723          /**
 724           * Send content to the editor
 725           *
 726           * @param {string} html Content to send to the editor
 727           */
 728          insert: function( html ) {
 729              var editor, wpActiveEditor,
 730                  hasTinymce = ! _.isUndefined( window.tinymce ),
 731                  hasQuicktags = ! _.isUndefined( window.QTags );
 732  
 733              if ( this.activeEditor ) {
 734                  wpActiveEditor = window.wpActiveEditor = this.activeEditor;
 735              } else {
 736                  wpActiveEditor = window.wpActiveEditor;
 737              }
 738  
 739              /*
 740               * Delegate to the global `send_to_editor` if it exists.
 741               * This attempts to play nice with any themes/plugins
 742               * that have overridden the insert functionality.
 743               */
 744              if ( window.send_to_editor ) {
 745                  return window.send_to_editor.apply( this, arguments );
 746              }
 747  
 748              if ( ! wpActiveEditor ) {
 749                  if ( hasTinymce && tinymce.activeEditor ) {
 750                      editor = tinymce.activeEditor;
 751                      wpActiveEditor = window.wpActiveEditor = editor.id;
 752                  } else if ( ! hasQuicktags ) {
 753                      return false;
 754                  }
 755              } else if ( hasTinymce ) {
 756                  editor = tinymce.get( wpActiveEditor );
 757              }
 758  
 759              if ( editor && ! editor.isHidden() ) {
 760                  editor.execCommand( 'mceInsertContent', false, html );
 761              } else if ( hasQuicktags ) {
 762                  QTags.insertContent( html );
 763              } else {
 764                  document.getElementById( wpActiveEditor ).value += html;
 765              }
 766  
 767              // If the old thickbox remove function exists, call it in case
 768              // a theme/plugin overloaded it.
 769              if ( window.tb_remove ) {
 770                  try { window.tb_remove(); } catch( e ) {}
 771              }
 772          },
 773  
 774          /**
 775           * Setup 'workflow' and add to the 'workflows' cache. 'open' can
 776           *  subsequently be called upon it.
 777           *
 778           * @param {string} id A slug used to identify the workflow.
 779           * @param {Object} [options={}]
 780           *
 781           * @this wp.media.editor
 782           *
 783           * @return {wp.media.view.MediaFrame.Select} A media workflow.
 784           */
 785          add: function( id, options ) {
 786              var workflow = this.get( id );
 787  
 788              // Only add once: if exists return existing.
 789              if ( workflow ) {
 790                  return workflow;
 791              }
 792  
 793              workflow = workflows[ id ] = wp.media( _.defaults( options || {}, {
 794                  frame:    'post',
 795                  state:    'insert',
 796                  title:    wp.media.view.l10n.addMedia,
 797                  multiple: true
 798              } ) );
 799  
 800              workflow.on( 'insert', function( selection ) {
 801                  var state = workflow.state();
 802  
 803                  selection = selection || state.get('selection');
 804  
 805                  if ( ! selection ) {
 806                      return;
 807                  }
 808  
 809                  $.when.apply( $, selection.map( function( attachment ) {
 810                      var display = state.display( attachment ).toJSON();
 811                      /**
 812                       * @this wp.media.editor
 813                       */
 814                      return this.send.attachment( display, attachment.toJSON() );
 815                  }, this ) ).done( function() {
 816                      wp.media.editor.insert( _.toArray( arguments ).join('\n\n') );
 817                  });
 818              }, this );
 819  
 820              workflow.state('gallery-edit').on( 'update', function( selection ) {
 821                  /**
 822                   * @this wp.media.editor
 823                   */
 824                  this.insert( wp.media.gallery.shortcode( selection ).string() );
 825              }, this );
 826  
 827              workflow.state('playlist-edit').on( 'update', function( selection ) {
 828                  /**
 829                   * @this wp.media.editor
 830                   */
 831                  this.insert( wp.media.playlist.shortcode( selection ).string() );
 832              }, this );
 833  
 834              workflow.state('video-playlist-edit').on( 'update', function( selection ) {
 835                  /**
 836                   * @this wp.media.editor
 837                   */
 838                  this.insert( wp.media.playlist.shortcode( selection ).string() );
 839              }, this );
 840  
 841              workflow.state('embed').on( 'select', function() {
 842                  /**
 843                   * @this wp.media.editor
 844                   */
 845                  var state = workflow.state(),
 846                      type = state.get('type'),
 847                      embed = state.props.toJSON();
 848  
 849                  embed.url = embed.url || '';
 850  
 851                  if ( 'link' === type ) {
 852                      _.defaults( embed, {
 853                          linkText: embed.url,
 854                          linkUrl: embed.url
 855                      });
 856  
 857                      this.send.link( embed ).done( function( resp ) {
 858                          wp.media.editor.insert( resp );
 859                      });
 860  
 861                  } else if ( 'image' === type ) {
 862                      _.defaults( embed, {
 863                          title:   embed.url,
 864                          linkUrl: '',
 865                          align:   'none',
 866                          link:    'none'
 867                      });
 868  
 869                      if ( 'none' === embed.link ) {
 870                          embed.linkUrl = '';
 871                      } else if ( 'file' === embed.link ) {
 872                          embed.linkUrl = embed.url;
 873                      }
 874  
 875                      this.insert( wp.media.string.image( embed ) );
 876                  }
 877              }, this );
 878  
 879              workflow.state('featured-image').on( 'select', wp.media.featuredImage.select );
 880              workflow.setState( workflow.options.state );
 881              return workflow;
 882          },
 883          /**
 884           * Determines the proper current workflow id
 885           *
 886           * @param {string} [id=''] A slug used to identify the workflow.
 887           *
 888           * @return {wpActiveEditor|string|tinymce.activeEditor.id}
 889           */
 890          id: function( id ) {
 891              if ( id ) {
 892                  return id;
 893              }
 894  
 895              // If an empty `id` is provided, default to `wpActiveEditor`.
 896              id = window.wpActiveEditor;
 897  
 898              // If that doesn't work, fall back to `tinymce.activeEditor.id`.
 899              if ( ! id && ! _.isUndefined( window.tinymce ) && tinymce.activeEditor ) {
 900                  id = tinymce.activeEditor.id;
 901              }
 902  
 903              // Last but not least, fall back to the empty string.
 904              id = id || '';
 905              return id;
 906          },
 907          /**
 908           * Return the workflow specified by id
 909           *
 910           * @param {string} id A slug used to identify the workflow.
 911           *
 912           * @this wp.media.editor
 913           *
 914           * @return {wp.media.view.MediaFrame} A media workflow.
 915           */
 916          get: function( id ) {
 917              id = this.id( id );
 918              return workflows[ id ];
 919          },
 920          /**
 921           * Remove the workflow represented by id from the workflow cache
 922           *
 923           * @param {string} id A slug used to identify the workflow.
 924           *
 925           * @this wp.media.editor
 926           */
 927          remove: function( id ) {
 928              id = this.id( id );
 929              delete workflows[ id ];
 930          },
 931          /** @namespace wp.media.editor.send */
 932          send: {
 933              /**
 934               * Called when sending an attachment to the editor
 935               *   from the medial modal.
 936               *
 937               * @param {Object} props Attachment details (align, link, size, etc).
 938               * @param {Object} attachment The attachment object, media version of Post.
 939               * @return {Promise}
 940               */
 941              attachment: function( props, attachment ) {
 942                  var caption = attachment.caption,
 943                      options, html;
 944  
 945                  // If captions are disabled, clear the caption.
 946                  if ( ! wp.media.view.settings.captions ) {
 947                      delete attachment.caption;
 948                  }
 949  
 950                  props = wp.media.string.props( props, attachment );
 951  
 952                  options = {
 953                      id:           attachment.id,
 954                      post_content: attachment.description,
 955                      post_excerpt: caption
 956                  };
 957  
 958                  if ( props.linkUrl ) {
 959                      options.url = props.linkUrl;
 960                  }
 961  
 962                  if ( 'image' === attachment.type ) {
 963                      html = wp.media.string.image( props );
 964  
 965                      _.each({
 966                          align: 'align',
 967                          size:  'image-size',
 968                          alt:   'image_alt'
 969                      }, function( option, prop ) {
 970                          if ( props[ prop ] ) {
 971                              options[ option ] = props[ prop ];
 972                          }
 973                      });
 974                  } else if ( 'video' === attachment.type ) {
 975                      html = wp.media.string.video( props, attachment );
 976                  } else if ( 'audio' === attachment.type ) {
 977                      html = wp.media.string.audio( props, attachment );
 978                  } else {
 979                      html = wp.media.string.link( props );
 980                      options.post_title = props.title;
 981                  }
 982  
 983                  return wp.media.post( 'send-attachment-to-editor', {
 984                      nonce:      wp.media.view.settings.nonce.sendToEditor,
 985                      attachment: options,
 986                      html:       html,
 987                      post_id:    wp.media.view.settings.post.id
 988                  });
 989              },
 990              /**
 991               * Called when 'Insert From URL' source is not an image. Example: YouTube url.
 992               *
 993               * @param {Object} embed
 994               * @return {Promise}
 995               */
 996              link: function( embed ) {
 997                  return wp.media.post( 'send-link-to-editor', {
 998                      nonce:     wp.media.view.settings.nonce.sendToEditor,
 999                      src:       embed.linkUrl,
1000                      link_text: embed.linkText,
1001                      html:      wp.media.string.link( embed ),
1002                      post_id:   wp.media.view.settings.post.id
1003                  });
1004              }
1005          },
1006          /**
1007           * Open a workflow
1008           *
1009           * @param {string} [id=undefined] Optional. A slug used to identify the workflow.
1010           * @param {Object} [options={}]
1011           *
1012           * @this wp.media.editor
1013           *
1014           * @return {wp.media.view.MediaFrame}
1015           */
1016          open: function( id, options ) {
1017              var workflow;
1018  
1019              options = options || {};
1020  
1021              id = this.id( id );
1022              this.activeEditor = id;
1023  
1024              workflow = this.get( id );
1025  
1026              // Redo workflow if state has changed.
1027              if ( ! workflow || ( workflow.options && options.state !== workflow.options.state ) ) {
1028                  workflow = this.add( id, options );
1029              }
1030  
1031              wp.media.frame = workflow;
1032  
1033              return workflow.open();
1034          },
1035  
1036          /**
1037           * Bind click event for .insert-media using event delegation
1038           */
1039          init: function() {
1040              $(document.body)
1041                  .on( 'click.add-media-button', '.insert-media', function( event ) {
1042                      var elem = $( event.currentTarget ),
1043                          editor = elem.data('editor'),
1044                          options = {
1045                              frame:    'post',
1046                              state:    'insert',
1047                              title:    wp.media.view.l10n.addMedia,
1048                              multiple: true
1049                          };
1050  
1051                      event.preventDefault();
1052  
1053                      if ( elem.hasClass( 'gallery' ) ) {
1054                          options.state = 'gallery';
1055                          options.title = wp.media.view.l10n.createGalleryTitle;
1056                      }
1057  
1058                      wp.media.editor.open( editor, options );
1059                  });
1060  
1061              // Initialize and render the Editor drag-and-drop uploader.
1062              new wp.media.view.EditorUploader().render();
1063          }
1064      };
1065  
1066      _.bindAll( wp.media.editor, 'open' );
1067      $( wp.media.editor.init );
1068  }(jQuery, _));


Generated : Tue Mar 19 08:20:01 2024 Cross-referenced by PHPXref