/* exported ImageResize */
/** ImageResize Class used to handle the resizing */
class ImageResize{
/**
* Creates an instance of ImageResize
* and checks for invalid parameters
* @param {Object} [parameters] Parameters holder
* @param {String|Element} [parameters.source] Input to automatically resize
* @param {Boolean} [parameters.keepAspectRatio=true] Keep the aspect ratio ?
* @param {Number} [parameters.maxWidth] Image max width
* @param {Number} [parameters.maxHeight] Image max height
* @param {Number} [parameters.width] Image fixed width
* @param {Number} [parameters.height] Image fixed width
* @param {Function} [parameters.onResize] Resize callback
*/
constructor(parameters = {}){
/**
* Parameters holder
* @type {Object}
* @private
*/
this._parameters = {
source: null,
keepAspectRatio: true,
maxWidth: parameters ? null : 50,
maxHeight: parameters ? null : 50,
width: null,
height: null,
...parameters,
onResize: parameters.onResize ? [parameters.onResize] : []
};
// Used on an input
if(this._parameters.source){
/**
* Input holder
* @type {HTMLInputElement}
* @private
*/
this._input = this._parameters.source instanceof Element ? this._parameters.source : document.querySelector(this._parameters.source);
// Invalid element
if(this._input){
// Not an INPUT
if(this._input.nodeName == 'INPUT'){
// Already initialized
if(this._input.classList.contains('slick-complete-input')){
console.warn('ImageResize: The element has already been initialized.');
}else{
this._build();
this._listen();
}
}else{
console.warn('ImageResize: '+(typeof target == 'string' ? 'The selector `'+this._parameters.source+'` didn\'t match any input.' : 'The element you provided isn\'t an input.'));
}
}else{
console.warn('ImageResize: '+(typeof target == 'string' ? 'The selector `'+this._parameters.source+'` didn\'t match any element.' : 'The element you provided was undefined'));
}
// Used as an empty image processor
}else{
this._build();
this._listen();
}
}
/**
* Builds the ImageResize utilities
* @private
*/
_build(){
/**
* Canvas used to resize
* @type {HTMLCanvasElement}
* @private
*/
this._canvas = document.createElement('canvas');
}
/**
* Creates event listeners
* @private
*/
_listen(){
// Input change
if(this._input){
this._input.addEventListener('change', () => {
const [image] = this._input.files;
if(image.type.match(/image.*/)){
this._process(image).then(resizedImage => {
// Call onResize callbacks
for(const callback of this._parameters.onResize) Reflect.apply(callback, null, [resizedImage]);
});
}else{
console.warn('ImageResize: You tried to resize a non-image file.');
}
});
}
}
/**
* Process the image
* @param {File} file The image file
* @returns {Promise} Resolved when the image is processed
* @private
*/
_process(file){
return new Promise(resolve => {
const reader = new FileReader();
// Reader load handler
reader.addEventListener('load', event => {
// Special case for SVGs
if(file.name.endsWith('.svg')){
// Load the SVG before sending it back
const unalteredSVG = new Image();
unalteredSVG.addEventListener('load', () => {
resolve(unalteredSVG);
});
unalteredSVG.src = reader.result;
}else{
const image = new Image();
// Image load handler
image.addEventListener('load', () => {
let {width, height} = image;
const ratio = width / height;
// Scale width down to maxWidth
if(this._parameters.maxWidth !== null && width > this._parameters.maxWidth){
width = this._parameters.maxWidth;
// Update height if keepAspectRatio
if(this._parameters.keepAspectRatio) height = width / ratio;
}
// Scale height down to maxHeight
if(this._parameters.maxHeight !== null && height > this._parameters.maxHeight){
height = this._parameters.maxHeight;
// Update width if keepAspectRatio
if(this._parameters.keepAspectRatio) width = ratio * height;
}
// Scale width down to fixed width
if(this._parameters.width !== null && width > this._parameters.width){
// eslint-disable-next-line prefer-destructuring
width = this._parameters.width;
// Update height if keepAspectRatio
if(this._parameters.keepAspectRatio) height = width / ratio;
}
// Scale height down to fixed height
if(this._parameters.height !== null && height > this._parameters.height){
// eslint-disable-next-line prefer-destructuring
height = this._parameters.height;
// Update width if keepAspectRatio
if(this._parameters.keepAspectRatio) width = ratio * height;
}
// Scale the canvas accordingly
this._canvas.width = width;
this._canvas.height = height;
// Reset the canvas
this._canvas.getContext('2d').clearRect(0, 0, this._canvas.width, this._canvas.height);
// Draw the new image
this._canvas.getContext('2d').drawImage(image, 0, 0, width, height);
// Get the processed image as dataUrl
const dataUrl = this._canvas.toDataURL('image/' + file.name.split('.').pop());
// Load the new image before sending it back
const resizedImage = new Image();
resizedImage.addEventListener('load', () => {
resolve(resizedImage);
});
resizedImage.src = dataUrl;
});
// Trigger the image load
image.src = event.target.result;
}
});
// Trigger the reader load
reader.readAsDataURL(file);
});
}
/**
* Manually process an image
* @param {String|File} file The image SRC or the image File itself
* @returns {Promise} Resolved with the new Image
*/
process(file){
return new Promise(resolve => {
let image = null;
new Promise(solve => {
if(file instanceof File){
image = file;
resolve();
}else{
fetch(file)
.then(res => res.blob())
.then(blob => {
blob.lastModifiedDate = new Date();
blob.name = 'resizedImage' + blob.type.split('/').pop();
image = blob;
solve();
});
}
}).then(() => {
this._process(image).then(resizedImage => {
resolve(resizedImage);
});
});
});
}
/**
* Set the resized images max-width
* @param {Number} value
* @returns {ImageResize} The current {@link ImageResize}
*/
maxWidth(value){
this._parameters.maxWidth = value;
return this;
}
/**
* Set the resized images max-height
* @param {Number} value
* @returns {ImageResize} The current {@link ImageResize}
*/
maxheight(value){
this._parameters.maxheight = value;
return this;
}
/**
* Set the resized images width
* @param {Number} value
* @returns {ImageResize} The current {@link ImageResize}
*/
width(value){
this._parameters.width = value;
return this;
}
/**
* Set the resized images height
* @param {Number} value
* @returns {ImageResize} The current {@link ImageResize}
*/
height(value){
this._parameters.height = value;
return this;
}
/**
* Sets wether the resized images should keep their initial aspect ratio
* @param {Boolean} value
* @returns {ImageResize} The current {@link ImageResize}
*/
keepAspectRatio(value){
this._parameters.keepAspectRatio = value;
return this;
}
}