Updated to nearly fully functional. Pre-release 1. Still needs some UI changes in the slideshow and admin pages (move the save button & fix the save detection for the internship list). Customer had one more page change request which I need to re-define and handle.
This commit is contained in:
382
imports/ui/dialogs/SelectImageDialog.js
Normal file
382
imports/ui/dialogs/SelectImageDialog.js
Normal file
@@ -0,0 +1,382 @@
|
||||
import './SelectImageDialog.html';
|
||||
|
||||
Template.SelectImageDialog.onCreated(function() {
|
||||
this.originalImage = new ReactiveVar(undefined); //The original image. This is used to regenerate the displayed image after altering the rendering pipeline.
|
||||
this.currentWidth = new ReactiveVar(0); //The current width of the image. Must be > 0 and <= the original image width. The aspect ratio must be maintained (there is no reason to ever allow a change in aspect ratio).
|
||||
this.currentHeight = new ReactiveVar(0); //The current height of the image. Must be > 0 and <= the original image height. The aspect ratio must be maintained (there is no reason to ever allow a change in aspect ratio).
|
||||
this.currentCompression = new ReactiveVar(8); //On a range of 0..10 where 10 is maximum, and zero is none.
|
||||
this.lossyCompression = new ReactiveVar(true); //Boolean value indicating whether lossy compression should be utilized to reduce image sizes.
|
||||
this.compressedSize = new ReactiveVar(0); //The current display image's size once compressed (in base64 units). Note we could start saving the image outside the html (non-embeded), in which case the image size would be reduced by 1/4 roughly.
|
||||
this.previewCompression = new ReactiveVar(false); //Whether the viewed image includes the compression.
|
||||
this.convertedImage = undefined;
|
||||
});
|
||||
|
||||
Template.SelectImageDialog.onRendered(function() {
|
||||
let template = this;
|
||||
//TODO: Setup Jimp to edit the image in the canvas
|
||||
|
||||
let $canvas = template.$('.insertImageCanvas');
|
||||
let canvas = $canvas[0];
|
||||
let context = canvas.getContext('2d');
|
||||
|
||||
canvas.height = 300;
|
||||
canvas.width = 300;
|
||||
|
||||
template.readFile = function(file) {
|
||||
let reader = new FileReader();
|
||||
|
||||
reader.onload = function(e) {
|
||||
let image = document.createElement("img");
|
||||
|
||||
image.addEventListener("load", function() {
|
||||
//canvas.height = image.height;
|
||||
//canvas.width = image.width;
|
||||
//$canvas.css({width: image.width, height: image.height});
|
||||
template.currentHeight.set(image.height);
|
||||
template.currentWidth.set(image.width);
|
||||
//context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
//context.drawImage(image, 0, 0);
|
||||
$canvas.removeClass("empty");
|
||||
template.originalImage.set(image);
|
||||
template.rerender();
|
||||
}, false);
|
||||
|
||||
image.src = e.target.result;
|
||||
//var id = 'blobid' + (new Date()).getTime();
|
||||
//var blobCache = tinymce.activeEditor.editorUpload.blobCache;
|
||||
//var base64 = reader.result.split(',')[1];
|
||||
//var blobInfo = blobCache.create(id, file, base64);
|
||||
//blobCache.add(blobInfo);
|
||||
//
|
||||
//// call the callback and populate the Title field with the file name
|
||||
//cb(blobInfo.blobUri(), { title: file.name });
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
context.font = '10pt Arial, Sans Serif';
|
||||
context.fillStyle = 'red';
|
||||
context.textAlign = 'center';
|
||||
context.fillText("Drag & Drop", canvas.width / 2, canvas.height / 2 - 14);
|
||||
context.fillStyle = '#666';
|
||||
context.fillText("or", canvas.width / 2, canvas.height / 2);
|
||||
context.fillStyle = 'red';
|
||||
context.fillText("Click To Add Image", canvas.width / 2, canvas.height / 2 + 16);
|
||||
|
||||
template.changeWidth = function(delta) {
|
||||
let originalImage = template.originalImage.get();
|
||||
let ratio = originalImage.height / originalImage.width;
|
||||
let width = template.currentWidth.get() + delta;
|
||||
let height = 0;
|
||||
|
||||
if(width < 0) width = 1;
|
||||
else if(width > originalImage.width) width = originalImage.width;
|
||||
|
||||
height = Math.round(ratio * width);
|
||||
template.setSize(width, height <= 0 ? 1 : height);
|
||||
};
|
||||
template.changeHeight = function(delta) {
|
||||
let originalImage = template.originalImage.get();
|
||||
let ratio = originalImage.width / originalImage.height;
|
||||
let height = template.currentHeight.get() + delta;
|
||||
let width;
|
||||
|
||||
if(height < 0) height = 1;
|
||||
else if(height > originalImage.height) height = originalImage.height;
|
||||
|
||||
width = Math.round(ratio * height);
|
||||
template.setSize(width <= 0 ? 1 : width, height);
|
||||
};
|
||||
template.setSize = function(width, height) {
|
||||
template.currentWidth.set(width);
|
||||
template.currentHeight.set(height);
|
||||
template.rerender();
|
||||
};
|
||||
template.rerender = function() {
|
||||
let image = template.originalImage.get();
|
||||
|
||||
//Apply the width/height and other changes to the original image and render to the canvas.
|
||||
canvas.height = image.height;
|
||||
canvas.width = image.width;
|
||||
$canvas.css({width: image.width, height: image.height});
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.drawImage(image, 0, 0);
|
||||
|
||||
//Apply filters
|
||||
|
||||
//Resize
|
||||
if(template.currentWidth.get() !== image.width && template.currentHeight.get() !== image.height) {
|
||||
let width = template.currentWidth.get();
|
||||
let height = template.currentHeight.get();
|
||||
|
||||
template.resizeCanvas(canvas, width, height, true);
|
||||
$canvas.css({width: width, height: height});
|
||||
}
|
||||
|
||||
//If the user has requested that the display show the compression then get the compressed canvas contents and re-display them in the canvas such that the compression is shown.
|
||||
if(template.previewCompression.get()) {
|
||||
let image = new Image();
|
||||
|
||||
//Save the converted image such that we don't compress an already compressed image if the user saves the changes.
|
||||
template.convertedImage = getImageFromCanvas();
|
||||
//Use an image object to convert the compressed image into something we can render to the canvas.
|
||||
image.onload = function() {
|
||||
context.drawImage(image, 0, 0);
|
||||
};
|
||||
image.src = template.convertedImage;
|
||||
}
|
||||
else {
|
||||
template.convertedImage = undefined;
|
||||
}
|
||||
|
||||
template.collectStatistics();
|
||||
};
|
||||
function getImageFromCanvas() { //Gets the base64 string containing the image with selected compression and in the desired format.
|
||||
let compression = Math.abs(template.currentCompression.get() -10) / 10;
|
||||
let type = template.lossyCompression.get() ? "image/jpeg" : "image/png";
|
||||
|
||||
return canvas.toDataURL(type, compression);
|
||||
}
|
||||
template.saveImage = function() {
|
||||
let dataURL = template.convertedImage ? template.convertedImage : getImageFromCanvas();
|
||||
|
||||
let data = Template.currentData();
|
||||
|
||||
if(data && data.onApply && typeof data.onApply === 'function') {
|
||||
data.onApply(dataURL);
|
||||
}
|
||||
};
|
||||
template.collectStatistics = function() {
|
||||
let imageData = getImageFromCanvas();
|
||||
|
||||
//Save the compressed base64 size. If we were to allow the image to be saved outside the html (non-embedded) then we should multiply this by 0.75 for an estimate of a binary format.
|
||||
//Convert to kilo bytes and ensure the value is at least 1kb (it would be weird to have zero kb files).
|
||||
template.compressedSize.set(Math.max(1, Math.round(imageData.length / 1000)) + "kb");
|
||||
};
|
||||
template.resizeCanvas = function(canvas, width, height, resizeCanvas) {
|
||||
let width_source = canvas.width;
|
||||
let height_source = canvas.height;
|
||||
width = Math.round(width);
|
||||
height = Math.round(height);
|
||||
|
||||
let ratio_w = width_source / width;
|
||||
let ratio_h = height_source / height;
|
||||
let ratio_w_half = Math.ceil(ratio_w / 2);
|
||||
let ratio_h_half = Math.ceil(ratio_h / 2);
|
||||
|
||||
let ctx = canvas.getContext("2d");
|
||||
let img = ctx.getImageData(0, 0, width_source, height_source);
|
||||
let img2 = ctx.createImageData(width, height);
|
||||
let data = img.data;
|
||||
let data2 = img2.data;
|
||||
|
||||
for(let j = 0; j < height; j++) {
|
||||
for(let i = 0; i < width; i++) {
|
||||
let x2 = (i + j * width) * 4;
|
||||
let weight = 0;
|
||||
let weights = 0;
|
||||
let weights_alpha = 0;
|
||||
let gx_r = 0;
|
||||
let gx_g = 0;
|
||||
let gx_b = 0;
|
||||
let gx_a = 0;
|
||||
let center_y = (j + 0.5) * ratio_h;
|
||||
let yy_start = Math.floor(j * ratio_h);
|
||||
let yy_stop = Math.ceil((j + 1) * ratio_h);
|
||||
|
||||
for (let yy = yy_start; yy < yy_stop; yy++) {
|
||||
let dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
|
||||
let center_x = (i + 0.5) * ratio_w;
|
||||
let w0 = dy * dy; //pre-calc part of w
|
||||
let xx_start = Math.floor(i * ratio_w);
|
||||
let xx_stop = Math.ceil((i + 1) * ratio_w);
|
||||
for (let xx = xx_start; xx < xx_stop; xx++) {
|
||||
let dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
|
||||
let w = Math.sqrt(w0 + dx * dx);
|
||||
if (w >= 1) {
|
||||
//pixel too far
|
||||
continue;
|
||||
}
|
||||
//hermite filter
|
||||
weight = 2 * w * w * w - 3 * w * w + 1;
|
||||
let pos_x = 4 * (xx + yy * width_source);
|
||||
//alpha
|
||||
gx_a += weight * data[pos_x + 3];
|
||||
weights_alpha += weight;
|
||||
//colors
|
||||
if (data[pos_x + 3] < 255)
|
||||
weight = weight * data[pos_x + 3] / 250;
|
||||
gx_r += weight * data[pos_x];
|
||||
gx_g += weight * data[pos_x + 1];
|
||||
gx_b += weight * data[pos_x + 2];
|
||||
weights += weight;
|
||||
}
|
||||
}
|
||||
data2[x2] = gx_r / weights;
|
||||
data2[x2 + 1] = gx_g / weights;
|
||||
data2[x2 + 2] = gx_b / weights;
|
||||
data2[x2 + 3] = gx_a / weights_alpha;
|
||||
}
|
||||
}
|
||||
//clear and resize canvas
|
||||
if(resizeCanvas) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
} else {
|
||||
ctx.clearRect(0, 0, width_source, height_source);
|
||||
}
|
||||
|
||||
//draw
|
||||
ctx.putImageData(img2, 0, 0);
|
||||
};
|
||||
});
|
||||
|
||||
Template.SelectImageDialog.events({
|
||||
'click .close': function(event, template) {
|
||||
let data = Template.currentData();
|
||||
|
||||
if(data && data.onClose && typeof data.onClose === 'function') {
|
||||
data.onClose();
|
||||
}
|
||||
},
|
||||
'mouseup .insertImageCanvas': function(event, template) {
|
||||
let input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
input.setAttribute('accept', 'image/*');
|
||||
|
||||
input.onchange = function() {
|
||||
template.readFile(this.files[0]);
|
||||
};
|
||||
|
||||
input.click();
|
||||
},
|
||||
'click .select': function(event, template) {
|
||||
template.saveImage();
|
||||
},
|
||||
'focusout .width': function(event, template) {
|
||||
let width = parseInt(event.target.value);
|
||||
|
||||
if(width !== template.currentWidth.get()) template.changeWidth(event.target.value - template.currentWidth.get());
|
||||
},
|
||||
'keypress .width': function(event, template) {
|
||||
if(event.which === 13) {
|
||||
let width = parseInt(event.target.value);
|
||||
|
||||
if(width !== template.currentWidth.get()) template.changeWidth(event.target.value - template.currentWidth.get());
|
||||
}
|
||||
},
|
||||
'click .widthLess': function(event, template) {
|
||||
template.changeWidth(-1);
|
||||
},
|
||||
'click .widthMore': function(event, template) {
|
||||
template.changeWidth(1);
|
||||
},
|
||||
'focusout .height': function(event, template) {
|
||||
let height = parseInt(event.target.value);
|
||||
|
||||
if(height !== template.currentHeight.get()) template.changeHeight(height - template.currentHeight.get());
|
||||
},
|
||||
'keypress .height': function(event, template) {
|
||||
if(event.which === 13) {
|
||||
let height = parseInt(event.target.value);
|
||||
|
||||
if(height !== template.currentHeight.get()) template.changeHeight(height - template.currentHeight.get());
|
||||
}
|
||||
},
|
||||
'click .heightLess': function(event, template) {
|
||||
template.changeHeight(-1);
|
||||
},
|
||||
'click .heightMore': function(event, template) {
|
||||
template.changeHeight(1);
|
||||
},
|
||||
'dragover .insertImageCanvas': function(event, template) {
|
||||
event.preventDefault();
|
||||
},
|
||||
'drop .insertImageCanvas': function(event, template) {
|
||||
event.preventDefault();
|
||||
let files = event.originalEvent.dataTransfer.files;
|
||||
|
||||
if(files.length > 0) {
|
||||
let file = files[0];
|
||||
|
||||
if(typeof FileReader !== 'undefined' && file.type.indexOf("image") !== -1) {
|
||||
template.readFile(file);
|
||||
}
|
||||
}
|
||||
},
|
||||
'focusout .compression': function(event, template) {
|
||||
let compression = parseInt(event.target.value);
|
||||
|
||||
if(compression !== template.currentCompression.get()) {
|
||||
template.currentCompression.set(compression >= 0 ? (compression <= 10 ? compression : 10) : 0);
|
||||
template.collectStatistics();
|
||||
}
|
||||
},
|
||||
'keypress .compression': function(event, template) {
|
||||
if(event.which === 13) {
|
||||
let compression = parseInt(event.target.value);
|
||||
|
||||
if(compression !== template.currentCompress.get()) {
|
||||
template.currentCompression.set(compression >= 0 ? (compression <= 10 ? compression : 10) : 0);
|
||||
|
||||
if(template.previewCompression.get()) template.rerender();
|
||||
else template.collectStatistics();
|
||||
}
|
||||
}
|
||||
},
|
||||
'click .compressionLess': function(event, template) {
|
||||
let compression = template.currentCompression.get() - 1;
|
||||
template.currentCompression.set(compression >= 0 ? compression : 0);
|
||||
|
||||
if(template.previewCompression.get()) template.rerender();
|
||||
else template.collectStatistics();
|
||||
},
|
||||
'click .compressionMore': function(event, template) {
|
||||
let compression = template.currentCompression.get() + 1;
|
||||
template.currentCompression.set(compression <= 10 ? compression : 10);
|
||||
|
||||
if(template.previewCompression.get()) template.rerender();
|
||||
else template.collectStatistics();
|
||||
|
||||
},
|
||||
'change .lossyCompression': function(event, template) {
|
||||
template.lossyCompression.set(event.target.checked);
|
||||
template.collectStatistics();
|
||||
},
|
||||
'change .previewCompression': function(event, template) {
|
||||
template.previewCompression.set(event.target.checked);
|
||||
template.rerender();
|
||||
}
|
||||
});
|
||||
|
||||
Template.SelectImageDialog.helpers({
|
||||
image: function() {
|
||||
return Template.instance().originalImage.get();
|
||||
},
|
||||
originalWidth: function() {
|
||||
let image = Template.instance().originalImage.get();
|
||||
return image ? image.width : 0;
|
||||
},
|
||||
originalHeight: function() {
|
||||
let image = Template.instance().originalImage.get();
|
||||
return image ? image.height : 0;
|
||||
},
|
||||
currentWidth: function() {
|
||||
return Template.instance().currentWidth.get();
|
||||
},
|
||||
currentHeight: function() {
|
||||
return Template.instance().currentHeight.get();
|
||||
},
|
||||
currentCompression() {
|
||||
return Template.instance().currentCompression.get();
|
||||
},
|
||||
lossyCompression() {
|
||||
return Template.instance().lossyCompression.get();
|
||||
},
|
||||
compressedSize() {
|
||||
return Template.instance().compressedSize.get();
|
||||
},
|
||||
previewCompression() {
|
||||
return Template.instance().previewCompression.get();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user