PluginProbe ʕ •ᴥ•ʔ
Meta Box / 4.13.0
Meta Box v4.13.0
trunk 4.1.10 4.1.11 4.10 4.10.1 4.10.2 4.10.3 4.10.4 4.11 4.11.1 4.11.2 4.12.1 4.12.4 4.12.5 4.12.6 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.10 4.14.11 4.14.2 4.14.4 4.14.5 4.14.6 4.14.7 4.14.8 4.14.9 4.15.0 4.15.1 4.15.2 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.2 4.16.3 4.17.0 4.17.1 4.17.2 4.17.3 4.18.0 4.18.1 4.18.2 4.18.3 4.18.4 4.2 4.2.1 4.2.2 4.2.3 4.2.4 4.3 4.3.1 4.3.10 4.3.11 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.3.8 4.3.9 4.4.0 4.4.1 4.4.3 4.5 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.5.6 4.5.7 4.6 4.7 4.7.1 4.7.2 4.7.3 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.9 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.6 4.9.7 4.9.8 5.0.0 5.0.1 5.1.0 5.1.1 5.1.2 5.10.0 5.10.1 5.10.10 5.10.11 5.10.12 5.10.13 5.10.14 5.10.15 5.10.16 5.10.17 5.10.18 5.10.19 5.10.2 5.10.3 5.10.4 5.10.5 5.10.6 5.10.7 5.10.8 5.10.9 5.11.0 5.11.1 5.11.2 5.11.3 5.11.4 5.12.0 5.2.0 5.2.1 5.2.10 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.10 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8 5.3.9 5.4.0 5.4.1 5.4.2 5.4.3 5.4.4 5.4.5 5.4.6 5.4.7 5.4.8 5.5.0 5.5.1 5.6.0 5.6.1 5.6.10 5.6.11 5.6.12 5.6.13 5.6.14 5.6.15 5.6.16 5.6.17 5.6.18 5.6.2 5.6.3 5.6.4 5.6.5 5.6.6 5.6.7 5.6.8 5.6.9 5.7.0 5.7.1 5.7.2 5.7.3 5.7.4 5.7.5 5.8.0 5.8.1 5.8.2 5.9.0 5.9.1 5.9.10 5.9.11 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 5.9.9
meta-box / js / media.js
meta-box / js Last commit date
jquery-validation 9 years ago jqueryui 9 years ago select2 10 years ago wp-color-picker-alpha 8 years ago autocomplete.js 8 years ago autosave.js 8 years ago button-group.js 8 years ago clone.js 8 years ago color.js 9 years ago date.js 8 years ago datetime.js 8 years ago file-input.js 9 years ago file-upload.js 8 years ago file.js 8 years ago image-advanced.js 8 years ago image-select.js 9 years ago image-upload.js 8 years ago input-list.js 8 years ago map-frontend.js 8 years ago map.js 8 years ago media.js 8 years ago oembed.js 8 years ago range.js 8 years ago script.js 8 years ago select-advanced.js 8 years ago select-tree.js 9 years ago select.js 8 years ago slider.js 8 years ago thickbox-image.js 8 years ago time.js 9 years ago validate.js 8 years ago video.js 8 years ago wysiwyg.js 8 years ago
media.js
618 lines
1 /* global jQuery, _,i18nRwmbMedia */
2
3 window.rwmb = window.rwmb || {};
4
5 jQuery( function ( $ ) {
6 'use strict';
7
8 var views = rwmb.views = rwmb.views || {},
9 models = rwmb.models = rwmb.models || {},
10 media = wp.media,
11 MediaFrame = media.view.MediaFrame,
12 MediaCollection, Controller, MediaField, MediaList, MediaItem, MediaButton, MediaStatus, EditMedia,
13 MediaDetails, MediaLibrary, MediaSelect;
14
15 MediaCollection = models.MediaCollection = media.model.Attachments.extend( {
16 initialize: function ( models, options ) {
17 this.controller = options.controller || new models.Controller;
18 this.on( 'add remove reset', function () {
19 var max = this.controller.get( 'maxFiles' );
20 this.controller.set( 'length', this.length );
21 this.controller.set( 'full', max > 0 && this.length >= max );
22 } );
23
24 media.model.Attachments.prototype.initialize.call( this, models, options );
25 },
26
27 add: function ( models, options ) {
28 var max = this.controller.get( 'maxFiles' ),
29 left = max - this.length;
30
31 if ( max > 0 && left <= 0 ) {
32 return this;
33 }
34 if( models) {
35 if ( ! models.hasOwnProperty( 'length' ) ) {
36 models = [models];
37 } else if ( models instanceof media.model.Attachments ) {
38 models = models.models;
39 }
40 }
41 if ( left > 0 ) {
42 models = _.difference( models, this.models );
43 models = _.first( models, left );
44 }
45
46 return media.model.Attachments.prototype.add.call( this, models, options );
47 },
48
49 remove: function ( models, options ) {
50 models = media.model.Attachments.prototype.remove.call( this, models, options );
51 if ( this.controller.get( 'forceDelete' ) === true ) {
52 models = ! _.isArray( models ) ? [models] : models;
53 _.each( models, function ( model ) {
54 model.destroy();
55 } );
56 }
57 },
58
59 destroyAll: function () {
60 _.each( _.clone( this.models ), function ( model ) {
61 model.destroy();
62 } );
63 }
64 } );
65
66 /***
67 * Controller Model
68 * Manages data of media field and media models. Most of the media views will use this to manage the media
69 */
70 Controller = models.Controller = Backbone.Model.extend( {
71 //Default options
72 defaults: {
73 maxFiles: 0,
74 ids: [],
75 mimeType: '',
76 forceDelete: false,
77 maxStatus: true,
78 length: 0
79 },
80
81 //Initialize Controller model
82 initialize: function () {
83 // All numbers, no 0 ids
84 this.set( 'ids', _.without( _.map( this.get( 'ids' ), Number ), 0, - 1 ) );
85
86 // Create items collection
87 this.set( 'items', new MediaCollection( [], {controller: this} ) );
88
89 // Listen for destroy event on controller, delete all models when triggered
90 this.on( 'destroy', function () {
91 if ( this.get( 'forceDelete' ) ) {
92 this.get( 'items' ).destroyAll();
93 }
94 } );
95 },
96
97
98 // Load initial media
99 load: function () {
100 if ( _.isEmpty( this.get( 'ids' ) ) ) {
101 return;
102 }
103 this.get( 'items' ).props.set( {
104 query: true,
105 include: this.get( 'ids' ),
106 orderby: 'post__in',
107 order: 'ASC',
108 type: this.get( 'mimeType' ),
109 perPage: this.get( 'maxFiles' ) || - 1
110 } );
111 // Get more then trigger ready
112 this.get( 'items' ).more();
113 }
114 } );
115
116 /***
117 * MediaField
118 * Sets up media field view and subviews
119 */
120 MediaField = views.MediaField = Backbone.View.extend( {
121 className: 'rwmb-media-view',
122 initialize: function ( options ) {
123 var that = this,
124 fieldName = options.input.name;
125 this.$input = $( options.input );
126
127 if ( 1 != this.$input.attr( 'data-single-image' ) ) {
128 fieldName += '[]';
129 }
130
131 this.controller = new Controller( _.extend(
132 {
133 fieldName: fieldName,
134 ids: this.$input.val().split( ',' )
135 },
136 this.$input.data( 'options' )
137 ) );
138
139 // Create views
140 this.createList();
141 this.createAddButton();
142 this.createStatus();
143
144 // Render
145 this.render();
146
147 // Load media
148 this.controller.load();
149
150 // Listen for destroy event on input
151 this.$input.on( 'remove', function () {
152 that.controller.destroy();
153 } );
154
155 this.$input.on( 'media:reset', function() {
156 that.controller.get( 'items' ).reset();
157 } );
158
159 this.controller.get( 'items' ).on( 'add remove reset', _.debounce( function () {
160 that.$input.trigger( 'change', [that.$( '.rwmb-media-input' )] );
161 }, 500 ) );
162
163 this.controller.get( 'items' ).on( 'remove', _.debounce( function () {
164 that.$input.val( '' );
165 }, 500 ) );
166 },
167
168 // Creates media list
169 createList: function () {
170 this.list = new MediaList( {controller: this.controller} );
171 },
172
173 // Creates button that adds media
174 createAddButton: function () {
175 this.addButton = new MediaButton( {controller: this.controller} );
176 },
177
178 // Creates status
179 createStatus: function () {
180 this.status = new MediaStatus( {controller: this.controller} );
181 },
182
183 // Render field and adds sub fields
184 render: function () {
185 // Empty then add parts
186 this.$el.empty().append(
187 this.list.el,
188 this.status.el,
189 this.addButton.el
190 );
191 }
192 } );
193
194 /***
195 * Media List
196 * lists media
197 */
198 MediaList = views.MediaList = Backbone.View.extend( {
199 tagName: 'ul',
200 className: 'rwmb-media-list',
201
202 initialize: function ( options ) {
203 this.controller = options.controller;
204 this.collection = this.controller.get( 'items' );
205 this.itemView = options.itemView || MediaItem;
206 this.getItemView = _.memoize( function ( item ) {
207 var itemView = new this.itemView( {
208 model: item,
209 controller: this.controller
210 } );
211
212 this.listenToItemView( itemView );
213
214 return itemView;
215 },
216 function ( item ) {
217 return item.cid;
218 } );
219
220 this.listenTo( this.collection, 'add', this.addItemView );
221 this.listenTo( this.collection, 'remove', this.removeItemView );
222 this.listenTo( this.collection, 'reset', this.resetItemViews );
223
224 // Sort media using sortable
225 this.initSortable();
226 },
227
228 listenToItemView: function ( itemView ) {
229 this.listenTo( itemView, 'click:remove', this.removeItem );
230 this.listenTo( itemView, 'click:switch', this.switchItem );
231 this.listenTo( itemView, 'click:edit', this.editItem );
232 },
233
234 //Add item view
235 addItemView: function ( item ) {
236 var index = this.collection.indexOf( item ),
237 itemEl = this.getItemView( item ).el;
238
239 if ( 0 >= index ) {
240 this.$el.prepend( itemEl );
241 }
242 else if ( this.$el.children().length <= index ) {
243 this.$el.append( itemEl )
244 }
245 else {
246 this.$el.children().eq( index - 1 ).after( itemEl );
247 }
248 },
249
250 // Remove item view
251 removeItemView: function ( item ) {
252 this.getItemView( item ).$el.detach();
253 },
254
255 removeItem: function ( item ) {
256 this.collection.remove( item );
257 },
258
259 resetItemViews: function( items, options ){
260 var that = this;
261 _.each( options.previousModels, function( item ){
262 that.removeItemView( item );
263 } );
264 items.each( function( item ) {
265 that.addItemView( item );
266 } );
267 },
268
269 switchItem: function ( item ) {
270 if ( this._switchFrame ) {
271 //this.stopListening( this._frame );
272 this._switchFrame.dispose();
273 }
274 this._switchFrame = new MediaSelect( {
275 className: 'media-frame rwmb-media-frame',
276 multiple: false,
277 title: i18nRwmbMedia.select,
278 editing: true,
279 library: {
280 type: this.controller.get( 'mimeType' )
281 },
282 edit: this.controller.get( 'items' )
283 } );
284
285 this._switchFrame.on( 'select', function () {
286 var selection = this._switchFrame.state().get( 'selection' ),
287 collection = this.collection,
288 index = collection.indexOf( item );
289
290 if ( ! _.isEmpty( selection ) ) {
291 collection.remove( item );
292 collection.add( selection, {at: index} );
293 }
294 }, this );
295
296 this._switchFrame.open();
297 return false;
298 },
299
300 editItem: function ( item ) {
301 // Destroy the previous collection frame.
302 if ( this._editFrame ) {
303 //this.stopListening( this._frame );
304 this._editFrame.dispose();
305 }
306
307 // Trigger the media frame to open the correct item
308 this._editFrame = new EditMedia( {
309 frame: 'edit-attachments',
310 controller: {
311 // Needed to trick Edit modal to think there is a gridRouter
312 gridRouter: {
313 navigate: function ( destination ) {
314 },
315 baseUrl: function ( url ) {
316 }
317 }
318 },
319 library: this.collection,
320 model: item
321 } );
322
323 this._editFrame.open();
324 },
325
326 initSortable: function () {
327 var collection = this.controller.get( 'items' );
328 this.$el.sortable( {
329 // Clone the element and the clone will be dragged. Prevent trigger click on the image, which means reselect.
330 helper : 'clone',
331
332 // Record the initial `index` of the dragged model.
333 start: function ( event, ui ) {
334 ui.item.data( 'sortableIndexStart', ui.item.index() );
335 },
336
337 // Update the model's index in the collection.
338 // Do so silently, as the view is already accurate.
339 update: function ( event, ui ) {
340 var model = collection.at( ui.item.data( 'sortableIndexStart' ) );
341
342 // Silently shift the model to its new index.
343 collection.remove( model, {
344 silent: true
345 } );
346 collection.add( model, {
347 silent: true,
348 at: ui.item.index()
349 } );
350
351 // Fire the `reset` event to ensure other collections sync.
352 collection.trigger( 'reset', collection );
353 }
354 } );
355 }
356 } );
357
358 /***
359 * MediaStatus view.
360 * Show number of selected/uploaded files and number of files remain if "maxStatus" parameter is true.
361 */
362 MediaStatus = views.MediaStatus = Backbone.View.extend( {
363 tagName: 'div',
364 className: 'rwmb-media-status',
365 template: wp.template( 'rwmb-media-status' ),
366
367 initialize: function ( options ) {
368 this.controller = options.controller;
369
370 // Auto hide if maxStatus is false
371 if ( ! this.controller.get( 'maxStatus' ) ) {
372 this.$el.hide();
373 return;
374 }
375
376 // Re-render if changes happen in controller
377 this.listenTo( this.controller.get( 'items' ), 'update', this.render );
378 this.listenTo( this.controller.get( 'items' ), 'reset', this.render );
379
380 // Render
381 this.render();
382 },
383
384 render: function () {
385 var attributes = _.clone( this.controller.attributes );
386 this.$el.html( this.template( attributes ) );
387 }
388 } );
389
390 /***
391 * Media Button
392 * Selects and adds media to controller
393 */
394 MediaButton = views.MediaButton = Backbone.View.extend( {
395 tagName: 'div',
396 className: 'rwmb-media-add',
397 template: wp.template( 'rwmb-media-button' ),
398 events: {
399 'click .button': function () {
400 // Destroy the previous collection frame.
401 if ( this._frame ) {
402 //this.stopListening( this._frame );
403 this._frame.dispose();
404 }
405 var maxFiles = this.controller.get( 'maxFiles' );
406 this._frame = new MediaSelect( {
407 className: 'media-frame rwmb-media-frame',
408 multiple: maxFiles > 1 || maxFiles <= 0 ? 'add' : false,
409 title: i18nRwmbMedia.select,
410 editing: true,
411 library: {
412 type: this.controller.get( 'mimeType' )
413 },
414 edit: this.controller.get( 'items' )
415 } );
416
417 this._frame.on( 'select', function () {
418 var selection = this._frame.state().get( 'selection' );
419 this.controller.get( 'items' ).add( selection.models );
420 }, this );
421
422 this._frame.open();
423 }
424 },
425 render: function () {
426 this.$el.html( this.template( {text: i18nRwmbMedia.add} ) );
427 return this;
428 },
429
430 initialize: function ( options ) {
431 this.controller = options.controller;
432 this.collection = this.controller.get( 'items' );
433
434 // Auto hide if you reach the max number of media
435 this.listenTo( this.controller, 'change:full', function () {
436 this.$el.toggle( ! this.controller.get( 'full' ) );
437 } );
438
439 this.render();
440 }
441 } );
442
443 /***
444 * MediaItem
445 * View for individual media items
446 */
447 MediaItem = views.MediaItem = Backbone.View.extend( {
448 tagName: 'li',
449 className: 'rwmb-media-item attachment',
450 template: wp.template( 'rwmb-media-item' ),
451 initialize: function ( options ) {
452 this.controller = options.controller;
453 this.render();
454 this.listenTo( this.model, 'change', function () {
455 this.render();
456 } );
457
458 this.$el.data( 'id', this.model.cid );
459 },
460
461
462 events: {
463 'click .rwmb-overlay': function () {
464 this.trigger( 'click:switch', this.model );
465 return false;
466 },
467
468 // Event when remove button clicked
469 'click .rwmb-remove-media': function () {
470 this.trigger( 'click:remove', this.model );
471 return false;
472 },
473
474 'click .rwmb-edit-media': function () {
475 this.trigger( 'click:edit', this.model );
476 return false;
477 }
478 },
479
480 render: function () {
481 var attrs = _.clone( this.model.attributes );
482 attrs.controller = _.clone( this.controller.attributes );
483 this.$el.html( this.template( attrs ) );
484 return this;
485 }
486 } );
487
488 /**
489 * Extend media frames to make things work right
490 */
491
492 /**
493 * MediaDetails
494 * Custom version of TwoColumn view to prevent all video and audio from being unset
495 */
496 MediaDetails = views.MediaDetails = media.view.Attachment.Details.TwoColumn.extend( {
497 render: function () {
498 var that = this;
499 media.view.Attachment.Details.prototype.render.apply( this, arguments );
500 this.players = this.players || [];
501
502 media.mixin.unsetPlayers.call( this );
503
504 this.$( 'audio, video' ).each( function ( i, elem ) {
505 var el = media.view.MediaDetails.prepareSrc( elem );
506 that.players.push( new window.MediaElementPlayer( el, media.mixin.mejsSettings ) );
507 } );
508 }
509 } );
510
511 /**
512 * MediaLibrary
513 * Custom version of Library to exclude already selected media in a media frame
514 */
515 MediaLibrary = media.controller.Library.extend( {
516 defaults: _.defaults( {
517 multiple: 'add',
518 filterable: 'uploaded',
519 priority: 100,
520 syncSelection: false
521 }, media.controller.Library.prototype.defaults ),
522
523 activate: function () {
524 var library = this.get( 'library' ),
525 edit = this.frame.options.edit;
526
527 if ( this.editLibrary && this.editLibrary !== edit ) {
528 library.unobserve( this.editLibrary );
529 }
530
531 // Accepts attachments that exist in the original library and
532 // that do not exist in gallery's library.
533 library.validator = function ( attachment ) {
534 return ! ! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && media.model.Selection.prototype.validator.apply( this, arguments );
535 };
536
537 // Reset the library to ensure that all attachments are re-added
538 // to the collection. Do so silently, as calling `observe` will
539 // trigger the `reset` event.
540 library.reset( library.mirroring.models, {silent: true} );
541 library.observe( edit );
542 this.editLibrary = edit;
543
544 media.controller.Library.prototype.activate.apply( this, arguments );
545 }
546 } );
547
548 /**
549 * MediaSelect
550 * Custom version of Select media frame that uses MediaLibrary
551 */
552 MediaSelect = views.MediaSelect = MediaFrame.Select.extend( {
553 /**
554 * Create the default states on the frame.
555 */
556 createStates: function () {
557 var options = this.options;
558
559 if ( this.options.states ) {
560 return;
561 }
562
563 // Add the default states.
564 this.states.add( [
565 // Main states.
566 new MediaLibrary( {
567 library: media.query( options.library ),
568 multiple: options.multiple,
569 title: options.title,
570 priority: 20
571 } )
572 ] );
573 }
574 } );
575
576 /***
577 * EditMedia
578 * Custom version of EditAttachments frame to prevent all video and audio from being unset
579 */
580 EditMedia = views.EditMedia = MediaFrame.EditAttachments.extend( {
581 /**
582 * Content region rendering callback for the `edit-metadata` mode.
583 *
584 * @param {Object} contentRegion Basic object with a `view` property, which
585 * should be set with the proper region view.
586 */
587 editMetadataMode: function ( contentRegion ) {
588 contentRegion.view = new MediaDetails( {
589 controller: this,
590 model: this.model
591 } );
592
593 /**
594 * Attach a subview to display fields added via the
595 * `attachment_fields_to_edit` filter.
596 */
597 contentRegion.view.views.set( '.attachment-compat', new media.view.AttachmentCompat( {
598 controller: this,
599 model: this.model
600 } ) );
601 }
602 } );
603
604 /**
605 * Initialize media fields
606 * @return void
607 */
608 function initMediaField() {
609 var view = new MediaField( { input: this } );
610 //Remove old then add new
611 $( this ).siblings( 'div.rwmb-media-view' ).remove();
612 $( this ).after( view.el );
613 }
614
615 $( '.rwmb-file_advanced' ).each( initMediaField );
616 $( document ).on( 'clone', '.rwmb-file_advanced', initMediaField );
617 } );
618