PluginProbe
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more / 2.1
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more v2.1
3.7.0 3.6.0 3.6.1 3.5.2 trunk 2.0 2.1 2.2 2.3 2.4 2.4.1 2.4.2 2.4.3 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.6.0 2.6.1 3.0.0 3.0.1 All 71 releases
stockpack / assets / js / stockpack-image.js

stockpack-image.js in StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more 2.1, at assets/js/stockpack-image.js

334 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 const StockpackImage = wp.media.View.extend( {
2 tagName: 'li',
3 className: 'attachment',
4 template: wp.template( 'stockpack-attachment' ),
5 events: {
6 click: 'toggleSelectionHandler',
7 keydown: 'toggleSelectionHandler',
8 'click .check': 'checkClickHandler',
9 'click .download': 'downloadClickHandler',
10 },
11 buttons: {
12 check: true,
13 },
14
15 maybeUpdateGlobalProvider() {
16 const provider = this.options.provider;
17 const currentProvider = this.model.get( 'provider' );
18 if ( provider !== currentProvider ) {
19 this.controller.trigger( 'update_provider', currentProvider );
20 }
21 },
22
23 initialize() {
24 const selection = this.options.selection;
25
26 this.listenTo( this.model, 'change', this.render );
27
28 // Update the selection.
29 this.listenTo( this.model, 'add', this.select );
30 this.listenTo( this.model, 'remove', this.deselect );
31 if ( selection ) {
32 selection.on( 'reset', this.updateSelect, this );
33 // Update the model's details view.
34 this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
35 this.details( this.model, this.controller.state().get( 'selection' ) );
36 }
37 this.maybeUpdateGlobalProvider();
38 },
39
40 /**
41 * @return {StockpackImage} Returns itself to allow chaining
42 */
43 dispose() {
44 const selection = this.options.selection;
45
46 if ( selection ) {
47 selection.off( null, null, this );
48 }
49 /**
50 * call 'dispose' directly on the parent class
51 */
52 wp.media.View.prototype.dispose.apply( this, arguments );
53 return this;
54 },
55 /**
56 * @return {StockpackImage} Returns itself to allow chaining
57 */
58 render() {
59 const options = _.defaults( this.model.toJSON(), {
60 orientation: 'landscape',
61 type: 'image',
62 subtype: '',
63 width: '',
64 height: '',
65 caption: '',
66 description: '',
67 }, this.options );
68
69 options.buttons = this.buttons;
70
71 if ( 'image' === options.type ) {
72 options.size = this.imageSize();
73 }
74
75 this.views.detach();
76 this.$el.html( this.template( options ) );
77
78 // Check if the model is selected.
79 this.updateSelect();
80
81 this.views.render();
82
83 return this;
84 },
85
86 /**
87 * @param {Object} event
88 */
89 toggleSelectionHandler( event ) {
90 let method;
91
92 // Don't do anything inside inputs and on the attachment check and remove buttons.
93 if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName || 'A' === event.target.nodeName ) {
94 return;
95 }
96
97 // Catch arrow events
98 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
99 this.controller.trigger( 'attachment:keydown:arrow', event );
100 return;
101 }
102
103 // Catch enter and space events
104 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
105 return;
106 }
107
108 event.preventDefault();
109
110 // In the grid view, bubble up an edit:attachment event to the controller.
111 if ( this.controller.isModeActive( 'grid' ) ) {
112 if ( this.controller.isModeActive( 'edit' ) ) {
113 // Pass the current target to restore focus when closing
114 this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
115 return;
116 }
117
118 if ( this.controller.isModeActive( 'select' ) ) {
119 method = 'toggle';
120 }
121 }
122
123 if ( event.shiftKey ) {
124 method = 'between';
125 } else if ( event.ctrlKey || event.metaKey ) {
126 method = 'toggle';
127 }
128
129 this.toggleSelection( {
130 method,
131 } );
132
133 this.controller.trigger( 'selection:toggle' );
134 },
135 /**
136 * @param {Object} options
137 */
138 toggleSelection( options ) {
139 let method = options && options.method,
140 models, singleIndex, modelIndex;
141
142 const selection = this.options.selection;
143 if ( ! selection ) {
144 return;
145 }
146 const collection = this.collection;
147 const model = this.model;
148 const single = selection.single();
149 method = _.isUndefined( method ) ? selection.multiple : method;
150
151 // If the `method` is set to `between`, select all models that
152 // exist between the current and the selected model.
153 if ( 'between' === method && single && selection.multiple ) {
154 // If the models are the same, short-circuit.
155 if ( single === model ) {
156 return;
157 }
158
159 singleIndex = collection.indexOf( single );
160 modelIndex = collection.indexOf( this.model );
161
162 if ( singleIndex < modelIndex ) {
163 models = collection.models.slice( singleIndex, modelIndex + 1 );
164 } else {
165 models = collection.models.slice( modelIndex, singleIndex + 1 );
166 }
167
168 selection.add( models );
169 selection.single( model );
170 return;
171
172 // If the `method` is set to `toggle`, just flip the selection
173 // status, regardless of whether the model is the single model.
174 } else if ( 'toggle' === method ) {
175 selection[ this.selected() ? 'remove' : 'add' ]( model );
176 selection.single( model );
177 return;
178 } else if ( 'add' === method ) {
179 selection.add( model );
180 selection.single( model );
181 return;
182 }
183
184 // Fixes bug that loses focus when selecting a featured image
185 if ( ! method ) {
186 method = 'add';
187 }
188
189 if ( method !== 'add' ) {
190 method = 'reset';
191 }
192
193 if ( this.selected() ) {
194 // If the model is the single model, remove it.
195 // If it is not the same as the single model,
196 // it now becomes the single model.
197 selection[ single === model ? 'remove' : 'single' ]( model );
198 } else {
199 // If the model is not selected, run the `method` on the
200 // selection. By default, we `reset` the selection, but the
201 // `method` can be set to `add` the model to the selection.
202 selection[ method ]( model );
203 selection.single( model );
204 }
205 },
206
207 updateSelect() {
208 this[ this.selected() ? 'select' : 'deselect' ]();
209 },
210 /**
211 * @return {boolean} selected
212 */
213 selected() {
214 const selection = this.options.selection;
215 if ( selection ) {
216 return !! selection.get( this.model.cid );
217 }
218 },
219 /**
220 * @param {Backbone.Model} model
221 * @param {Backbone.Collection} collection
222 */
223 select( model, collection ) {
224 const selection = this.options.selection,
225 controller = this.controller;
226
227 // Check if a selection exists and if it's the collection provided.
228 // If they're not the same collection, bail; we're in another
229 // selection's event loop.
230 if ( ! selection || ( collection && collection !== selection ) ) {
231 return;
232 }
233
234 // Bail if the model is already selected.
235 if ( this.$el.hasClass( 'selected' ) ) {
236 // call download function
237 return;
238 }
239
240 // Add 'selected' class to model, set aria-checked to true.
241 this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
242 // Make the checkbox tabable, except in media grid (bulk select mode).
243 if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
244 this.$( '.check' ).attr( 'tabindex', '0' );
245 }
246 },
247 /**
248 * @param {Backbone.Model} model
249 * @param {Backbone.Collection} collection
250 */
251 deselect( model, collection ) {
252 const selection = this.options.selection;
253
254 // Check if a selection exists and if it's the collection provided.
255 // If they're not the same collection, bail; we're in another
256 // selection's event loop.
257 if ( ! selection || ( collection && collection !== selection ) ) {
258 return;
259 }
260 this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
261 .find( '.check' ).attr( 'tabindex', '-1' );
262 },
263 /**
264 * @param {Backbone.Model} model
265 * @param {Backbone.Collection} collection
266 */
267 details( model, collection ) {
268 const selection = this.options.selection;
269
270 if ( selection !== collection ) {
271 return;
272 }
273
274 const details = selection.single();
275 this.$el.toggleClass( 'details', details === this.model );
276 },
277 /**
278 * @param {string} size
279 * @return {Object} sizes
280 */
281 imageSize( size ) {
282 const sizes = this.model.get( 'sizes' );
283 let matched = false;
284
285 size = size || 'medium';
286
287 // Use the provided image size if possible.
288 if ( sizes ) {
289 if ( sizes[ size ] ) {
290 matched = sizes[ size ];
291 } else if ( sizes.large ) {
292 matched = sizes.large;
293 } else if ( sizes.thumbnail ) {
294 matched = sizes.thumbnail;
295 } else if ( sizes.full ) {
296 matched = sizes.full;
297 }
298
299 if ( matched ) {
300 return _.clone( matched );
301 }
302 }
303
304 return {
305 url: this.model.get( 'url' ),
306 width: this.model.get( 'width' ),
307 height: this.model.get( 'height' ),
308 orientation: this.model.get( 'orientation' ),
309 };
310 },
311
312 checkClickHandler( event ) {
313 const selection = this.options.selection;
314 if ( ! selection ) {
315 return;
316 }
317 event.stopPropagation();
318 if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
319 selection.remove( this.model );
320 // Move focus back to the attachment tile (from the check).
321 this.$el.focus();
322 } else {
323 selection.add( this.model );
324 }
325 },
326
327 downloadClickHandler( event ) {
328 this.controller.trigger( 'download-file', this.model, event.currentTarget );
329 },
330
331 } );
332
333 export default StockpackImage;
334