| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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, _));
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Thu Sep 24 08:20:34 2026 | Cross-referenced by PHPXref |