34777677e6b47eb6eb64d3fa146a87a4610b7e91
[autoinstalls/wordpress.git] / wp-includes / js / media-editor.js
1 // WordPress, TinyMCE, and Media
2 // -----------------------------
3 (function($){
4         // Stores the editors' `wp.media.controller.Frame` instances.
5         var workflows = {};
6
7         wp.media.string = {
8                 // Joins the `props` and `attachment` objects,
9                 // outputting the proper object format based on the
10                 // attachment's type.
11                 props: function( props, attachment ) {
12                         var link, linkUrl, size, sizes, fallbacks;
13
14                         // Final fallbacks run after all processing has been completed.
15                         fallbacks = function( props ) {
16                                 // Generate alt fallbacks and strip tags.
17                                 if ( 'image' === props.type && ! props.alt ) {
18                                         props.alt = props.caption || props.title || '';
19                                         props.alt = props.alt.replace( /<\/?[^>]+>/g, '' );
20                                 }
21
22                                 return props;
23                         };
24
25                         props = props ? _.clone( props ) : {};
26
27                         if ( attachment && attachment.type )
28                                 props.type = attachment.type;
29
30                         if ( 'image' === props.type ) {
31                                 props = _.defaults( props || {}, {
32                                         align:   getUserSetting( 'align', 'none' ),
33                                         size:    getUserSetting( 'imgsize', 'medium' ),
34                                         url:     '',
35                                         classes: []
36                                 });
37                         }
38
39                         // All attachment-specific settings follow.
40                         if ( ! attachment )
41                                 return fallbacks( props );
42
43                         props.title = props.title || attachment.title;
44
45                         link = props.link || getUserSetting( 'urlbutton', 'post' );
46                         if ( 'file' === link )
47                                 linkUrl = attachment.url;
48                         else if ( 'post' === link )
49                                 linkUrl = attachment.link;
50                         else if ( 'custom' === link )
51                                 linkUrl = props.linkUrl;
52                         props.linkUrl = linkUrl || '';
53
54                         // Format properties for images.
55                         if ( 'image' === attachment.type ) {
56                                 props.classes.push( 'wp-image-' + attachment.id );
57
58                                 sizes = attachment.sizes;
59                                 size = sizes && sizes[ props.size ] ? sizes[ props.size ] : attachment;
60
61                                 _.extend( props, _.pick( attachment, 'align', 'caption', 'alt' ), {
62                                         width:     size.width,
63                                         height:    size.height,
64                                         src:       size.url,
65                                         captionId: 'attachment_' + attachment.id
66                                 });
67
68                         // Format properties for non-images.
69                         } else {
70                                 props.title = props.title || attachment.filename;
71                                 props.rel = props.rel || 'attachment wp-att-' + attachment.id;
72                         }
73
74                         return fallbacks( props );
75                 },
76
77                 link: function( props, attachment ) {
78                         var options;
79
80                         props = wp.media.string.props( props, attachment );
81
82                         options = {
83                                 tag:     'a',
84                                 content: props.title,
85                                 attrs:   {
86                                         href: props.linkUrl
87                                 }
88                         };
89
90                         if ( props.rel )
91                                 options.attrs.rel = props.rel;
92
93                         return wp.html.string( options );
94                 },
95
96                 image: function( props, attachment ) {
97                         var img = {},
98                                 options, classes, shortcode, html;
99
100                         props = wp.media.string.props( props, attachment );
101                         classes = props.classes || [];
102
103                         img.src = props.url;
104                         _.extend( img, _.pick( props, 'width', 'height', 'alt' ) );
105
106                         // Only assign the align class to the image if we're not printing
107                         // a caption, since the alignment is sent to the shortcode.
108                         if ( props.align && ! props.caption )
109                                 classes.push( 'align' + props.align );
110
111                         if ( props.size )
112                                 classes.push( 'size-' + props.size );
113
114                         img['class'] = _.compact( classes ).join(' ');
115
116                         // Generate `img` tag options.
117                         options = {
118                                 tag:    'img',
119                                 attrs:  img,
120                                 single: true
121                         };
122
123                         // Generate the `a` element options, if they exist.
124                         if ( props.linkUrl ) {
125                                 options = {
126                                         tag:   'a',
127                                         attrs: {
128                                                 href: props.linkUrl
129                                         },
130                                         content: options
131                                 };
132                         }
133
134                         html = wp.html.string( options );
135
136                         // Generate the caption shortcode.
137                         if ( props.caption ) {
138                                 shortcode = {};
139
140                                 if ( img.width )
141                                         shortcode.width = img.width;
142
143                                 if ( props.captionId )
144                                         shortcode.id = props.captionId;
145
146                                 if ( props.align )
147                                         shortcode.align = 'align' + props.align;
148
149                                 html = wp.shortcode.string({
150                                         tag:     'caption',
151                                         attrs:   shortcode,
152                                         content: html + ' ' + props.caption
153                                 });
154                         }
155
156                         return html;
157                 }
158         };
159
160         wp.media.gallery = (function() {
161                 var galleries = {};
162
163                 return {
164                         defaults: {
165                                 order:      'ASC',
166                                 id:         wp.media.view.settings.post.id,
167                                 itemtag:    'dl',
168                                 icontag:    'dt',
169                                 captiontag: 'dd',
170                                 columns:    3,
171                                 size:       'thumbnail',
172                                 orderby:    'menu_order ID'
173                         },
174
175                         attachments: function( shortcode ) {
176                                 var shortcodeString = shortcode.string(),
177                                         result = galleries[ shortcodeString ],
178                                         attrs, args, query, others;
179
180                                 delete galleries[ shortcodeString ];
181
182                                 if ( result )
183                                         return result;
184
185                                 // Fill the default shortcode attributes.
186                                 attrs = _.defaults( shortcode.attrs.named, wp.media.gallery.defaults );
187                                 args  = _.pick( attrs, 'orderby', 'order' );
188
189                                 args.type    = 'image';
190                                 args.perPage = -1;
191
192                                 // Mark the `orderby` override attribute.
193                                 if ( 'rand' === attrs.orderby )
194                                         attrs._orderbyRandom = true;
195
196                                 // Map the `orderby` attribute to the corresponding model property.
197                                 if ( ! attrs.orderby || /^menu_order(?: ID)?$/i.test( attrs.orderby ) )
198                                         args.orderby = 'menuOrder';
199
200                                 // Map the `ids` param to the correct query args.
201                                 if ( attrs.ids ) {
202                                         args.post__in = attrs.ids.split(',');
203                                         args.orderby  = 'post__in';
204                                 } else if ( attrs.include ) {
205                                         args.post__in = attrs.include.split(',');
206                                 }
207
208                                 if ( attrs.exclude )
209                                         args.post__not_in = attrs.exclude.split(',');
210
211                                 if ( ! args.post__in )
212                                         args.uploadedTo = attrs.id;
213
214                                 // Collect the attributes that were not included in `args`.
215                                 others = _.omit( attrs, 'id', 'ids', 'include', 'exclude', 'orderby', 'order' );
216
217                                 query = wp.media.query( args );
218                                 query.gallery = new Backbone.Model( others );
219                                 return query;
220                         },
221
222                         shortcode: function( attachments ) {
223                                 var props = attachments.props.toJSON(),
224                                         attrs = _.pick( props, 'orderby', 'order' ),
225                                         shortcode, clone;
226
227                                 if ( attachments.gallery )
228                                         _.extend( attrs, attachments.gallery.toJSON() );
229
230                                 // Convert all gallery shortcodes to use the `ids` property.
231                                 // Ignore `post__in` and `post__not_in`; the attachments in
232                                 // the collection will already reflect those properties.
233                                 attrs.ids = attachments.pluck('id');
234
235                                 // Copy the `uploadedTo` post ID.
236                                 if ( props.uploadedTo )
237                                         attrs.id = props.uploadedTo;
238
239                                 // Check if the gallery is randomly ordered.
240                                 if ( attrs._orderbyRandom )
241                                         attrs.orderby = 'rand';
242                                 delete attrs._orderbyRandom;
243
244                                 // If the `ids` attribute is set and `orderby` attribute
245                                 // is the default value, clear it for cleaner output.
246                                 if ( attrs.ids && 'post__in' === attrs.orderby )
247                                         delete attrs.orderby;
248
249                                 // Remove default attributes from the shortcode.
250                                 _.each( wp.media.gallery.defaults, function( value, key ) {
251                                         if ( value === attrs[ key ] )
252                                                 delete attrs[ key ];
253                                 });
254
255                                 shortcode = new wp.shortcode({
256                                         tag:    'gallery',
257                                         attrs:  attrs,
258                                         type:   'single'
259                                 });
260
261                                 // Use a cloned version of the gallery.
262                                 clone = new wp.media.model.Attachments( attachments.models, {
263                                         props: props
264                                 });
265                                 clone.gallery = attachments.gallery;
266                                 galleries[ shortcode.string() ] = clone;
267
268                                 return shortcode;
269                         },
270
271                         edit: function( content ) {
272                                 var shortcode = wp.shortcode.next( 'gallery', content ),
273                                         defaultPostId = wp.media.gallery.defaults.id,
274                                         attachments, selection;
275
276                                 // Bail if we didn't match the shortcode or all of the content.
277                                 if ( ! shortcode || shortcode.content !== content )
278                                         return;
279
280                                 // Ignore the rest of the match object.
281                                 shortcode = shortcode.shortcode;
282
283                                 if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) )
284                                         shortcode.set( 'id', defaultPostId );
285
286                                 attachments = wp.media.gallery.attachments( shortcode );
287
288                                 selection = new wp.media.model.Selection( attachments.models, {
289                                         props:    attachments.props.toJSON(),
290                                         multiple: true
291                                 });
292
293                                 selection.gallery = attachments.gallery;
294
295                                 // Fetch the query's attachments, and then break ties from the
296                                 // query to allow for sorting.
297                                 selection.more().done( function() {
298                                         // Break ties with the query.
299                                         selection.props.set({ query: false });
300                                         selection.unmirror();
301                                         selection.props.unset('orderby');
302                                 });
303
304                                 // Destroy the previous gallery frame.
305                                 if ( this.frame )
306                                         this.frame.dispose();
307
308                                 // Store the current gallery frame.
309                                 this.frame = wp.media({
310                                         frame:     'post',
311                                         state:     'gallery-edit',
312                                         title:     wp.media.view.l10n.editGalleryTitle,
313                                         editing:   true,
314                                         multiple:  true,
315                                         selection: selection
316                                 }).open();
317
318                                 return this.frame;
319                         }
320                 };
321         }());
322
323         wp.media.featuredImage = {
324                 get: function() {
325                         return wp.media.view.settings.post.featuredImageId;
326                 },
327
328                 set: function( id ) {
329                         var settings = wp.media.view.settings;
330
331                         settings.post.featuredImageId = id;
332
333                         wp.media.post( 'set-post-thumbnail', {
334                                 json:         true,
335                                 post_id:      settings.post.id,
336                                 thumbnail_id: settings.post.featuredImageId,
337                                 _wpnonce:     settings.post.nonce
338                         }).done( function( html ) {
339                                 $( '.inside', '#postimagediv' ).html( html );
340                         });
341                 },
342
343                 frame: function() {
344                         if ( this._frame )
345                                 return this._frame;
346
347                         this._frame = wp.media({
348                                 state: 'featured-image',
349                                 states: [ new wp.media.controller.FeaturedImage() ]
350                         });
351
352                         this._frame.on( 'toolbar:create:featured-image', function( toolbar ) {
353                                 this.createSelectToolbar( toolbar, {
354                                         text: wp.media.view.l10n.setFeaturedImage
355                                 });
356                         }, this._frame );
357
358                         this._frame.state('featured-image').on( 'select', this.select );
359                         return this._frame;
360                 },
361
362                 select: function() {
363                         var settings = wp.media.view.settings,
364                                 selection = this.get('selection').single();
365
366                         if ( ! settings.post.featuredImageId )
367                                 return;
368
369                         wp.media.featuredImage.set( selection ? selection.id : -1 );
370                 },
371
372                 init: function() {
373                         // Open the content media manager to the 'featured image' tab when
374                         // the post thumbnail is clicked.
375                         $('#postimagediv').on( 'click', '#set-post-thumbnail', function( event ) {
376                                 event.preventDefault();
377                                 // Stop propagation to prevent thickbox from activating.
378                                 event.stopPropagation();
379
380                                 wp.media.featuredImage.frame().open();
381
382                         // Update the featured image id when the 'remove' link is clicked.
383                         }).on( 'click', '#remove-post-thumbnail', function() {
384                                 wp.media.view.settings.post.featuredImageId = -1;
385                         });
386                 }
387         };
388
389         $( wp.media.featuredImage.init );
390
391         wp.media.editor = {
392                 insert: function( h ) {
393                         var mce = typeof(tinymce) != 'undefined',
394                                 qt = typeof(QTags) != 'undefined',
395                                 wpActiveEditor = window.wpActiveEditor,
396                                 ed;
397
398                         // Delegate to the global `send_to_editor` if it exists.
399                         // This attempts to play nice with any themes/plugins that have
400                         // overridden the insert functionality.
401                         if ( window.send_to_editor )
402                                 return window.send_to_editor.apply( this, arguments );
403
404                         if ( ! wpActiveEditor ) {
405                                 if ( mce && tinymce.activeEditor ) {
406                                         ed = tinymce.activeEditor;
407                                         wpActiveEditor = window.wpActiveEditor = ed.id;
408                                 } else if ( !qt ) {
409                                         return false;
410                                 }
411                         } else if ( mce ) {
412                                 if ( tinymce.activeEditor && (tinymce.activeEditor.id == 'mce_fullscreen' || tinymce.activeEditor.id == 'wp_mce_fullscreen') )
413                                         ed = tinymce.activeEditor;
414                                 else
415                                         ed = tinymce.get(wpActiveEditor);
416                         }
417
418                         if ( ed && !ed.isHidden() ) {
419                                 // restore caret position on IE
420                                 if ( tinymce.isIE && ed.windowManager.insertimagebookmark )
421                                         ed.selection.moveToBookmark(ed.windowManager.insertimagebookmark);
422
423                                 if ( h.indexOf('[caption') !== -1 ) {
424                                         if ( ed.wpSetImgCaption )
425                                                 h = ed.wpSetImgCaption(h);
426                                 } else if ( h.indexOf('[gallery') !== -1 ) {
427                                         if ( ed.plugins.wpgallery )
428                                                 h = ed.plugins.wpgallery._do_gallery(h);
429                                 } else if ( h.indexOf('[embed') === 0 ) {
430                                         if ( ed.plugins.wordpress )
431                                                 h = ed.plugins.wordpress._setEmbed(h);
432                                 }
433
434                                 ed.execCommand('mceInsertContent', false, h);
435                         } else if ( qt ) {
436                                 QTags.insertContent(h);
437                         } else {
438                                 document.getElementById(wpActiveEditor).value += h;
439                         }
440
441                         // If the old thickbox remove function exists, call it in case
442                         // a theme/plugin overloaded it.
443                         if ( window.tb_remove )
444                                 try { window.tb_remove(); } catch( e ) {}
445                 },
446
447                 add: function( id, options ) {
448                         var workflow = this.get( id );
449
450                         if ( workflow )
451                                 return workflow;
452
453                         workflow = workflows[ id ] = wp.media( _.defaults( options || {}, {
454                                 frame:    'post',
455                                 state:    'insert',
456                                 title:    wp.media.view.l10n.addMedia,
457                                 multiple: true
458                         } ) );
459
460                         workflow.on( 'insert', function( selection ) {
461                                 var state = workflow.state();
462
463                                 selection = selection || state.get('selection');
464
465                                 if ( ! selection )
466                                         return;
467
468                                 $.when.apply( $, selection.map( function( attachment ) {
469                                         var display = state.display( attachment ).toJSON();
470                                         return this.send.attachment( display, attachment.toJSON() );
471                                 }, this ) ).done( function() {
472                                         wp.media.editor.insert( _.toArray( arguments ).join("\n\n") );
473                                 });
474                         }, this );
475
476                         workflow.state('gallery-edit').on( 'update', function( selection ) {
477                                 this.insert( wp.media.gallery.shortcode( selection ).string() );
478                         }, this );
479
480                         workflow.state('embed').on( 'select', function() {
481                                 var state = workflow.state(),
482                                         type = state.get('type'),
483                                         embed = state.props.toJSON();
484
485                                 embed.url = embed.url || '';
486
487                                 if ( 'link' === type ) {
488                                         _.defaults( embed, {
489                                                 title:   embed.url,
490                                                 linkUrl: embed.url
491                                         });
492
493                                         this.send.link( embed ).done( function( resp ) {
494                                                 wp.media.editor.insert( resp );
495                                         });
496
497                                 } else if ( 'image' === type ) {
498                                         _.defaults( embed, {
499                                                 title:   embed.url,
500                                                 linkUrl: '',
501                                                 align:   'none',
502                                                 link:    'none'
503                                         });
504
505                                         if ( 'none' === embed.link )
506                                                 embed.linkUrl = '';
507                                         else if ( 'file' === embed.link )
508                                                 embed.linkUrl = embed.url;
509
510                                         this.insert( wp.media.string.image( embed ) );
511                                 }
512                         }, this );
513
514                         workflow.state('featured-image').on( 'select', wp.media.featuredImage.select );
515                         workflow.setState( workflow.options.state );
516                         return workflow;
517                 },
518
519                 id: function( id ) {
520                         if ( id )
521                                 return id;
522
523                         // If an empty `id` is provided, default to `wpActiveEditor`.
524                         id = wpActiveEditor;
525
526                         // If that doesn't work, fall back to `tinymce.activeEditor.id`.
527                         if ( ! id && typeof tinymce !== 'undefined' && tinymce.activeEditor )
528                                 id = tinymce.activeEditor.id;
529
530                         // Last but not least, fall back to the empty string.
531                         id = id || '';
532                         return id;
533                 },
534
535                 get: function( id ) {
536                         id = this.id( id );
537                         return workflows[ id ];
538                 },
539
540                 remove: function( id ) {
541                         id = this.id( id );
542                         delete workflows[ id ];
543                 },
544
545                 send: {
546                         attachment: function( props, attachment ) {
547                                 var caption = attachment.caption,
548                                         options, html;
549
550                                 // If captions are disabled, clear the caption.
551                                 if ( ! wp.media.view.settings.captions )
552                                         delete attachment.caption;
553
554                                 props = wp.media.string.props( props, attachment );
555
556                                 options = {
557                                         id:           attachment.id,
558                                         post_content: attachment.description,
559                                         post_excerpt: caption
560                                 };
561
562                                 if ( props.linkUrl )
563                                         options.url = props.linkUrl;
564
565                                 if ( 'image' === attachment.type ) {
566                                         html = wp.media.string.image( props );
567
568                                         _.each({
569                                                 align: 'align',
570                                                 size:  'image-size',
571                                                 alt:   'image_alt'
572                                         }, function( option, prop ) {
573                                                 if ( props[ prop ] )
574                                                         options[ option ] = props[ prop ];
575                                         });
576
577                                 } else {
578                                         html = wp.media.string.link( props );
579                                         options.post_title = props.title;
580                                 }
581
582                                 return wp.media.post( 'send-attachment-to-editor', {
583                                         nonce:      wp.media.view.settings.nonce.sendToEditor,
584                                         attachment: options,
585                                         html:       html,
586                                         post_id:    wp.media.view.settings.post.id
587                                 });
588                         },
589
590                         link: function( embed ) {
591                                 return wp.media.post( 'send-link-to-editor', {
592                                         nonce:   wp.media.view.settings.nonce.sendToEditor,
593                                         src:     embed.linkUrl,
594                                         title:   embed.title,
595                                         html:    wp.media.string.link( embed ),
596                                         post_id: wp.media.view.settings.post.id
597                                 });
598                         }
599                 },
600
601                 open: function( id ) {
602                         var workflow, editor;
603
604                         id = this.id( id );
605
606                         // Save a bookmark of the caret position in IE.
607                         if ( typeof tinymce !== 'undefined' ) {
608                                 editor = tinymce.get( id );
609
610                                 if ( tinymce.isIE && editor && ! editor.isHidden() ) {
611                                         editor.focus();
612                                         editor.windowManager.insertimagebookmark = editor.selection.getBookmark();
613                                 }
614                         }
615
616                         workflow = this.get( id );
617
618                         // Initialize the editor's workflow if we haven't yet.
619                         if ( ! workflow )
620                                 workflow = this.add( id );
621
622                         return workflow.open();
623                 },
624
625                 init: function() {
626                         $(document.body).on( 'click', '.insert-media', function( event ) {
627                                 var $this = $(this),
628                                         editor = $this.data('editor');
629
630                                 event.preventDefault();
631
632                                 // Remove focus from the `.insert-media` button.
633                                 // Prevents Opera from showing the outline of the button
634                                 // above the modal.
635                                 //
636                                 // See: http://core.trac.wordpress.org/ticket/22445
637                                 $this.blur();
638
639                                 wp.media.editor.open( editor );
640                         });
641                 }
642         };
643
644         _.bindAll( wp.media.editor, 'open' );
645         $( wp.media.editor.init );
646 }(jQuery));