[ 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 * @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, _));
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |