diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:56:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:56:49 +0000 |
commit | a415c29efee45520ae252d2aa28f1083a521cd7b (patch) | |
tree | f4ade4b6668ecc0765de7e1424f7c1427ad433ff /wp-includes/js/media-editor.js | |
parent | Initial commit. (diff) | |
download | wordpress-a415c29efee45520ae252d2aa28f1083a521cd7b.tar.xz wordpress-a415c29efee45520ae252d2aa28f1083a521cd7b.zip |
Adding upstream version 6.4.3+dfsg1.upstream/6.4.3+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'wp-includes/js/media-editor.js')
-rw-r--r-- | wp-includes/js/media-editor.js | 1068 |
1 files changed, 1068 insertions, 0 deletions
diff --git a/wp-includes/js/media-editor.js b/wp-includes/js/media-editor.js new file mode 100644 index 0000000..02c993f --- /dev/null +++ b/wp-includes/js/media-editor.js @@ -0,0 +1,1068 @@ +/** + * @output wp-includes/js/media-editor.js + */ + +/* global getUserSetting, tinymce, QTags */ + +// WordPress, TinyMCE, and Media +// ----------------------------- +(function($, _){ + /** + * Stores the editors' `wp.media.controller.Frame` instances. + * + * @static + */ + var workflows = {}; + + /** + * A helper mixin function to avoid truthy and falsey values being + * passed as an input that expects booleans. If key is undefined in the map, + * but has a default value, set it. + * + * @param {Object} attrs Map of props from a shortcode or settings. + * @param {string} key The key within the passed map to check for a value. + * @return {mixed|undefined} The original or coerced value of key within attrs. + */ + wp.media.coerce = function ( attrs, key ) { + if ( _.isUndefined( attrs[ key ] ) && ! _.isUndefined( this.defaults[ key ] ) ) { + attrs[ key ] = this.defaults[ key ]; + } else if ( 'true' === attrs[ key ] ) { + attrs[ key ] = true; + } else if ( 'false' === attrs[ key ] ) { + attrs[ key ] = false; + } + return attrs[ key ]; + }; + + /** @namespace wp.media.string */ + wp.media.string = { + /** + * Joins the `props` and `attachment` objects, + * outputting the proper object format based on the + * attachment's type. + * + * @param {Object} [props={}] Attachment details (align, link, size, etc). + * @param {Object} attachment The attachment object, media version of Post. + * @return {Object} Joined props + */ + props: function( props, attachment ) { + var link, linkUrl, size, sizes, + defaultProps = wp.media.view.settings.defaultProps; + + props = props ? _.clone( props ) : {}; + + if ( attachment && attachment.type ) { + props.type = attachment.type; + } + + if ( 'image' === props.type ) { + props = _.defaults( props || {}, { + align: defaultProps.align || getUserSetting( 'align', 'none' ), + size: defaultProps.size || getUserSetting( 'imgsize', 'medium' ), + url: '', + classes: [] + }); + } + + // All attachment-specific settings follow. + if ( ! attachment ) { + return props; + } + + props.title = props.title || attachment.title; + + link = props.link || defaultProps.link || getUserSetting( 'urlbutton', 'file' ); + if ( 'file' === link || 'embed' === link ) { + linkUrl = attachment.url; + } else if ( 'post' === link ) { + linkUrl = attachment.link; + } else if ( 'custom' === link ) { + linkUrl = props.linkUrl; + } + props.linkUrl = linkUrl || ''; + + // Format properties for images. + if ( 'image' === attachment.type ) { + props.classes.push( 'wp-image-' + attachment.id ); + + sizes = attachment.sizes; + size = sizes && sizes[ props.size ] ? sizes[ props.size ] : attachment; + + _.extend( props, _.pick( attachment, 'align', 'caption', 'alt' ), { + width: size.width, + height: size.height, + src: size.url, + captionId: 'attachment_' + attachment.id + }); + } else if ( 'video' === attachment.type || 'audio' === attachment.type ) { + _.extend( props, _.pick( attachment, 'title', 'type', 'icon', 'mime' ) ); + // Format properties for non-images. + } else { + props.title = props.title || attachment.filename; + props.rel = props.rel || 'attachment wp-att-' + attachment.id; + } + + return props; + }, + /** + * Create link markup that is suitable for passing to the editor + * + * @param {Object} props Attachment details (align, link, size, etc). + * @param {Object} attachment The attachment object, media version of Post. + * @return {string} The link markup + */ + link: function( props, attachment ) { + var options; + + props = wp.media.string.props( props, attachment ); + + options = { + tag: 'a', + content: props.title, + attrs: { + href: props.linkUrl + } + }; + + if ( props.rel ) { + options.attrs.rel = props.rel; + } + + return wp.html.string( options ); + }, + /** + * Create an Audio shortcode string that is suitable for passing to the editor + * + * @param {Object} props Attachment details (align, link, size, etc). + * @param {Object} attachment The attachment object, media version of Post. + * @return {string} The audio shortcode + */ + audio: function( props, attachment ) { + return wp.media.string._audioVideo( 'audio', props, attachment ); + }, + /** + * Create a Video shortcode string that is suitable for passing to the editor + * + * @param {Object} props Attachment details (align, link, size, etc). + * @param {Object} attachment The attachment object, media version of Post. + * @return {string} The video shortcode + */ + video: function( props, attachment ) { + return wp.media.string._audioVideo( 'video', props, attachment ); + }, + /** + * Helper function to create a media shortcode string + * + * @access private + * + * @param {string} type The shortcode tag name: 'audio' or 'video'. + * @param {Object} props Attachment details (align, link, size, etc). + * @param {Object} attachment The attachment object, media version of Post. + * @return {string} The media shortcode + */ + _audioVideo: function( type, props, attachment ) { + var shortcode, html, extension; + + props = wp.media.string.props( props, attachment ); + if ( props.link !== 'embed' ) { + return wp.media.string.link( props ); + } + + shortcode = {}; + + if ( 'video' === type ) { + if ( attachment.image && -1 === attachment.image.src.indexOf( attachment.icon ) ) { + shortcode.poster = attachment.image.src; + } + + if ( attachment.width ) { + shortcode.width = attachment.width; + } + + if ( attachment.height ) { + shortcode.height = attachment.height; + } + } + + extension = attachment.filename.split('.').pop(); + + if ( _.contains( wp.media.view.settings.embedExts, extension ) ) { + shortcode[extension] = attachment.url; + } else { + // Render unsupported audio and video files as links. + return wp.media.string.link( props ); + } + + html = wp.shortcode.string({ + tag: type, + attrs: shortcode + }); + + return html; + }, + /** + * Create image markup, optionally with a link and/or wrapped in a caption shortcode, + * that is suitable for passing to the editor + * + * @param {Object} props Attachment details (align, link, size, etc). + * @param {Object} attachment The attachment object, media version of Post. + * @return {string} + */ + image: function( props, attachment ) { + var img = {}, + options, classes, shortcode, html; + + props.type = 'image'; + props = wp.media.string.props( props, attachment ); + classes = props.classes || []; + + img.src = ! _.isUndefined( attachment ) ? attachment.url : props.url; + _.extend( img, _.pick( props, 'width', 'height', 'alt' ) ); + + // Only assign the align class to the image if we're not printing + // a caption, since the alignment is sent to the shortcode. + if ( props.align && ! props.caption ) { + classes.push( 'align' + props.align ); + } + + if ( props.size ) { + classes.push( 'size-' + props.size ); + } + + img['class'] = _.compact( classes ).join(' '); + + // Generate `img` tag options. + options = { + tag: 'img', + attrs: img, + single: true + }; + + // Generate the `a` element options, if they exist. + if ( props.linkUrl ) { + options = { + tag: 'a', + attrs: { + href: props.linkUrl + }, + content: options + }; + } + + html = wp.html.string( options ); + + // Generate the caption shortcode. + if ( props.caption ) { + shortcode = {}; + + if ( img.width ) { + shortcode.width = img.width; + } + + if ( props.captionId ) { + shortcode.id = props.captionId; + } + + if ( props.align ) { + shortcode.align = 'align' + props.align; + } + + html = wp.shortcode.string({ + tag: 'caption', + attrs: shortcode, + content: html + ' ' + props.caption + }); + } + + return html; + } + }; + + wp.media.embed = { + coerce : wp.media.coerce, + + defaults : { + url : '', + width: '', + height: '' + }, + + edit : function( data, isURL ) { + var frame, props = {}, shortcode; + + if ( isURL ) { + props.url = data.replace(/<[^>]+>/g, ''); + } else { + shortcode = wp.shortcode.next( 'embed', data ).shortcode; + + props = _.defaults( shortcode.attrs.named, this.defaults ); + if ( shortcode.content ) { + props.url = shortcode.content; + } + } + + frame = wp.media({ + frame: 'post', + state: 'embed', + metadata: props + }); + + return frame; + }, + + shortcode : function( model ) { + var self = this, content; + + _.each( this.defaults, function( value, key ) { + model[ key ] = self.coerce( model, key ); + + if ( value === model[ key ] ) { + delete model[ key ]; + } + }); + + content = model.url; + delete model.url; + + return new wp.shortcode({ + tag: 'embed', + attrs: model, + content: content + }); + } + }; + + /** + * @class wp.media.collection + * + * @param {Object} attributes + */ + wp.media.collection = function(attributes) { + var collections = {}; + + return _.extend(/** @lends wp.media.collection.prototype */{ + coerce : wp.media.coerce, + /** + * Retrieve attachments based on the properties of the passed shortcode + * + * @param {wp.shortcode} shortcode An instance of wp.shortcode(). + * @return {wp.media.model.Attachments} A Backbone.Collection containing + * the media items belonging to a collection. + * The query[ this.tag ] property is a Backbone.Model + * containing the 'props' for the collection. + */ + attachments: function( shortcode ) { + var shortcodeString = shortcode.string(), + result = collections[ shortcodeString ], + attrs, args, query, others, self = this; + + delete collections[ shortcodeString ]; + if ( result ) { + return result; + } + // Fill the default shortcode attributes. + attrs = _.defaults( shortcode.attrs.named, this.defaults ); + args = _.pick( attrs, 'orderby', 'order' ); + + args.type = this.type; + args.perPage = -1; + + // Mark the `orderby` override attribute. + if ( undefined !== attrs.orderby ) { + attrs._orderByField = attrs.orderby; + } + + if ( 'rand' === attrs.orderby ) { + attrs._orderbyRandom = true; + } + + // Map the `orderby` attribute to the corresponding model property. + if ( ! attrs.orderby || /^menu_order(?: ID)?$/i.test( attrs.orderby ) ) { + args.orderby = 'menuOrder'; + } + + // Map the `ids` param to the correct query args. + if ( attrs.ids ) { + args.post__in = attrs.ids.split(','); + args.orderby = 'post__in'; + } else if ( attrs.include ) { + args.post__in = attrs.include.split(','); + } + + if ( attrs.exclude ) { + args.post__not_in = attrs.exclude.split(','); + } + + if ( ! args.post__in ) { + args.uploadedTo = attrs.id; + } + + // Collect the attributes that were not included in `args`. + others = _.omit( attrs, 'id', 'ids', 'include', 'exclude', 'orderby', 'order' ); + + _.each( this.defaults, function( value, key ) { + others[ key ] = self.coerce( others, key ); + }); + + query = wp.media.query( args ); + query[ this.tag ] = new Backbone.Model( others ); + return query; + }, + /** + * Triggered when clicking 'Insert {label}' or 'Update {label}' + * + * @param {wp.media.model.Attachments} attachments A Backbone.Collection containing + * the media items belonging to a collection. + * The query[ this.tag ] property is a Backbone.Model + * containing the 'props' for the collection. + * @return {wp.shortcode} + */ + shortcode: function( attachments ) { + var props = attachments.props.toJSON(), + attrs = _.pick( props, 'orderby', 'order' ), + shortcode, clone; + + if ( attachments.type ) { + attrs.type = attachments.type; + delete attachments.type; + } + + if ( attachments[this.tag] ) { + _.extend( attrs, attachments[this.tag].toJSON() ); + } + + /* + * Convert all gallery shortcodes to use the `ids` property. + * Ignore `post__in` and `post__not_in`; the attachments in + * the collection will already reflect those properties. + */ + attrs.ids = attachments.pluck('id'); + + // Copy the `uploadedTo` post ID. + if ( props.uploadedTo ) { + attrs.id = props.uploadedTo; + } + // Check if the gallery is randomly ordered. + delete attrs.orderby; + + if ( attrs._orderbyRandom ) { + attrs.orderby = 'rand'; + } else if ( attrs._orderByField && 'rand' !== attrs._orderByField ) { + attrs.orderby = attrs._orderByField; + } + + delete attrs._orderbyRandom; + delete attrs._orderByField; + + // If the `ids` attribute is set and `orderby` attribute + // is the default value, clear it for cleaner output. + if ( attrs.ids && 'post__in' === attrs.orderby ) { + delete attrs.orderby; + } + + attrs = this.setDefaults( attrs ); + + shortcode = new wp.shortcode({ + tag: this.tag, + attrs: attrs, + type: 'single' + }); + + // Use a cloned version of the gallery. + clone = new wp.media.model.Attachments( attachments.models, { + props: props + }); + clone[ this.tag ] = attachments[ this.tag ]; + collections[ shortcode.string() ] = clone; + + return shortcode; + }, + /** + * Triggered when double-clicking a collection shortcode placeholder + * in the editor + * + * @param {string} content Content that is searched for possible + * shortcode markup matching the passed tag name, + * + * @this wp.media.{prop} + * + * @return {wp.media.view.MediaFrame.Select} A media workflow. + */ + edit: function( content ) { + var shortcode = wp.shortcode.next( this.tag, content ), + defaultPostId = this.defaults.id, + attachments, selection, state; + + // Bail if we didn't match the shortcode or all of the content. + if ( ! shortcode || shortcode.content !== content ) { + return; + } + + // Ignore the rest of the match object. + shortcode = shortcode.shortcode; + + if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) ) { + shortcode.set( 'id', defaultPostId ); + } + + attachments = this.attachments( shortcode ); + + selection = new wp.media.model.Selection( attachments.models, { + props: attachments.props.toJSON(), + multiple: true + }); + + selection[ this.tag ] = attachments[ this.tag ]; + + // Fetch the query's attachments, and then break ties from the + // query to allow for sorting. + selection.more().done( function() { + // Break ties with the query. + selection.props.set({ query: false }); + selection.unmirror(); + selection.props.unset('orderby'); + }); + + // Destroy the previous gallery frame. + if ( this.frame ) { + this.frame.dispose(); + } + + if ( shortcode.attrs.named.type && 'video' === shortcode.attrs.named.type ) { + state = 'video-' + this.tag + '-edit'; + } else { + state = this.tag + '-edit'; + } + + // Store the current frame. + this.frame = wp.media({ + frame: 'post', + state: state, + title: this.editTitle, + editing: true, + multiple: true, + selection: selection + }).open(); + + return this.frame; + }, + + setDefaults: function( attrs ) { + var self = this; + // Remove default attributes from the shortcode. + _.each( this.defaults, function( value, key ) { + attrs[ key ] = self.coerce( attrs, key ); + if ( value === attrs[ key ] ) { + delete attrs[ key ]; + } + }); + + return attrs; + } + }, attributes ); + }; + + wp.media._galleryDefaults = { + itemtag: 'dl', + icontag: 'dt', + captiontag: 'dd', + columns: '3', + link: 'post', + size: 'thumbnail', + order: 'ASC', + id: wp.media.view.settings.post && wp.media.view.settings.post.id, + orderby : 'menu_order ID' + }; + + if ( wp.media.view.settings.galleryDefaults ) { + wp.media.galleryDefaults = _.extend( {}, wp.media._galleryDefaults, wp.media.view.settings.galleryDefaults ); + } else { + wp.media.galleryDefaults = wp.media._galleryDefaults; + } + + wp.media.gallery = new wp.media.collection({ + tag: 'gallery', + type : 'image', + editTitle : wp.media.view.l10n.editGalleryTitle, + defaults : wp.media.galleryDefaults, + + setDefaults: function( attrs ) { + var self = this, changed = ! _.isEqual( wp.media.galleryDefaults, wp.media._galleryDefaults ); + _.each( this.defaults, function( value, key ) { + attrs[ key ] = self.coerce( attrs, key ); + if ( value === attrs[ key ] && ( ! changed || value === wp.media._galleryDefaults[ key ] ) ) { + delete attrs[ key ]; + } + } ); + return attrs; + } + }); + + /** + * @namespace wp.media.featuredImage + * @memberOf wp.media + */ + wp.media.featuredImage = { + /** + * Get the featured image post ID + * + * @return {wp.media.view.settings.post.featuredImageId|number} + */ + get: function() { + return wp.media.view.settings.post.featuredImageId; + }, + /** + * Sets the featured image ID property and sets the HTML in the post meta box to the new featured image. + * + * @param {number} id The post ID of the featured image, or -1 to unset it. + */ + set: function( id ) { + var settings = wp.media.view.settings; + + settings.post.featuredImageId = id; + + wp.media.post( 'get-post-thumbnail-html', { + post_id: settings.post.id, + thumbnail_id: settings.post.featuredImageId, + _wpnonce: settings.post.nonce + }).done( function( html ) { + if ( '0' === html ) { + window.alert( wp.i18n.__( 'Could not set that as the thumbnail image. Try a different attachment.' ) ); + return; + } + $( '.inside', '#postimagediv' ).html( html ); + }); + }, + /** + * Remove the featured image id, save the post thumbnail data and + * set the HTML in the post meta box to no featured image. + */ + remove: function() { + wp.media.featuredImage.set( -1 ); + }, + /** + * The Featured Image workflow + * + * @this wp.media.featuredImage + * + * @return {wp.media.view.MediaFrame.Select} A media workflow. + */ + frame: function() { + if ( this._frame ) { + wp.media.frame = this._frame; + return this._frame; + } + + this._frame = wp.media({ + state: 'featured-image', + states: [ new wp.media.controller.FeaturedImage() , new wp.media.controller.EditImage() ] + }); + + this._frame.on( 'toolbar:create:featured-image', function( toolbar ) { + /** + * @this wp.media.view.MediaFrame.Select + */ + this.createSelectToolbar( toolbar, { + text: wp.media.view.l10n.setFeaturedImage + }); + }, this._frame ); + + this._frame.on( 'content:render:edit-image', function() { + var selection = this.state('featured-image').get('selection'), + view = new wp.media.view.EditImage( { model: selection.single(), controller: this } ).render(); + + this.content.set( view ); + + // After bringing in the frame, load the actual editor via an Ajax call. + view.loadEditor(); + + }, this._frame ); + + this._frame.state('featured-image').on( 'select', this.select ); + return this._frame; + }, + /** + * 'select' callback for Featured Image workflow, triggered when + * the 'Set Featured Image' button is clicked in the media modal. + * + * @this wp.media.controller.FeaturedImage + */ + select: function() { + var selection = this.get('selection').single(); + + if ( ! wp.media.view.settings.post.featuredImageId ) { + return; + } + + wp.media.featuredImage.set( selection ? selection.id : -1 ); + }, + /** + * Open the content media manager to the 'featured image' tab when + * the post thumbnail is clicked. + * + * Update the featured image id when the 'remove' link is clicked. + */ + init: function() { + $('#postimagediv').on( 'click', '#set-post-thumbnail', function( event ) { + event.preventDefault(); + // Stop propagation to prevent thickbox from activating. + event.stopPropagation(); + + wp.media.featuredImage.frame().open(); + }).on( 'click', '#remove-post-thumbnail', function() { + wp.media.featuredImage.remove(); + return false; + }); + } + }; + + $( wp.media.featuredImage.init ); + + /** @namespace wp.media.editor */ + wp.media.editor = { + /** + * Send content to the editor + * + * @param {string} html Content to send to the editor + */ + insert: function( html ) { + var editor, wpActiveEditor, + hasTinymce = ! _.isUndefined( window.tinymce ), + hasQuicktags = ! _.isUndefined( window.QTags ); + + if ( this.activeEditor ) { + wpActiveEditor = window.wpActiveEditor = this.activeEditor; + } else { + wpActiveEditor = window.wpActiveEditor; + } + + /* + * Delegate to the global `send_to_editor` if it exists. + * This attempts to play nice with any themes/plugins + * that have overridden the insert functionality. + */ + if ( window.send_to_editor ) { + return window.send_to_editor.apply( this, arguments ); + } + + if ( ! wpActiveEditor ) { + if ( hasTinymce && tinymce.activeEditor ) { + editor = tinymce.activeEditor; + wpActiveEditor = window.wpActiveEditor = editor.id; + } else if ( ! hasQuicktags ) { + return false; + } + } else if ( hasTinymce ) { + editor = tinymce.get( wpActiveEditor ); + } + + if ( editor && ! editor.isHidden() ) { + editor.execCommand( 'mceInsertContent', false, html ); + } else if ( hasQuicktags ) { + QTags.insertContent( html ); + } else { + document.getElementById( wpActiveEditor ).value += html; + } + + // If the old thickbox remove function exists, call it in case + // a theme/plugin overloaded it. + if ( window.tb_remove ) { + try { window.tb_remove(); } catch( e ) {} + } + }, + + /** + * Setup 'workflow' and add to the 'workflows' cache. 'open' can + * subsequently be called upon it. + * + * @param {string} id A slug used to identify the workflow. + * @param {Object} [options={}] + * + * @this wp.media.editor + * + * @return {wp.media.view.MediaFrame.Select} A media workflow. + */ + add: function( id, options ) { + var workflow = this.get( id ); + + // Only add once: if exists return existing. + if ( workflow ) { + return workflow; + } + + workflow = workflows[ id ] = wp.media( _.defaults( options || {}, { + frame: 'post', + state: 'insert', + title: wp.media.view.l10n.addMedia, + multiple: true + } ) ); + + workflow.on( 'insert', function( selection ) { + var state = workflow.state(); + + selection = selection || state.get('selection'); + + if ( ! selection ) { + return; + } + + $.when.apply( $, selection.map( function( attachment ) { + var display = state.display( attachment ).toJSON(); + /** + * @this wp.media.editor + */ + return this.send.attachment( display, attachment.toJSON() ); + }, this ) ).done( function() { + wp.media.editor.insert( _.toArray( arguments ).join('\n\n') ); + }); + }, this ); + + workflow.state('gallery-edit').on( 'update', function( selection ) { + /** + * @this wp.media.editor + */ + this.insert( wp.media.gallery.shortcode( selection ).string() ); + }, this ); + + workflow.state('playlist-edit').on( 'update', function( selection ) { + /** + * @this wp.media.editor + */ + this.insert( wp.media.playlist.shortcode( selection ).string() ); + }, this ); + + workflow.state('video-playlist-edit').on( 'update', function( selection ) { + /** + * @this wp.media.editor + */ + this.insert( wp.media.playlist.shortcode( selection ).string() ); + }, this ); + + workflow.state('embed').on( 'select', function() { + /** + * @this wp.media.editor + */ + var state = workflow.state(), + type = state.get('type'), + embed = state.props.toJSON(); + + embed.url = embed.url || ''; + + if ( 'link' === type ) { + _.defaults( embed, { + linkText: embed.url, + linkUrl: embed.url + }); + + this.send.link( embed ).done( function( resp ) { + wp.media.editor.insert( resp ); + }); + + } else if ( 'image' === type ) { + _.defaults( embed, { + title: embed.url, + linkUrl: '', + align: 'none', + link: 'none' + }); + + if ( 'none' === embed.link ) { + embed.linkUrl = ''; + } else if ( 'file' === embed.link ) { + embed.linkUrl = embed.url; + } + + this.insert( wp.media.string.image( embed ) ); + } + }, this ); + + workflow.state('featured-image').on( 'select', wp.media.featuredImage.select ); + workflow.setState( workflow.options.state ); + return workflow; + }, + /** + * Determines the proper current workflow id + * + * @param {string} [id=''] A slug used to identify the workflow. + * + * @return {wpActiveEditor|string|tinymce.activeEditor.id} + */ + id: function( id ) { + if ( id ) { + return id; + } + + // If an empty `id` is provided, default to `wpActiveEditor`. + id = window.wpActiveEditor; + + // If that doesn't work, fall back to `tinymce.activeEditor.id`. + if ( ! id && ! _.isUndefined( window.tinymce ) && tinymce.activeEditor ) { + id = tinymce.activeEditor.id; + } + + // Last but not least, fall back to the empty string. + id = id || ''; + return id; + }, + /** + * Return the workflow specified by id + * + * @param {string} id A slug used to identify the workflow. + * + * @this wp.media.editor + * + * @return {wp.media.view.MediaFrame} A media workflow. + */ + get: function( id ) { + id = this.id( id ); + return workflows[ id ]; + }, + /** + * Remove the workflow represented by id from the workflow cache + * + * @param {string} id A slug used to identify the workflow. + * + * @this wp.media.editor + */ + remove: function( id ) { + id = this.id( id ); + delete workflows[ id ]; + }, + /** @namespace wp.media.editor.send */ + send: { + /** + * Called when sending an attachment to the editor + * from the medial modal. + * + * @param {Object} props Attachment details (align, link, size, etc). + * @param {Object} attachment The attachment object, media version of Post. + * @return {Promise} + */ + attachment: function( props, attachment ) { + var caption = attachment.caption, + options, html; + + // If captions are disabled, clear the caption. + if ( ! wp.media.view.settings.captions ) { + delete attachment.caption; + } + + props = wp.media.string.props( props, attachment ); + + options = { + id: attachment.id, + post_content: attachment.description, + post_excerpt: caption + }; + + if ( props.linkUrl ) { + options.url = props.linkUrl; + } + + if ( 'image' === attachment.type ) { + html = wp.media.string.image( props ); + + _.each({ + align: 'align', + size: 'image-size', + alt: 'image_alt' + }, function( option, prop ) { + if ( props[ prop ] ) { + options[ option ] = props[ prop ]; + } + }); + } else if ( 'video' === attachment.type ) { + html = wp.media.string.video( props, attachment ); + } else if ( 'audio' === attachment.type ) { + html = wp.media.string.audio( props, attachment ); + } else { + html = wp.media.string.link( props ); + options.post_title = props.title; + } + + return wp.media.post( 'send-attachment-to-editor', { + nonce: wp.media.view.settings.nonce.sendToEditor, + attachment: options, + html: html, + post_id: wp.media.view.settings.post.id + }); + }, + /** + * Called when 'Insert From URL' source is not an image. Example: YouTube url. + * + * @param {Object} embed + * @return {Promise} + */ + link: function( embed ) { + return wp.media.post( 'send-link-to-editor', { + nonce: wp.media.view.settings.nonce.sendToEditor, + src: embed.linkUrl, + link_text: embed.linkText, + html: wp.media.string.link( embed ), + post_id: wp.media.view.settings.post.id + }); + } + }, + /** + * Open a workflow + * + * @param {string} [id=undefined] Optional. A slug used to identify the workflow. + * @param {Object} [options={}] + * + * @this wp.media.editor + * + * @return {wp.media.view.MediaFrame} + */ + open: function( id, options ) { + var workflow; + + options = options || {}; + + id = this.id( id ); + this.activeEditor = id; + + workflow = this.get( id ); + + // Redo workflow if state has changed. + if ( ! workflow || ( workflow.options && options.state !== workflow.options.state ) ) { + workflow = this.add( id, options ); + } + + wp.media.frame = workflow; + + return workflow.open(); + }, + + /** + * Bind click event for .insert-media using event delegation + */ + init: function() { + $(document.body) + .on( 'click.add-media-button', '.insert-media', function( event ) { + var elem = $( event.currentTarget ), + editor = elem.data('editor'), + options = { + frame: 'post', + state: 'insert', + title: wp.media.view.l10n.addMedia, + multiple: true + }; + + event.preventDefault(); + + if ( elem.hasClass( 'gallery' ) ) { + options.state = 'gallery'; + options.title = wp.media.view.l10n.createGalleryTitle; + } + + wp.media.editor.open( editor, options ); + }); + + // Initialize and render the Editor drag-and-drop uploader. + new wp.media.view.EditorUploader().render(); + } + }; + + _.bindAll( wp.media.editor, 'open' ); + $( wp.media.editor.init ); +}(jQuery, _)); |