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