PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.2.8
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.2.8
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / assets / js / imagify-gulp.min.js

imagify-gulp.min.js in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.2.8, at assets/js/imagify-gulp.min.js

397 lines 9.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Library that handles the bulk optimization processes.
3 *
4 * @requires jQuery
5 */
6 window.imagify = window.imagify || {};
7
8 /* eslint-disable no-underscore-dangle, consistent-this */
9 (function($, d, w) {
10
11 /**
12 * Construct the optimizer.
13 *
14 * @param {object} settings {
15 * Optimizer settings:
16 *
17 * @type {string} groupID Group ID, like 'library' or 'custom-folders'.
18 * @type {string} context Context within this group, like 'wp' or 'custom-folders' (yes, again).
19 * @type {int} level Optimization level: 0 to 2.
20 * @type {int} bufferSize Number of parallel optimizations: usually 4.
21 * @type {string} ajaxUrl URL to request to optimize.
22 * @type {object} files Files to optimize: media ID as key (prefixed with an underscore), file URL as value.
23 * @type {string} defaultThumb A default thumbnail URL.
24 * @type {string} doneEvent Name of the event to listen to know when optimizations end.
25 * @type {array} imageExtensions A list of supported image extensions (only images).
26 * }
27 */
28 w.imagify.Optimizer = function ( settings ) {
29 // Settings.
30 this.groupID = settings.groupID;
31 this.context = settings.context;
32 this.level = settings.level;
33 this.bufferSize = settings.bufferSize || 1;
34 this.ajaxUrl = settings.ajaxUrl;
35 this.files = settings.files;
36 this.defaultThumb = settings.defaultThumb;
37 this.doneEvent = settings.doneEvent;
38
39 if ( settings.imageExtensions ) {
40 this.imageExtensions = settings.imageExtensions;
41 } else {
42 this.imageExtensions = [ 'jpg', 'jpeg', 'jpe', 'png', 'gif' ];
43 }
44
45 /**
46 * An array of media IDs (prefixed with an underscore).
47 */
48 this.prefixedMediaIDs = Object.keys( this.files );
49 /**
50 * An array of medias currently being optimized: {
51 * @type {int} mediaID The media ID.
52 * @type {string} filename The file name.
53 * @type {string} thumbnail The file thumbnail URL.
54 * }
55 */
56 this.currentItems = [];
57
58 // Internal counters.
59 this.totalMedia = this.prefixedMediaIDs.length;
60 this.processedMedia = 0;
61
62 // Global stats.
63 this.globalOriginalSize = 0;
64 this.globalOptimizedSize = 0;
65 this.globalGain = 0;
66 this.globalPercent = 0;
67
68 // Callbacks.
69 this._before = function () {};
70 this._each = function () {};
71 this._done = function () {};
72
73 // Listen to the "optimization done" event.
74 if ( this.totalMedia && this.doneEvent ) {
75 $( w ).on( this.doneEvent, { _this: this }, this.processedCallback );
76 }
77 };
78
79 /**
80 * Callback to trigger before each media optimization.
81 *
82 * @param {callable} fnc A callback.
83 * @return this
84 */
85 w.imagify.Optimizer.prototype.before = function( fnc ) {
86 this._before = fnc;
87 return this;
88 };
89
90 /**
91 * Callback to trigger after each media optimization.
92 *
93 * @param {callable} fnc A callback.
94 * @return this
95 */
96 w.imagify.Optimizer.prototype.each = function( fnc ) {
97 this._each = fnc;
98 return this;
99 };
100
101 /**
102 * Callback to trigger all media optimizations have been done.
103 *
104 * @param {callable} fnc A callback.
105 * @return this
106 */
107 w.imagify.Optimizer.prototype.done = function( fnc ) {
108 this._done = fnc;
109 return this;
110 };
111
112 /**
113 * Launch optimizations.
114 *
115 * @return this
116 */
117 w.imagify.Optimizer.prototype.run = function() {
118 var chunkLength = this.prefixedMediaIDs.length > this.bufferSize ? this.bufferSize : this.prefixedMediaIDs.length,
119 i;
120
121 for ( i = 0; i < chunkLength; i++ ) {
122 this.processNext();
123 }
124
125 return this;
126 };
127
128 /**
129 * Launch next optimization.
130 *
131 * @return this
132 */
133 w.imagify.Optimizer.prototype.processNext = function() {
134 if ( this.prefixedMediaIDs.length ) {
135 this.process( this.prefixedMediaIDs.shift() );
136 }
137
138 return this;
139 };
140
141 /**
142 * Launch an optimization.
143 *
144 * @param {string} prefixedId A media ID, prefixed with an underscore.
145 * @return this
146 */
147 w.imagify.Optimizer.prototype.process = function( prefixedId ) {
148 var _this = this,
149 fileURL = this.files[ prefixedId ],
150 data = {
151 mediaID: parseInt( prefixedId.toString().substr( 1 ), 10 ),
152 filename: this.files[ prefixedId ].split( '/' ).pop(),
153 thumbnail: this.defaultThumb
154 },
155 extension = data.filename.split( '.' ).pop().toLowerCase(),
156 regexp = new RegExp( '^' + this.imageExtensions.join( '|' ).toLowerCase() + '$' ),
157 image;
158
159 if ( ! extension.match( regexp ) ) {
160 // Not an image.
161 this.currentItems.push( data );
162 this._before( data );
163 this.send( data );
164 return this;
165 }
166
167 // Create a thumbnail and send the ajax request.
168 image = new Image();
169
170 image.onerror = function () {
171 _this.currentItems.push( data );
172 _this._before( data );
173 _this.send( data );
174 };
175
176 image.onload = function () {
177 var maxWidth = 33,
178 maxHeight = 33,
179 imageWidth = image.width,
180 imageHeight = image.height,
181 newHeight = 0,
182 newWidth = 0,
183 topOffset = 0,
184 leftOffset = 0,
185 canvas = null,
186 ctx = null;
187
188 if ( imageWidth < imageHeight ) {
189 // Portrait.
190 newWidth = maxWidth;
191 newHeight = newWidth * imageHeight / imageWidth;
192 topOffset = ( maxHeight - newHeight ) / 2;
193 } else {
194 // Landscape.
195 newHeight = maxHeight;
196 newWidth = newHeight * imageWidth / imageHeight;
197 leftOffset = ( maxWidth - newWidth ) / 2;
198 }
199
200 canvas = d.createElement( 'canvas' );
201
202 canvas.width = maxWidth;
203 canvas.height = maxHeight;
204
205 ctx = canvas.getContext( '2d' );
206 ctx.drawImage( this, leftOffset, topOffset, newWidth, newHeight );
207
208 try {
209 data.thumbnail = canvas.toDataURL( 'image/jpeg' );
210 } catch ( e ) {
211 data.thumbnail = _this.defaultThumb;
212 }
213
214 canvas = null;
215 ctx = null;
216 image = null;
217
218 _this.currentItems.push( data );
219 _this._before( data );
220 _this.send( data );
221 };
222
223 image.src = fileURL;
224
225 return this;
226 };
227
228 /**
229 * Do the ajax request.
230 *
231 * @param {object} data {
232 * The data:
233 *
234 * @type {int} mediaID The media ID.
235 * @type {string} filename The file name.
236 * @type {string} thumbnail The file thumbnail URL.
237 * }
238 * @return this
239 */
240 w.imagify.Optimizer.prototype.send = function( data ) {
241 var _this = this,
242 defaultResponse = {
243 success: false,
244 mediaID: data.mediaID,
245 groupID: this.groupID,
246 context: this.context,
247 filename: data.filename,
248 thumbnail: data.thumbnail,
249 status: 'error',
250 error: ''
251 };
252
253 $.post( {
254 url: this.ajaxUrl,
255 data: {
256 media_id: data.mediaID,
257 context: this.context,
258 optimization_level: this.level
259 },
260 dataType: 'json'
261 } )
262 .done( function( response ) {
263 if ( response.success ) {
264 return;
265 }
266
267 defaultResponse.error = response.data.error;
268
269 _this.processed( defaultResponse );
270 } )
271 .fail( function( jqXHR ) {
272 defaultResponse.error = jqXHR.statusText;
273
274 _this.processed( defaultResponse );
275 } );
276
277 return this;
278 };
279
280 /**
281 * Callback triggered when an optimization is complete.
282 *
283 * @param {object} e jQuery's Event object.
284 * @param {object} item {
285 * The response:
286 *
287 * @type {int} mediaID The media ID.
288 * @type {string} context The context.
289 * }
290 */
291 w.imagify.Optimizer.prototype.processedCallback = function( e, item ) {
292 var _this = e.data._this;
293
294 if ( item.context !== _this.context ) {
295 return;
296 }
297
298 if ( ! item.mediaID || typeof _this.files[ '_' + item.mediaID ] === 'undefined' ) {
299 return;
300 }
301
302 item.groupID = _this.groupID;
303
304 if ( ! _this.currentItems.length ) {
305 // Trouble.
306 _this.processed( item );
307 return;
308 }
309
310 $.each( _this.currentItems, function( i, mediaData ) {
311 if ( item.mediaID === mediaData.mediaID ) {
312 item.filename = mediaData.filename;
313 item.thumbnail = mediaData.thumbnail;
314 return false;
315 }
316 } );
317
318 _this.processed( item );
319 };
320
321 /**
322 * After a media has been processed.
323 *
324 * @param {object} response {
325 * The response:
326 *
327 * @type {bool} success Whether the optimization succeeded or not ("already optimized" is a success).
328 * @type {int} mediaID The media ID.
329 * @type {string} groupID The group ID.
330 * @type {string} context The context.
331 * @type {string} filename The file name.
332 * @type {string} thumbnail The file thumbnail URL.
333 * @type {string} status The status, like 'optimized', 'already-optimized', 'over-quota', 'error'.
334 * @type {string} error The error message.
335 * }
336 * @return this
337 */
338 w.imagify.Optimizer.prototype.processed = function( response ) {
339 var currentItems = this.currentItems;
340
341 if ( currentItems.length ) {
342 // Remove this media from the "current" list.
343 $.each( currentItems, function( i, mediaData ) {
344 if ( response.mediaID === mediaData.mediaID ) {
345 currentItems.splice( i, 1 );
346 return false;
347 }
348 } );
349
350 this.currentItems = currentItems;
351 }
352
353 // Update stats.
354 if ( response.success && 'already-optimized' !== response.status ) {
355 this.globalOriginalSize += response.originalOverallSize;
356 this.globalOptimizedSize += response.newOverallSize;
357 this.globalGain += response.overallSaving;
358 this.globalPercent = ( 100 - this.globalOptimizedSize / this.globalOptimizedSize * 100 ).toFixed( 2 );
359 }
360
361 ++this.processedMedia;
362 response.progress = Math.floor( this.processedMedia / this.totalMedia * 100 );
363
364 this._each( response );
365
366 if ( this.prefixedMediaIDs.length ) {
367 this.processNext();
368 } else if ( this.totalMedia === this.processedMedia ) {
369 this._done( {
370 globalOriginalSize: this.globalOriginalSize,
371 globalOptimizedSize: this.globalOptimizedSize,
372 globalGain: this.globalGain
373 } );
374 }
375
376 return this;
377 };
378
379 /**
380 * Stop the process.
381 *
382 * @return this
383 */
384 w.imagify.Optimizer.prototype.stopProcess = function() {
385 this.files = {};
386 this.prefixedMediaIDs = [];
387 this.currentItems = [];
388
389 if ( this.doneEvent ) {
390 $( w ).off( this.doneEvent, this.processedCallback );
391 }
392
393 return this;
394 };
395
396 } )(jQuery, document, window);
397