PluginProbe ʕ •ᴥ•ʔ
Meta Box / 5.3.9
Meta Box v5.3.9
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
jqueryui 9 years ago select2 6 years ago wp-color-picker-alpha 5 years ago autocomplete.js 6 years ago autosave.js 7 years ago button-group.js 6 years ago clone.js 5 years ago color.js 6 years ago date.js 6 years ago datetime.js 6 years ago file-input.js 6 years ago file-upload.js 6 years ago file.js 5 years ago image-advanced.js 6 years ago image-select.js 6 years ago image-upload.js 6 years ago input-list.js 6 years ago map-frontend.js 6 years ago map.js 5 years ago media.js 5 years ago notification.js 6 years ago oembed.js 6 years ago osm-frontend.js 7 years ago osm.js 5 years ago range.js 6 years ago script.js 6 years ago select-advanced.js 6 years ago select-tree.js 6 years ago select.js 6 years ago slider.js 6 years ago taxonomy.js 6 years ago time.js 6 years ago validation.min.js 5 years ago video.js 6 years ago wysiwyg.js 5 years ago
media.js
586 lines
1 ( function ( $, wp, _, rwmb, i18n ) {
2 'use strict';
3
4 var views = rwmb.views = rwmb.views || {},
5 models = rwmb.models = rwmb.models || {},
6 media = wp.media,
7 MediaFrame = media.view.MediaFrame,
8 MediaCollection, Controller, MediaField, MediaList, MediaItem, MediaButton, MediaStatus, EditMedia,
9 MediaDetails, MediaLibrary, MediaSelect;
10
11 MediaCollection = Backbone.Collection.extend( {
12 model: wp.media.model.Attachment,
13
14 initialize: function ( models, options ) {
15 this.controller = options.controller || new models.Controller;
16 this.on( 'add remove reset', function () {
17 var max = this.controller.get( 'maxFiles' );
18 this.controller.set( 'length', this.length );
19 this.controller.set( 'full', max > 0 && this.length >= max );
20 } );
21 },
22
23 add: function ( models, options ) {
24 var max = this.controller.get( 'maxFiles' ),
25 left = max - this.length;
26
27 if ( ! models || ( max > 0 && left <= 0 ) ) {
28 return this;
29 }
30 if ( ! models.hasOwnProperty( 'length' ) ) {
31 models = [models];
32 } else if ( models instanceof media.model.Attachments ) {
33 models = models.models;
34 }
35
36 models = _.difference( models, this.models );
37 if ( left > 0 ) {
38 models = _.first( models, left );
39 }
40
41 Backbone.Collection.prototype.add.call( this, models, options );
42 },
43
44 remove: function ( models, options ) {
45 // Don't remove models if event is not fired from MB plugin.
46 if( ! $( event.target ).closest( '.rwmb-field, [data-class="rwmb-field"]' ).length ) {
47 return;
48 }
49 models = Backbone.Collection.prototype.remove.call( this, models, options );
50 if ( this.controller.get( 'forceDelete' ) === true ) {
51 models = ! _.isArray( models ) ? [models] : models;
52 _.each( models, function ( model ) {
53 model.destroy();
54 } );
55 }
56 },
57
58 destroyAll: function () {
59 _.each( _.clone( this.models ), function ( model ) {
60 model.destroy();
61 } );
62 }
63 } );
64
65 /***
66 * Controller Model
67 * Manages data of media field and media models. Most of the media views will use this to manage the media
68 */
69 Controller = models.Controller = Backbone.Model.extend( {
70 //Default options
71 defaults: {
72 maxFiles: 0,
73 ids: [],
74 mimeType: '',
75 forceDelete: false,
76 maxStatus: true,
77 length: 0
78 },
79
80 //Initialize Controller model
81 initialize: function () {
82 // All numbers, no 0 ids
83 this.set( 'ids', _.without( _.map( this.get( 'ids' ), Number ), 0, - 1 ) );
84
85 // Create items collection
86 this.set( 'items', new MediaCollection( [], {controller: this} ) );
87
88 // Listen for destroy event on controller, delete all models when triggered
89 this.on( 'destroy', function () {
90 if ( this.get( 'forceDelete' ) ) {
91 this.get( 'items' ).destroyAll();
92 }
93 } );
94 }
95 } );
96
97 /***
98 * MediaField
99 * Sets up media field view and subviews
100 */
101 MediaField = views.MediaField = Backbone.View.extend( {
102 className: 'rwmb-media-view',
103 initialize: function ( options ) {
104 var that = this,
105 fieldName = options.input.name;
106 this.$input = $( options.input );
107
108 if ( 1 != this.$input.attr( 'data-single-image' ) ) {
109 fieldName += '[]';
110 }
111
112 this.controller = new Controller( _.extend(
113 {
114 fieldName: fieldName,
115 ids: this.$input.val().split( ',' )
116 },
117 this.$input.data( 'options' )
118 ) );
119
120 // Create views
121 this.createList();
122 this.createAddButton();
123 this.createStatus();
124
125 this.render();
126 this.loadInitialAttachments();
127
128 // Listen for destroy event on input
129 this.$input.on( 'remove', function () {
130 that.controller.destroy();
131 } );
132
133 var collection = this.controller.get( 'items' );
134 this.$input.on( 'media:reset', function() {
135 collection.reset();
136 } );
137
138 collection.on( 'add remove reset', _.debounce( function () {
139 var ids = collection.pluck( 'id' ).join( ',' );
140 that.$input.val( ids ).trigger( 'change', [that.$( '.rwmb-media-input' )] );
141 }, 500 ) );
142 },
143
144 loadInitialAttachments: function () {
145 if ( ! this.$input.val() ) {
146 return;
147 }
148 var models = this.$input.data( 'attachments' ).map( function( attachment ) {
149 return wp.media.model.Attachment.create( attachment );
150 } );
151 this.controller.get( 'items' ).add( models );
152 },
153
154 // Creates media list
155 createList: function () {
156 this.list = new MediaList( {controller: this.controller} );
157 },
158
159 // Creates button that adds media
160 createAddButton: function () {
161 this.addButton = new MediaButton( {controller: this.controller} );
162 },
163
164 // Creates status
165 createStatus: function () {
166 this.status = new MediaStatus( {controller: this.controller} );
167 },
168
169 // Render field and adds sub fields
170 render: function () {
171 // Empty then add parts
172 this.$el.empty().append(
173 this.list.el,
174 this.status.el,
175 this.addButton.el
176 );
177 }
178 } );
179
180 /***
181 * Media List
182 * lists media
183 */
184 MediaList = views.MediaList = Backbone.View.extend( {
185 tagName: 'ul',
186 className: 'rwmb-media-list',
187
188 initialize: function ( options ) {
189 this.controller = options.controller;
190 this.collection = this.controller.get( 'items' );
191 this.itemView = options.itemView || MediaItem;
192 this.getItemView = _.memoize( function ( item ) {
193 var itemView = new this.itemView( {
194 model: item,
195 controller: this.controller
196 } );
197
198 this.listenToItemView( itemView );
199
200 return itemView;
201 },
202 function ( item ) {
203 return item.cid;
204 }
205 );
206
207 this.listenTo( this.collection, 'add', this.addItemView );
208 this.listenTo( this.collection, 'remove', this.removeItemView );
209 this.listenTo( this.collection, 'reset', this.resetItemViews );
210
211 // Sort items using helper 'clone' to prevent trigger click on the image, which means reselect.
212 this.$el.sortable( {
213 helper : 'clone',
214 update: function( event, ui ) {
215 ui.item.find( rwmb.inputSelectors ).first().trigger( 'mb_change' );
216 }
217 } );
218 },
219
220 listenToItemView: function ( itemView ) {
221 this.listenTo( itemView, 'click:remove', this.removeItem );
222 this.listenTo( itemView, 'click:switch', this.switchItem );
223 this.listenTo( itemView, 'click:edit', this.editItem );
224 },
225
226 addItemView: function ( item ) {
227 var index = this.collection.indexOf( item ),
228 itemEl = this.getItemView( item ).el,
229 $children = this.$el.children();
230
231 if ( 0 >= index ) {
232 this.$el.prepend( itemEl );
233 } else if ( $children.length <= index ) {
234 this.$el.append( itemEl )
235 } else {
236 $children.eq( index - 1 ).after( itemEl );
237 }
238 },
239
240 // Remove item view
241 removeItemView: function ( item ) {
242 this.getItemView( item ).$el.detach();
243 },
244
245 removeItem: function ( item ) {
246 this.collection.remove( item );
247 },
248
249 resetItemViews: function( items ){
250 var that = this;
251 _.each( that.models, that.removeItemView );
252 items.each( that.addItemView );
253 },
254
255 switchItem: function ( item ) {
256 if ( this._switchFrame ) {
257 this._switchFrame.dispose();
258 }
259 this._switchFrame = new MediaSelect( {
260 multiple: false,
261 editing: true,
262 library: {
263 type: this.controller.get( 'mimeType' )
264 },
265 edit: this.collection
266 } );
267
268 // Refresh content when frame opens
269 this._switchFrame.on( 'open', function() {
270 var frameContent = this._switchFrame.content.get();
271 if ( frameContent && frameContent.collection ) {
272 frameContent.collection.mirroring._hasMore = true;
273 frameContent.collection.more();
274 }
275 }, this );
276
277 this._switchFrame.on( 'select', function () {
278 var selection = this._switchFrame.state().get( 'selection' ),
279 collection = this.collection,
280 index = collection.indexOf( item );
281
282 if ( ! _.isEmpty( selection ) ) {
283 collection.remove( item );
284 collection.add( selection, {at: index} );
285 }
286 }, this );
287
288 this._switchFrame.open();
289 return false;
290 },
291
292 editItem: function ( item ) {
293 if ( this._editFrame ) {
294 this._editFrame.dispose();
295 }
296
297 // Trigger the media frame to open the correct item.
298 this._editFrame = new EditMedia( {
299 frame: 'edit-attachments',
300 controller: {
301 gridRouter: new wp.media.view.MediaFrame.Manage.Router()
302 },
303 library: this.collection,
304 model: item
305 } );
306
307 this._editFrame.open();
308 }
309 } );
310
311 /***
312 * MediaStatus view.
313 * Show number of selected/uploaded files and number of files remain if "maxStatus" parameter is true.
314 */
315 MediaStatus = views.MediaStatus = Backbone.View.extend( {
316 tagName: 'div',
317 className: 'rwmb-media-status',
318 template: wp.template( 'rwmb-media-status' ),
319
320 initialize: function ( options ) {
321 this.controller = options.controller;
322
323 // Auto hide if maxStatus is false
324 if ( ! this.controller.get( 'maxStatus' ) ) {
325 this.$el.hide();
326 return;
327 }
328
329 // Re-render if changes happen in controller
330 this.listenTo( this.controller.get( 'items' ), 'update', this.render );
331 this.listenTo( this.controller.get( 'items' ), 'reset', this.render );
332
333 // Render
334 this.render();
335 },
336
337 render: function () {
338 this.$el.html( this.template( this.controller.toJSON() ) );
339 }
340 } );
341
342 /***
343 * Media Button
344 * Selects and adds media to controller
345 */
346 MediaButton = views.MediaButton = Backbone.View.extend( {
347 tagName: 'div',
348 className: 'rwmb-media-add',
349 template: wp.template( 'rwmb-media-button' ),
350 events: {
351 'click .button': function () {
352 if ( this._frame ) {
353 this._frame.dispose();
354 }
355 var maxFiles = this.controller.get( 'maxFiles' );
356 this._frame = new MediaSelect( {
357 multiple: maxFiles > 1 || maxFiles <= 0 ? 'add' : false,
358 editing: true,
359 library: {
360 type: this.controller.get( 'mimeType' )
361 },
362 edit: this.collection
363 } );
364
365 // Refresh content when frame opens
366 this._frame.on( 'open', function() {
367 var frameContent = this._frame.content.get();
368 if ( frameContent && frameContent.collection ) {
369 frameContent.collection.mirroring._hasMore = true;
370 frameContent.collection.more();
371 }
372 }, this );
373
374 this._frame.on( 'select', function () {
375 var selection = this._frame.state().get( 'selection' );
376 if ( this.controller.get( 'addTo' ) === 'beginning' ) {
377 this.collection.add( selection.models, {at: 0} );
378 } else {
379 this.collection.add( selection.models );
380 }
381 }, this );
382
383 this._frame.open();
384 }
385 },
386 render: function () {
387 this.$el.html( this.template( {text: i18n.add} ) );
388 return this;
389 },
390
391 initialize: function ( options ) {
392 this.controller = options.controller;
393 this.collection = this.controller.get( 'items' );
394
395 // Auto hide if you reach the max number of media
396 this.listenTo( this.controller, 'change:full', function () {
397 this.$el.toggle( ! this.controller.get( 'full' ) );
398 } );
399
400 this.render();
401 }
402 } );
403
404 /***
405 * MediaItem
406 * View for individual media items
407 */
408 MediaItem = views.MediaItem = Backbone.View.extend( {
409 tagName: 'li',
410 className: 'rwmb-media-item attachment',
411 template: wp.template( 'rwmb-media-item' ),
412 initialize: function ( options ) {
413 this.controller = options.controller;
414 this.collection = this.controller.get( 'items' );
415 this.render();
416 this.listenTo( this.model, 'change', this.render );
417
418 this.$el.data( 'id', this.model.cid );
419 },
420
421 events: {
422 'click .rwmb-image-overlay': function ( e ) {
423 e.preventDefault();
424 this.trigger( 'click:switch', this.model );
425 },
426 'click .rwmb-remove-media': function ( e ) {
427 e.preventDefault();
428 this.trigger( 'click:remove', this.model );
429 },
430 'click .rwmb-edit-media': function ( e ) {
431 e.preventDefault();
432 this.trigger( 'click:edit', this.model );
433 }
434 },
435
436 render: function () {
437 var data = this.model.toJSON();
438 data.controller = this.controller.toJSON();
439 this.$el.html( this.template( data ) );
440 return this;
441 }
442 } );
443
444 /**
445 * Extend media frames to make things work right
446 */
447
448 /**
449 * MediaDetails
450 * Custom version of TwoColumn view to prevent all video and audio from being unset
451 */
452 MediaDetails = views.MediaDetails = media.view.Attachment.Details.TwoColumn.extend( {
453 render: function () {
454 var that = this;
455 media.view.Attachment.Details.prototype.render.apply( this, arguments );
456 this.players = this.players || [];
457
458 media.mixin.unsetPlayers.call( this );
459
460 this.$( 'audio, video' ).each( function ( i, elem ) {
461 var el = media.view.MediaDetails.prepareSrc( elem );
462 that.players.push( new window.MediaElementPlayer( el, media.mixin.mejsSettings ) );
463 } );
464 }
465 } );
466
467 /**
468 * MediaLibrary
469 * Custom version of Library to exclude already selected media in a media frame
470 */
471 MediaLibrary = media.controller.Library.extend( {
472 defaults: _.defaults( {
473 multiple: 'add',
474 filterable: 'all',
475 priority: 100,
476 syncSelection: false
477 }, media.controller.Library.prototype.defaults ),
478
479 activate: function () {
480 var library = this.get( 'library' ),
481 edit = this.frame.options.edit;
482
483 if ( this.editLibrary && this.editLibrary !== edit ) {
484 library.unobserve( this.editLibrary );
485 }
486
487 // Accepts attachments that exist in the original library and
488 // that do not exist in gallery's library.
489 library.validator = function ( attachment ) {
490 return ! ! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && media.model.Selection.prototype.validator.apply( this, arguments );
491 };
492
493 // Reset the library to ensure that all attachments are re-added
494 // to the collection. Do so silently, as calling `observe` will
495 // trigger the `reset` event.
496 library.reset( library.mirroring.models, {silent: true} );
497 library.observe( edit );
498 this.editLibrary = edit;
499
500 media.controller.Library.prototype.activate.apply( this, arguments );
501 }
502 } );
503
504 /**
505 * MediaSelect
506 * Custom version of Select media frame that uses MediaLibrary
507 */
508 MediaSelect = views.MediaSelect = MediaFrame.Select.extend( {
509 /**
510 * Create the default states on the frame.
511 */
512 createStates: function () {
513 var options = this.options;
514
515 // Add reference so we know MediaFrame belongs to MB plugin.
516 this.$el.attr( 'data-class', 'rwmb-field' );
517
518 if ( this.options.states ) {
519 return;
520 }
521
522 // Add the default states.
523 this.states.add( [
524 // Main states.
525 new MediaLibrary( {
526 library: media.query( options.library ),
527 multiple: options.multiple,
528 priority: 20
529 } )
530 ] );
531 }
532 } );
533
534 /***
535 * EditMedia
536 * Custom version of EditAttachments frame to prevent all video and audio from being unset
537 */
538 EditMedia = views.EditMedia = MediaFrame.EditAttachments.extend( {
539 /**
540 * Content region rendering callback for the `edit-metadata` mode.
541 *
542 * @param {Object} contentRegion Basic object with a `view` property, which
543 * should be set with the proper region view.
544 */
545 editMetadataMode: function ( contentRegion ) {
546 contentRegion.view = new MediaDetails( {
547 controller: this,
548 model: this.model
549 } );
550
551 /**
552 * Attach a subview to display fields added via the
553 * `attachment_fields_to_edit` filter.
554 */
555 contentRegion.view.views.set( '.attachment-compat', new media.view.AttachmentCompat( {
556 controller: this,
557 model: this.model
558 } ) );
559 },
560 resetRoute: function() {}
561 } );
562
563 function initMediaField() {
564 var $this = $( this ),
565 view = $this.data( 'view' );
566
567 if ( view ) {
568 return;
569 }
570
571 view = new MediaField( { input: this } );
572
573 $this.siblings( '.rwmb-media-view' ).remove();
574 $this.after( view.el );
575 $this.data( 'view', view );
576 }
577
578 function init( e ) {
579 $( e.target ).find( '.rwmb-file_advanced' ).each( initMediaField );
580 }
581
582 rwmb.$document
583 .on( 'mb_ready', init )
584 .on( 'clone', '.rwmb-file_advanced', initMediaField );
585 } )( jQuery, wp, _, rwmb, i18nRwmbMedia );
586