| Server IP : 54.36.91.62 / Your IP : 216.73.217.94 Web Server : Apache System : Linux webm013.cluster127.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 User : coopiak ( 151928) PHP Version : 8.3.23 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/coopiak/amisdesseniors-fr/media/plg_system_jcepro/editor/js/editor/ |
Upload File : |
(function () {
'use strict';
/* eslint-disable */
/**
* Color Matrix
*
* A simplification of the color matrix class provided by
* EaselJS
*
* www.createjs.com/docs/easeljs/files/easeljs_filters_ColorFilter.js.html#l41
* https://github.com/vigetlabs/canvas-instagram-filters/blob/gh-pages/lib/color-matrix.js
*/
var DELTA_INDEX = [
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
10.0
];
function multiply(a, b) {
var i, j, k, col = [];
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
col[j] = a[j + i * 5];
}
for (j = 0; j < 5; j++) {
var val = 0;
for (k = 0; k < 5; k++) {
val += b[j + k * 5] * col[k];
}
a[j + i * 5] = val;
}
}
return a;
}
var identity = [
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
];
function adjustValue(value, limit) {
return Math.min(limit, Math.max(-limit, value));
}
var ColorMatrix = function (data) {
this.data = data;
};
ColorMatrix.prototype = {
brightness: function (value) {
value = adjustValue(value, 255);
return this.apply(multiply(identity, [
1, 0, 0, 0, value,
0, 1, 0, 0, value,
0, 0, 1, 0, value,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
]));
},
contrast: function (value) {
value = adjustValue(value, 100);
var x;
if (value < 0) {
x = 127 + value / 100 * 127;
} else {
x = value % 1;
if (x == 0) {
x = DELTA_INDEX[value];
} else {
x = DELTA_INDEX[(value << 0)] * (1 - x) + DELTA_INDEX[(value << 0) + 1] * x; // use linear interpolation for more granularity.
}
x = x * 127 + 127;
}
return this.apply(multiply(identity, [
x / 127, 0, 0, 0, 0.5 * (127 - x),
0, x / 127, 0, 0, 0.5 * (127 - x),
0, 0, x / 127, 0, 0.5 * (127 - x),
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
]));
},
saturate: function (value) {
value = adjustValue(value, 100);
var x = 1 + ((value > 0) ? 3 * value / 100 : value / 100);
var lumR = 0.3086;
var lumG = 0.6094;
var lumB = 0.0820;
return this.apply(multiply(identity, [
lumR * (1 - x) + x, lumG * (1 - x), lumB * (1 - x), 0, 0,
lumR * (1 - x), lumG * (1 - x) + x, lumB * (1 - x), 0, 0,
lumR * (1 - x), lumG * (1 - x), lumB * (1 - x) + x, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
]));
},
hue: function (value) {
value = adjustValue(value, 180) / 180 * Math.PI;
var cosVal = Math.cos(value);
var sinVal = Math.sin(value);
var lumR = 0.213;
var lumG = 0.715;
var lumB = 0.072;
return this.apply(multiply(identity, [
lumR + cosVal * (1 - lumR) + sinVal * (-lumR), lumG + cosVal * (-lumG) + sinVal * (-lumG), lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (0.143), lumG + cosVal * (1 - lumG) + sinVal * (0.140), lumB + cosVal * (-lumB) + sinVal * (-0.283), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), lumG + cosVal * (-lumG) + sinVal * (lumG), lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
]));
},
polaroid: function () {
return this.apply(multiply(identity, [
1.438, -0.062, -0.062, 0, 0, -0.122, 1.378, -0.122, 0, 0, -0.016, -0.016, 1.483, 0, 0,
0, 0, 0, 1, 0
]));
},
filter: function (name, value) {
var fn = this[name];
if (!fn) {
return this;
}
return fn.apply(this, [value]);
},
apply: function (matrix) {
// Apply Filter
var data = this.data;
var l = data.length;
var r, g, b, a;
var m0 = matrix[0],
m1 = matrix[1],
m2 = matrix[2],
m3 = matrix[3],
m4 = matrix[4];
var m5 = matrix[5],
m6 = matrix[6],
m7 = matrix[7],
m8 = matrix[8],
m9 = matrix[9];
var m10 = matrix[10],
m11 = matrix[11],
m12 = matrix[12],
m13 = matrix[13],
m14 = matrix[14];
var m15 = matrix[15],
m16 = matrix[16],
m17 = matrix[17],
m18 = matrix[18],
m19 = matrix[19];
for (var i = 0; i < l; i += 4) {
r = data[i];
g = data[i + 1];
b = data[i + 2];
a = data[i + 3];
data[i] = r * m0 + g * m1 + b * m2 + a * m3 + m4; // red
data[i + 1] = r * m5 + g * m6 + b * m7 + a * m8 + m9; // green
data[i + 2] = r * m10 + g * m11 + b * m12 + a * m13 + m14; // blue
data[i + 3] = r * m15 + g * m16 + b * m17 + a * m18 + m19; // alpha
}
return this;
}
};
const collect = (source, prefix, collection) => {
const r = new RegExp(`\\b${prefix} \\w+ (\\w+)`, 'ig');
source.replace(r, (match, name) => {
collection[name] = 0;
return match;
});
};
const compile = (gl, source, type) => {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(shader));
return null;
}
return shader;
};
const webGLProgram = function (gl, vertexSource, fragmentSource) {
const uniform = {};
const attribute = {};
const vsh = compile(gl, vertexSource, gl.VERTEX_SHADER);
const fsh = compile(gl, fragmentSource, gl.FRAGMENT_SHADER);
const id = gl.createProgram();
gl.attachShader(id, vsh);
gl.attachShader(id, fsh);
gl.linkProgram(id);
if (!gl.getProgramParameter(id, gl.LINK_STATUS)) {
console.log(gl.getProgramInfoLog(id));
}
gl.useProgram(id);
// Collect attributes
collect(vertexSource, 'attribute', attribute);
for (const a in attribute) {
attribute[a] = gl.getAttribLocation(id, a);
}
// Collect uniforms
collect(vertexSource, 'uniform', uniform);
collect(fragmentSource, 'uniform', uniform);
for (const u in uniform) {
uniform[u] = gl.getUniformLocation(id, u);
}
return {
attribute,
id,
uniform
};
};
// Color Matrix Filter.
const WITH_ALPHA = [
'precision highp float;',
'varying vec2 vUv;',
'uniform sampler2D texture;',
'uniform float m[20];',
'void main(void) {',
'vec4 c = texture2D(texture, vUv);',
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[3] * c.a + m[4];',
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[8] * c.a + m[9];',
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[13] * c.a + m[14];',
'gl_FragColor.a = m[15] * c.r + m[16] * c.g + m[17] * c.b + m[18] * c.a + m[19];',
'}'
].join('\n');
const WITHOUT_ALPHA = [
'precision highp float;',
'varying vec2 vUv;',
'uniform sampler2D texture;',
'uniform float m[20];',
'void main(void) {',
'vec4 c = texture2D(texture, vUv);',
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[4];',
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[9];',
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[14];',
'gl_FragColor.a = c.a;',
'}'
].join('\n');
const colorMatrix = (compileShader, gl, draw) => matrix => {
// Create a Float32 Array and normalize the offset component to 0-1
const m = new Float32Array(matrix);
m[4] /= 255;
m[9] /= 255;
m[14] /= 255;
m[19] /= 255;
// Can we ignore the alpha value? Makes things a bit faster.
const shader = (1 == m[18] && 0 == m[3] && 0 == m[8] && 0 == m[13] && 0 == m[15] && 0 == m[16] && 0 == m[17] && 0 == m[19]) ? WITHOUT_ALPHA : WITH_ALPHA;
const program = compileShader(shader);
gl.uniform1fv(program.uniform.m, m);
draw();
};
const LUM_R = 0.213;
const LUM_G = 0.715;
const LUM_B = 0.072;
var standard = (compileShader, gl, draw) => {
const colorMatrix$1 = colorMatrix(compileShader, gl, draw);
const brightness = (w, h, brightness = 0) => {
const b = brightness + 1;
colorMatrix$1([
b, 0, 0, 0, 0,
0, b, 0, 0, 0,
0, 0, b, 0, 0,
0, 0, 0, 1, 0
]);
};
const brownie = () => {
colorMatrix$1([
0.5997023498159715, 0.34553243048391263, -0.2708298674538042, 0, 47.43192855600873,
-0.037703249837783157, 0.8609577587992641, 0.15059552388459913, 0, -36.96841498319127,
0.24113635128153335, -0.07441037908422492, 0.44972182064877153, 0, -7.562075277591283,
0, 0, 0, 1, 0
]);
};
const contrast = (w, h, amount = 0) => {
const v = amount + 1;
const o = -128 * (v - 1);
colorMatrix$1([
v, 0, 0, 0, o,
0, v, 0, 0, o,
0, 0, v, 0, o,
0, 0, 0, 1, 0
]);
};
const desaturateLuminance = () => {
colorMatrix$1([
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
0, 0, 0, 1, 0
]);
};
const hue = (w, h, degrees = 0) => {
const rotation = degrees / 180 * Math.PI;
const cos = Math.cos(rotation);
const sin = Math.sin(rotation);
colorMatrix$1([
LUM_R + cos * (1 - LUM_R) + sin * (-LUM_R), LUM_G + cos * (-LUM_G) + sin * (-LUM_G), LUM_B + cos * (-LUM_B) + sin * (1 - LUM_B), 0, 0,
LUM_R + cos * (-LUM_R) + sin * (0.143), LUM_G + cos * (1 - LUM_G) + sin * (0.140), LUM_B + cos * (-LUM_B) + sin * (-0.283), 0, 0,
LUM_R + cos * (-LUM_R) + sin * (-(1 - LUM_R)), LUM_G + cos * (-LUM_G) + sin * (LUM_G), LUM_B + cos * (1 - LUM_B) + sin * (LUM_B), 0, 0,
0, 0, 0, 1, 0
]);
};
const kodachrome = () => {
colorMatrix$1([
1.1285582396593525, -0.3967382283601348, -0.03992559172921793, 0, 63.72958762196502,
-0.16404339962244616, 1.0835251566291304, -0.05498805115633132, 0, 24.732407896706203,
-0.16786010706155763, -0.5603416277695248, 1.6014850761964943, 0, 35.62982807460946,
0, 0, 0, 1, 0
]);
};
const polaroid = () => {
colorMatrix$1([
1.438, -0.062, -0.062, 0, 0,
-0.122, 1.378, -0.122, 0, 0,
-0.016, -0.016, 1.483, 0, 0,
0, 0, 0, 1, 0
]);
};
const negative = (w, h) => {
contrast(w, h, -2);
};
const saturation = (w, h, amount = 0) => {
const x = amount * 2 / 3 + 1;
const y = ((x - 1) * -0.5);
colorMatrix$1([
x, y, y, 0, 0,
y, x, y, 0, 0,
y, y, x, 0, 0,
0, 0, 0, 1, 0
]);
};
const desaturate = (w, h) => {
saturation(w, h, -1);
};
const sepia = () => {
colorMatrix$1([
0.393, 0.7689999, 0.18899999, 0, 0,
0.349, 0.6859999, 0.16799999, 0, 0,
0.272, 0.5339999, 0.13099999, 0, 0,
0, 0, 0, 1, 0
]);
};
const shiftToBGR = () => {
colorMatrix$1([
0, 0, 1, 0, 0,
0, 1, 0, 0, 0,
1, 0, 0, 0, 0,
0, 0, 0, 1, 0
]);
};
const technicolor = () => {
colorMatrix$1([
1.9125277891456083, -0.8545344976951645, -0.09155508482755585, 0, 11.793603434377337,
-0.3087833385928097, 1.7658908555458428, -0.10601743074722245, 0, -70.35205161461398,
-0.231103377548616, -0.7501899197440212, 1.847597816108189, 0, 30.950940869491138,
0, 0, 0, 1, 0
]);
};
const vintagePinhole = () => {
colorMatrix$1([
0.6279345635605994, 0.3202183420819367, -0.03965408211312453, 0, 9.651285835294123,
0.02578397704808868, 0.6441188644374771, 0.03259127616149294, 0, 7.462829176470591,
0.0466055556782719, -0.0851232987247891, 0.5241648018700465, 0, 5.159190588235296,
0, 0, 0, 1, 0
]);
};
return {
brightness,
brownie,
colorMatrix: (w, h, matrix) => {
colorMatrix$1(matrix);
},
contrast,
desaturate,
desaturateLuminance,
hue,
kodachrome,
negative,
polaroid,
saturation,
sepia,
shiftToBGR,
technicolor,
vintagePinhole
};
};
// Convolution Filter.
const SHADER$2 = [
'precision highp float;',
'varying vec2 vUv;',
'uniform sampler2D texture;',
'uniform vec2 px;',
'uniform float m[9];',
'void main(void) {',
'vec4 c11 = texture2D(texture, vUv - px);', // top left
'vec4 c12 = texture2D(texture, vec2(vUv.x, vUv.y - px.y));', // top center
'vec4 c13 = texture2D(texture, vec2(vUv.x + px.x, vUv.y - px.y));', // top right
'vec4 c21 = texture2D(texture, vec2(vUv.x - px.x, vUv.y) );', // mid left
'vec4 c22 = texture2D(texture, vUv);', // mid center
'vec4 c23 = texture2D(texture, vec2(vUv.x + px.x, vUv.y) );', // mid right
'vec4 c31 = texture2D(texture, vec2(vUv.x - px.x, vUv.y + px.y) );', // bottom left
'vec4 c32 = texture2D(texture, vec2(vUv.x, vUv.y + px.y) );', // bottom center
'vec4 c33 = texture2D(texture, vUv + px );', // bottom right
'gl_FragColor = ',
'c11 * m[0] + c12 * m[1] + c22 * m[2] +',
'c21 * m[3] + c22 * m[4] + c23 * m[5] +',
'c31 * m[6] + c32 * m[7] + c33 * m[8];',
'gl_FragColor.a = c22.a;',
'}'
].join('\n');
var convolutional = (compileShader, gl, draw) => {
const convolution = (width, height, matrix) => {
const m = new Float32Array(matrix);
const pixelSizeX = 1 / width;
const pixelSizeY = 1 / height;
const program = compileShader(SHADER$2);
gl.uniform1fv(program.uniform.m, m);
gl.uniform2f(program.uniform.px, pixelSizeX, pixelSizeY);
draw();
};
const detectEdges = (width, height) => {
convolution(width, height, [
0, 1, 0,
1, -4, 1,
0, 1, 0
]);
};
const emboss = (width, height, size = 1) => {
convolution(width, height, [
-2 * size, -1 * size, 0,
-1 * size, 1, 1 * size,
0, 1 * size, 2 * size
]);
};
const sharpen = (width, height, amount = 1) => {
convolution(width, height, [
0, -1 * amount, 0,
-1 * amount, 1 + 4 * amount, -1 * amount,
0, -1 * amount, 0
]);
};
const sobelX = (width, height) => {
convolution(width, height, [
-1, 0, 1,
-2, 0, 2,
-1, 0, 1
]);
};
const sobelY = (width, height) => {
convolution(width, height, [
-1, -2, -1,
0, 0, 0,
1, 2, 1
]);
};
return {
convolution,
detectEdges,
emboss,
sharpen,
sobelX,
sobelY
};
};
// Blur Filter.
const DRAW_INTERMEDIATE$1 = 1;
const SHADER$1 = [
'precision highp float;',
'varying vec2 vUv;',
'uniform sampler2D texture;',
'uniform vec2 px;',
'void main(void) {',
'gl_FragColor = vec4(0.0);',
'gl_FragColor += texture2D(texture, vUv + vec2(-7.0*px.x, -7.0*px.y))*0.0044299121055113265;',
'gl_FragColor += texture2D(texture, vUv + vec2(-6.0*px.x, -6.0*px.y))*0.00895781211794;',
'gl_FragColor += texture2D(texture, vUv + vec2(-5.0*px.x, -5.0*px.y))*0.0215963866053;',
'gl_FragColor += texture2D(texture, vUv + vec2(-4.0*px.x, -4.0*px.y))*0.0443683338718;',
'gl_FragColor += texture2D(texture, vUv + vec2(-3.0*px.x, -3.0*px.y))*0.0776744219933;',
'gl_FragColor += texture2D(texture, vUv + vec2(-2.0*px.x, -2.0*px.y))*0.115876621105;',
'gl_FragColor += texture2D(texture, vUv + vec2(-1.0*px.x, -1.0*px.y))*0.147308056121;',
'gl_FragColor += texture2D(texture, vUv )*0.159576912161;',
'gl_FragColor += texture2D(texture, vUv + vec2( 1.0*px.x, 1.0*px.y))*0.147308056121;',
'gl_FragColor += texture2D(texture, vUv + vec2( 2.0*px.x, 2.0*px.y))*0.115876621105;',
'gl_FragColor += texture2D(texture, vUv + vec2( 3.0*px.x, 3.0*px.y))*0.0776744219933;',
'gl_FragColor += texture2D(texture, vUv + vec2( 4.0*px.x, 4.0*px.y))*0.0443683338718;',
'gl_FragColor += texture2D(texture, vUv + vec2( 5.0*px.x, 5.0*px.y))*0.0215963866053;',
'gl_FragColor += texture2D(texture, vUv + vec2( 6.0*px.x, 6.0*px.y))*0.00895781211794;',
'gl_FragColor += texture2D(texture, vUv + vec2( 7.0*px.x, 7.0*px.y))*0.0044299121055113265;',
'}'
].join('\n');
var blur = (compileShader, gl, draw) => {
const blur = (width, height, radius = 0) => {
const blurSizeX = (radius / 7) / width;
const blurSizeY = (radius / 7) / height;
const program = compileShader(SHADER$1);
// Vertical
gl.uniform2f(program.uniform.px, 0, blurSizeY);
draw(DRAW_INTERMEDIATE$1);
// Horizontal
gl.uniform2f(program.uniform.px, blurSizeX, 0);
draw();
};
return { blur };
};
// Pixelate Filter
const SHADER = [
'precision highp float;',
'varying vec2 vUv;',
'uniform vec2 size;',
'uniform sampler2D texture;',
'vec2 pixelate(vec2 coord, vec2 size) {',
'return floor( coord / size ) * size;',
'}',
'void main(void) {',
'gl_FragColor = vec4(0.0);',
'vec2 coord = pixelate(vUv, size);',
'gl_FragColor += texture2D(texture, coord);',
'}'
].join('\n');
var pixelate = (compileShader, gl, draw) => {
const pixelate = (width, height, size = 0) => {
const blurSizeX = size / width;
const blurSizeY = size / height;
const program = compileShader(SHADER);
// Horizontal
gl.uniform2f(program.uniform.size, blurSizeX, blurSizeY);
draw();
};
return { pixelate };
};
const DRAW_INTERMEDIATE = 1;
const FLOAT_SIZE = Float32Array.BYTES_PER_ELEMENT;
const VERT_SIZE = 4 * FLOAT_SIZE;
const SHADER_VERTEX_IDENTITY = [
'precision highp float;',
'attribute vec2 pos;',
'attribute vec2 uv;',
'varying vec2 vUv;',
'uniform float flipY;',
'void main(void) {',
'vUv = uv;',
'gl_Position = vec4(pos.x, pos.y*flipY, 0.0, 1.);',
'}'
].join('\n');
const SHADER_FRAGMENT_IDENTITY = [
'precision highp float;',
'varying vec2 vUv;',
'uniform sampler2D texture;',
'void main(void) {',
'gl_FragColor = texture2D(texture, vUv);',
'}',
].join('\n');
const createFramebufferTexture = (gl, width, height) => {
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
const renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
return { fbo, texture };
};
const imageFilter = (params = {}) => {
const canvas = params.canvas || document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
throw `Couldn't get WebGL context.`;
}
let drawCount = 0;
let sourceTexture = null;
let lastInChain = false;
let currentFramebufferIndex = -1;
let tempFramebuffers = [null, null];
let filterChain = [];
let width = -1;
let height = -1;
let vertexBuffer = null;
let currentProgram = null;
// Key is the shader program source, value is the compiled program.
const shaderProgramCache = {};
const getTempFramebuffer = index => {
tempFramebuffers[index] = tempFramebuffers[index] ||
createFramebufferTexture(gl, width, height);
return tempFramebuffers[index];
};
const draw = flags => {
let source = null;
let target = null;
let flipY = false;
// Set up the source
if (drawCount === 0) {
// First draw call - use the source texture.
source = sourceTexture;
}
else {
// All following draw calls use the temp buffer last drawn to.
source = getTempFramebuffer(currentFramebufferIndex).texture;
}
drawCount += 1;
// Set up the target.
if (lastInChain && !(flags & DRAW_INTERMEDIATE)) {
// Last filter in our chain - draw directly to the WebGL Canvas.
// We may also have to flip the image vertically now.
target = null;
flipY = drawCount % 2 === 0;
}
else {
// Intermediate draw call - get a temp buffer to draw to.
currentFramebufferIndex = (currentFramebufferIndex + 1) % 2;
target = getTempFramebuffer(currentFramebufferIndex).fbo;
}
// Bind the source and target and draw the two triangles.
gl.bindTexture(gl.TEXTURE_2D, source);
gl.bindFramebuffer(gl.FRAMEBUFFER, target);
gl.uniform1f(currentProgram.uniform.flipY, (flipY ? -1 : 1));
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
const compileShader = fragmentSource => {
if (shaderProgramCache[fragmentSource]) {
currentProgram = shaderProgramCache[fragmentSource];
gl.useProgram(currentProgram.id);
return currentProgram;
}
// Compile shaders.
currentProgram = webGLProgram(gl, SHADER_VERTEX_IDENTITY, fragmentSource);
gl.enableVertexAttribArray(currentProgram.attribute.pos);
gl.vertexAttribPointer(currentProgram.attribute.pos, 2, gl.FLOAT, false, VERT_SIZE, 0);
gl.enableVertexAttribArray(currentProgram.attribute.uv);
gl.vertexAttribPointer(currentProgram.attribute.uv, 2, gl.FLOAT, false, VERT_SIZE, 2 * FLOAT_SIZE);
shaderProgramCache[fragmentSource] = currentProgram;
return currentProgram;
};
const resize = (w, h) => {
// Same width/height? Nothing to do here.
if (w === width && h === height) { return; }
canvas.width = width = w;
canvas.height = height = h;
// Create the context if we don't have it yet.
if (!vertexBuffer) {
// Create the vertex buffer for the two triangles [x, y, u, v] * 6.
const vertices = new Float32Array([
-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0,
-1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0
]);
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Note sure if this is a good idea; at least
// it makes texture loading in Ejecta instant.
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
}
gl.viewport(0, 0, width, height);
// Delete old temp framebuffers.
tempFramebuffers = [null, null];
};
const filters = {
...standard(compileShader, gl, draw),
...convolutional(compileShader, gl, draw),
...blur(compileShader, gl, draw),
...pixelate(compileShader, gl, draw),
};
const addFilter = (name, ...args) => {
const func = filters[name];
filterChain.push({ func, args });
};
const reset = () => {
filterChain = [];
};
let applied = false;
const apply = image => {
resize(image.width, image.height);
drawCount = 0;
// Create the texture for the input image.
if (!sourceTexture) {
sourceTexture = gl.createTexture();
}
gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
if (!applied) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
applied = true;
}
else {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
}
// No filters? Just draw.
if (filterChain.length === 0) {
compileShader(SHADER_FRAGMENT_IDENTITY);
draw();
return canvas;
}
const count = filterChain.length - 1;
filterChain.forEach((filter, index) => {
lastInChain = (index === count);
const { func, args } = filter;
// Include width, height for convolutional functions.
func(width, height, ...args);
});
return canvas;
};
return {
addFilter,
apply,
reset
};
};
var ImageFilter = imageFilter();
var Filter$1 = function (canvas) {
this.canvas = canvas;
this.context = canvas.getContext('2d');
this.filters = [];
return this;
};
Filter$1.prototype = {
apply: function () {
var self = this;
for (var i = 0; i < this.filters.length; i++) {
var item = self.filters[i],
fn = self[item.name];
if (fn) {
fn.call(self, [item.values]);
}
self.filters.splice(i);
}
return this;
},
add: function (filter, values) {
this.filters.push({ "name": filter, "values": values });
return this;
},
reset: function () {
this.filters = [];
return this;
},
_applyWebGlFilter: function (filter, value) {
var filteredImage;
ImageFilter.addFilter(filter, value);
try {
filteredImage = ImageFilter.apply(this.canvas);
} catch (err) {
// error
}
if (filteredImage) {
this.context.drawImage(filteredImage, 0, 0);
}
ImageFilter.reset();
},
_applyColorMatrixFilter: function (filter, amount) {
var imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
// Apply filter to region
new ColorMatrix(imageData.data).filter(filter, amount);
// apply filtered region to image buffer
this.context.putImageData(imageData, 0, 0);
},
/* Grayscale filter
* http://www.html5canvastutorials.com/advanced/html5-canvas-grayscale-image-colors-tutorial/
*/
grayscale: function (amount, save) {
/*var imgData = ctx.getImageData(0, 0, w, h);
var data = imgData.data;*/
this._filter(function (data) {
for (var i = 0, len = data.length; i < len; i += 4) {
// use same values as GD2 Library
var v = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
data[i] = v; // red
data[i + 1] = v; // green
data[i + 2] = v; // blue
}
});
//ctx.putImageData(imgData, 0, 0);
},
sepia: function (amount, save) {
this._filter(function (data, start, end) {
for (var i = 0, len = data.length; i < len; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
// change image colors using Microsoft sepia color matrix
data[i] = r * 0.393 + g * 0.769 + b * 0.189;
data[i + 1] = r * 0.349 + g * 0.686 + b * 0.168;
data[i + 2] = r * 0.272 + g * 0.534 + b * 0.131;
}
});
},
/*
* Invert Colours
* http://www.html5canvastutorials.com/advanced/html5-canvas-invert-image-colors-tutorial/
*/
invert: function (amount, save) {
this._filter(function (data) {
for (var i = 0, len = data.length; i < len; i += 4) {
data[i] = 255 - data[i]; // red
data[i + 1] = 255 - data[i + 1]; // green
data[i + 2] = 255 - data[i + 2]; // blue
}
});
},
threshold: function (threshold, save) {
if (save) {
this.save();
}
threshold = parseInt(threshold) || 128;
this._filter(function (data, start, end) {
for (var i = 0, len = data.length; i < len; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
var v = Math.round((r + g + b) / 3) >= threshold ? 255 : 0;
// Assign average to red, green, and blue.
data[i] = v;
data[i + 1] = v;
data[i + 2] = v;
}
}, threshold);
},
brightness: function (amount, save) {
// set max / min values
amount = Math.min(100, Math.max(-100, amount));
this._filter(function (data) {
for (var i = 0, len = data.length; i < len; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
// change image colors
data[i] = Math.max(0, Math.min(255, Math.round(r + amount)));
data[i + 1] = Math.max(0, Math.min(255, Math.round(g + amount)));
data[i + 2] = Math.max(0, Math.min(255, Math.round(b + amount)));
}
});
},
contrast: function (amount, save) {
amount = Math.max(-100, Math.min(100, amount));
var factor = 1;
if (amount > 0) {
factor = 1 + (amount / 100);
} else {
factor = (100 - Math.abs(amount)) / 100;
}
function n(x) {
return Math.max(0, Math.min(255, x));
}
this._filter(function (data) {
for (var i = 0, len = data.length; i < len; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
// change image contrast
r = n(factor * (r - 128) + 128);
g = n(factor * (g - 128) + 128);
b = n(factor * (b - 128) + 128);
data[i] = Math.min(255, Math.max(0, Math.round(r)));
data[i + 1] = Math.min(255, Math.max(0, Math.round(g)));
data[i + 2] = Math.min(255, Math.max(0, Math.round(b)));
}
});
},
saturate: function (amount, save) {
amount = parseFloat(amount) / 100;
amount = Math.max(-1, Math.min(1, amount));
this._filter(function (data) {
for (var i = 0, len = data.length; i < len; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
var average = (r + g + b) / 3;
r += Math.round((r - average) * amount);
g += Math.round((g - average) * amount);
b += Math.round((b - average) * amount);
data[i] = Math.min(255, Math.max(0, r));
data[i + 1] = Math.min(255, Math.max(0, g));
data[i + 2] = Math.min(255, Math.max(0, b));
}
});
},
desaturate: function (amount, save) {
this.saturate(0 - amount, save);
return this;
},
hue: function (amount, save) {
/*if (ImageFilter) {
amount = amount * 2;
return this._applyWebGlFilter('hue', amount);
}*/
this._applyColorMatrixFilter('hue', amount);
return this;
},
gamma: function (amount, save) {
/*if (ImageFilter) {
amount = amount / 100;
return this.webGLImageFilter('gamma', amount);
}*/
amount = amount / 100;
this._filter(function (data, start, end) {
for (var i = 0, len = data.length; i < len; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
data[i] = Math.pow(r / 255, 1 - amount) * 255;
data[i + 1] = Math.pow(g / 255, 1 - amount) * 255;
data[i + 2] = Math.pow(b / 255, 1 - amount) * 255;
}
});
return this;
},
exposure: function (amount, save) {
amount = amount / 10;
this._filter(function (data, start, end) {
for (var i = 0, len = data.length; i < len; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
data[i] = 255 * (1 - Math.exp(-(r / 255) * amount));
data[i + 1] = 255 * (1 - Math.exp(-(g / 255) * amount));
data[i + 2] = 255 * (1 - Math.exp(-(b / 255) * amount));
}
});
return this;
},
blur: function (amount, save) {
if (ImageFilter) {
amount = amount / 5;
return this._applyWebGlFilter('blur', amount);
}
var matrix = [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9];
if (amount) {
amount = Math.floor(amount / 5);
amount = Math.max(1, amount);
for (var i = 0; i < amount; i++) {
this.convolution(matrix);
}
}
},
sharpen: function (amount, save) {
if (ImageFilter) {
amount = amount / 100;
return this._applyWebGlFilter('sharpen', amount);
}
var matrix = [0, -1, 0, -1, 5, -1, 0, -1, 0];
if (amount) {
amount = Math.floor(amount / 10);
amount = Math.max(1, amount);
for (var i = 0; i < amount; i++) {
this.convolution(matrix);
}
}
},
polaroid: function (save) {
if (ImageFilter) {
return this._applyWebGlFilter('polaroid');
}
//return this._applyColorMatrixFilter('polaroid');
},
vintage: function (save) {
if (ImageFilter) {
return this._applyWebGlFilter('vintagePinhole');
}
//return this._applyColorMatrixFilter('polaroid');
},
brownie: function (save) {
if (ImageFilter) {
return this._applyWebGlFilter('brownie');
}
//return this._applyColorMatrixFilter('polaroid');
},
kodachrome: function (save) {
if (ImageFilter) {
return this._applyWebGlFilter('kodachrome');
}
//return this._applyColorMatrixFilter('polaroid');
},
technicolor: function (save) {
if (ImageFilter) {
return this._applyWebGlFilter('technicolor');
}
//return this._applyColorMatrixFilter('polaroid');
},
// http://www.html5rocks.com/en/tutorials/canvas/imagefilters/#toc-convolution
convolution: function (matrix) {
var ctx = this.context,
sw = this.canvas.width,
sh = this.canvas.height;
var side = Math.round(Math.sqrt(matrix.length));
var halfSide = Math.floor(side / 2);
// pad output by the convolution matrix
var w = sw;
var h = sh;
var input = ctx.getImageData(0, 0, sw, sh);
var output = ctx.createImageData(sw, sh);
var src = input.data;
var dst = output.data;
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
var sy = y;
var sx = x;
var dstOff = (y * w + x) * 4;
// calculate the weighed sum of the source image pixels that
// fall under the convolution matrix
var r = 0,
g = 0,
b = 0,
a = 0;
for (var cy = 0; cy < side; cy++) {
for (var cx = 0; cx < side; cx++) {
var scy = sy + cy - halfSide;
var scx = sx + cx - halfSide;
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
var srcOff = (scy * sw + scx) * 4;
var wt = matrix[cy * side + cx];
r += src[srcOff] * wt;
g += src[srcOff + 1] * wt;
b += src[srcOff + 2] * wt;
a += src[srcOff + 3] * wt;
}
}
}
dst[dstOff] = r;
dst[dstOff + 1] = g;
dst[dstOff + 2] = b;
dst[dstOff + 3] = a;
}
}
ctx.putImageData(output, 0, 0);
},
_filter: function (fn, amount) {
var ctx = this.context,
rx, ry, size = 480,
imageData, rects = [],
lastTime, rectIndex = 0;
// Default x, y, w, h
var x = 0,
y = 0,
w = this.canvas.width,
h = this.canvas.height;
// Calculate an array of rectanges to process
for (ry = y; ry < h; ry += size) {
for (rx = x; rx < w; rx += size) {
rects.push({
x: rx,
y: ry,
w: rx + size > w ? w - rx : size,
h: ry + size > h ? h - ry : size
});
}
}
// Start timer
lastTime = new Date().getTime();
// Filters a specific rectangle from queue
function processNext() {
var rect = rects[rectIndex++],
now;
if (rect) {
imageData = ctx.getImageData(rect.x, rect.y, rect.w, rect.h);
// Apply filter to region
fn(imageData.data);
// apply filtered region to image buffer
ctx.putImageData(imageData, rect.x, rect.y);
// get now
now = new Date().getTime();
// if process time is less than 1 second, process next
if (now - lastTime < 1000) {
processNext();
} else {
// slight delay to prevent browser hanging
lastTime = now;
window.setTimeout(processNext, 0);
//self._trigger('onfilterprogress', null, rectIndex / rects.length);
}
} else {
//self._trigger('onfilterprogress', null, 1);
return;
}
}
// Start the process
processNext();
}
};
window.Filter = Filter$1;
/* global jQuery, Wf, Filter */
(function ($, Wf) {
$.support.canvas = !!document.createElement('canvas').getContext;
$.widget("ui.canvas", {
stack: [],
options: {
onfilterstart: $.noop,
onfilterprogress: $.noop
},
_create: function () {
// create canvas
this.canvas = document.createElement('canvas');
// store context
this.context = this.canvas.getContext('2d', { willReadFrequently : true });
this.draw();
},
getContext: function () {
return this.context;
},
getCanvas: function () {
return this.canvas;
},
setSize: function (w, h) {
$.extend(this.options, {
width: w,
height: h
});
this.draw();
},
draw: function (el, w, h) {
el = el || $(this.element).get(0);
var w = w || this.options.width || el.width,
h = h || this.options.height || el.height;
// save state
this.save();
$(this.canvas).attr({
width: w,
height: h
});
this.context.drawImage(el, 0, 0, w, h);
},
/*
* Clear canvas and remove to free memmory
* @param {type} n
*/
free: function (n) {
n.getContext('2d').clearRect(0, 0, 0, 0);
$(n).remove();
},
/*
* Clone canvas element
* @returns canvas element
*/
clone: function () {
return $(this.canvas).clone().get(0);
},
/*
* Create a copy of the canvas
* @returns canvas copy
*/
copy: function () {
var copy = this.clone();
copy.getContext('2d').drawImage(this.canvas, 0, 0);
return copy;
},
/*
* Clear the canvas
* @returns {void}
*/
clear: function () {
var ctx = this.context;
var w = $(this.element).width(),
h = $(this.element).height();
if (ctx) {
ctx.clearRect(0, 0, w, h);
}
},
/*
* Resize Canvas
* @param {int} w
* @param {int} h
* @param {bool} save
* @returns {void}
*/
resize: function (w, h, save) {
var ctx = this.context;
w = parseInt(w), h = parseInt(h);
if (ctx) {
if (save) {
this.save();
}
ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = true;
var copy = this.copy();
copy.getContext('2d').drawImage(this.canvas, 0, 0, w, h);
$(this.canvas, copy).attr({
width: w,
height: h
});
ctx.drawImage(copy, 0, 0);
//this.resample();
// remove copy - free memory?
this.free(copy);
}
},
/**
* Crop an image
* @param w {integer} Crop width
* @param h {integer} Crop height
* @param x {integer} Crop position
* @param y {integer} Crop position
* @param save {boolean} Save before performing action
*/
crop: function (w, h, x, y, save) {
var ctx = this.context;
w = parseInt(w), h = parseInt(h), x = parseInt(x), y = parseInt(y);
if (ctx) {
if (save) {
this.save();
}
if (x < 0) {
x = 0;
}
if (x > this.canvas.width - 1) {
x = this.canvas.width - 1;
}
if (y < 0) {
y = 0;
}
if (y > this.canvas.height - 1) {
y = this.canvas.height - 1;
}
if (w < 1) {
w = 1;
}
if (x + w > this.canvas.width) {
w = this.canvas.width - x;
}
if (h < 1) {
h = 1;
}
if (y + h > this.canvas.height) {
h = this.canvas.height - y;
}
ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = true;
var copy = this.copy();
copy.getContext('2d').drawImage(this.canvas, 0, 0);
$(this.canvas).attr({
width: w,
height: h
});
ctx.drawImage(copy, x, y, w, h, 0, 0, w, h);
// remove copy - free memory?
this.free(copy);
}
},
/**
*Simple rotate on 90 degree increments
*/
rotate: function (angle, save) {
var ctx = this.context,
w = this.canvas.width,
h = this.canvas.height,
cw, ch;
if (angle < 0) {
angle = angle + 360;
}
switch (angle) {
case 90:
case 270:
cw = h;
ch = w;
break;
case 180:
cw = w;
ch = h;
break;
}
if (ctx) {
if (save) {
this.save();
}
var copy = this.copy();
$(this.canvas).attr({
width: cw,
height: ch
});
ctx.translate(cw / 2, ch / 2);
ctx.rotate(angle * Math.PI / 180);
ctx.drawImage(copy, -w / 2, -h / 2);
// remove copy - free memory?
this.free(copy);
}
},
/**
* Flip an image veritcally or horizontally
* @param axis {string} Axis to flip on
* @param save {boolean} Save before performing action
*/
flip: function (axis, save) {
var ctx = this.context,
w = this.canvas.width,
h = this.canvas.height;
if (ctx) {
if (save) {
this.save();
}
var copy = this.copy();
copy.getContext('2d').drawImage(this.canvas, 0, 0, w, h, 0, 0, w, h);
ctx.clearRect(0, 0, w, h);
$(this.canvas).attr({
width: w,
height: h
});
if (axis == "horizontal") {
ctx.scale(-1, 1);
ctx.drawImage(copy, -w, 0, w, h);
} else {
ctx.scale(1, -1);
ctx.drawImage(copy, 0, -h, w, h);
}
// remove copy - free memory?
this.free(copy);
}
},
filter: function (filter, amount, save) {
if (save) {
this.save();
}
new Filter(this.canvas).add(filter, amount).apply();
},
save: function () {
var ctx = this.context,
w = this.canvas.width,
h = this.canvas.height;
this.stack.push({
width: w,
height: h,
data: ctx.getImageData(0, 0, w, h)
});
},
/**
* Restore canvas to its original state
*/
restore: function () {
var ctx = this.context,
img = $(this.element).get(0);
ctx.restore();
ctx.drawImage(img, 0, 0);
},
/**
* Undo the last action
*/
undo: function () {
var ctx = this.context;
var item = this.stack.pop();
if (!item) {
return;
}
$(this.canvas).attr({
width: item.width,
height: item.height
});
if (item.data) {
ctx.putImageData(item.data, 0, 0);
} else {
this.restore();
}
},
load: function () {
var ctx = this.context;
var w = this.canvas.width,
h = this.canvas.height;
var data = ctx.getImageData(0, 0, w, h);
ctx.clearRect(0, 0, w, h);
ctx.putImageData(data, 0, 0);
},
update: function () {
this.load();
this.stack = [];
},
getMime: function (s) {
var mime = 'image/jpeg';
var ext = Wf.String.getExt(s);
switch (ext) {
case 'jpg':
case 'jpeg':
mime = 'image/jpeg';
break;
case 'png':
mime = 'image/png';
break;
case 'webp':
mime = 'image/webp';
break;
case 'bmp':
mime = 'image/bmp';
break;
}
return mime;
},
resample: function (callback, nw, nh) {
var self = this,
ctx = this.context;
var w = this.canvas.width,
h = this.canvas.height;
var data1 = ctx.getImageData(0, 0, w, h);
var tmp = this.copy();
var data2 = tmp.getContext("2d").getImageData(0, 0, w, h);
var worker = new Worker(Wf.getPath() + '/js/worker-hermite.js');
worker.onmessage = function (event) {
var out = event.data.data;
//console.log("hermite resize completed in "+(Math.round(Date.now() - time1)/1000)+" s");
self.clear();
self.context.putImageData(out, 0, 0);
typeof callback === "function" && callback();
};
worker.postMessage([data1, w, h, nw || w, nh || h, data2]);
},
output: function (mime, quality, blob) {
var self = this;
mime = mime || this.getMime($(this.element).get(0).src);
quality = parseInt(quality) || 100;
quality = Math.max(Math.min(quality, 100), 10);
// divide by 100 to give value between 0.1 and 1
quality = quality / 100;
// reload the data
this.load();
function dataURItoBlob(dataURI, mime) {
var byteString, i, arrayBuffer, intArray;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
// Convert base64 to raw binary data held in a string:
byteString = atob(dataURI.split(',')[1]);
} else {
// Convert base64/URLEncoded data component to raw binary data:
byteString = decodeURIComponent(dataURI.split(',')[1]);
}
// Write the bytes of the string to an ArrayBuffer:
arrayBuffer = new ArrayBuffer(byteString.length), intArray = new Uint8Array(arrayBuffer);
for (i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i);
}
return new Blob([arrayBuffer], {
'type': mime
});
}
if (blob) {
return dataURItoBlob(self.canvas.toDataURL(mime, quality), mime);
} else {
return self.canvas.toDataURL(mime, quality);
}
},
remove: function () {
$(this.canvas).remove();
this.destroy();
}
});
})(jQuery, Wf);
/* eslint-disable consistent-this */
/* eslint-disable radix */
/* global tinyMCEPopup, Wf, jQuery, FileBrowser */
(function ($, Wf) {
var $tmp = document.createElement('div'),
prefixes = ['-ms-', '-moz-', '-webkit-', '-o-', ''];
$.support.filter = (function () {
// No support for IE or Opera
if (document.documentMode || window.opera) {
return false;
}
$tmp.style.cssText = prefixes.join('filter:grayscale(1); ');
return !!$tmp.style.length;
})();
$.fn.cssFilter = function (o) {
var prefixes = ['', '-moz-', '-webkit-', '-ms-', ''],
filter = o.filter || '',
amount = o.amount || 1;
if (!filter) {
return this;
}
switch (filter) {
case 'desaturate':
case 'saturate':
amount = (100 + amount) / 100;
filter = 'saturate';
break;
case 'contrast':
case 'invert':
case 'grayscale':
case 'sepia':
break;
case 'brightness':
amount = amount / 100;
filter = 'brightness';
break;
case 'blur':
amount = amount / 100 + 'px';
break;
case 'sharpen':
break;
case 'gamma':
break;
case 'exposure':
break;
case 'hue':
amount = amount + 'deg';
filter = 'hue-rotate';
break;
}
$(this).attr('style', prefixes.join('filter' + ':' + filter + '(' + amount + '); '));
/*if (!$(this).get(0).style.cssText) {
// swap filter names
switch (filter) {
case 'saturate':
if (amount < 1) {
filter = 'desaturate';
}
break;
case 'brightness':
amount = 1 + amount / 0.66;
if (amount < 1) {
filter = 'darkness';
}
break;
}
$(this).css('filter', 'url(#' + filter + ')');
if (matrix.length) {
$('#' + filter + ' feColorMatrix').attr('values', matrix.join(' '));
}
if (filter == 'brightness' || filter == 'darkness') {
$('feFuncR, feFuncG, feFuncB', '#' + filter).attr('slope', amount);
}
}*/
return this;
};
$.fn.cssTransform = function (transform, amount) {
var keys = ['transform', 'msTransform', 'Transform', 'WebkitTransform', 'OTransform'];
this.each(function () {
var n = $(this).get(0);
switch (transform) {
case 'flip':
transform = (amount == 'horizontal') ? 'scaleX' : 'scaleY';
amount = -1;
break;
case 'rotate':
amount = amount + 'deg';
break;
}
$.each(keys, function (o, s) {
// get existing transforms
var transforms = n.style[s] || [];
if (transforms.length) {
transforms = transforms.split(' ');
}
transforms.push(transform + '(' + amount + ')');
n.style[s] = transforms.join(' ');
});
});
return this;
};
// uid counter
var counter = 0;
/**
Generates an unique ID.
@method uid
@return {String} Virtually unique id.
*/
function uid() {
var guid = new Date().getTime().toString(32),
i;
for (i = 0; i < 5; i++) {
guid += Math.floor(Math.random() * 65535).toString(32);
}
return 'wf_' + guid + (counter++).toString(32);
}
function getRatio(o) {
// Calculate Greatest Common Diviser
function gcd(a, b) {
return (b == 0) ? a : gcd(b, a % b);
}
// get gcd for the image
var r = gcd(o.width, o.height);
// return ratio
return (o.width / r) / (o.height / r);
}
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
// http://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
{
func.apply(context, args);
}
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
var ImageEditor = {
stack: [],
fxstack: [],
settings: {
resize_quality: 80,
onsave: $.noop
},
_setLoader: function () {
$('div.loading', '#editor').show();
// disable all inputs
$(':input', '#editor').addClass('working-disabled').prop('disabled', true);
this.working = true;
},
_removeLoader: function () {
// remove loader
$('div.loading', '#editor').hide();
// enable all inputs
$(':input.working-disabled', '#editor').removeClass('working-disabled').prop('disabled', false);
// set global working state
this.working = false;
},
init: function (options) {
var self = this;
// run init
Wf.init(options);
$('#editor').removeClass('offleft');
// window resize
$(window).on('resize orientationchange', function () {
self._resizeWin();
});
// get image src from window arguments
this.src = tinyMCEPopup.getWindowArg('url');
if (!this._validatePath(this.src)) {
Wf.Modal.alert('Invalid image file');
return false;
}
$.extend(this.settings, {
width: tinyMCEPopup.getWindowArg('width'),
height: tinyMCEPopup.getWindowArg('height'),
onsave: tinyMCEPopup.getWindowArg('onsave')
});
// set laoder
this._setLoader();
// build and store the image object
$('<img />').attr('src', this._loadImage(this.src)).one('load', function () {
var n = this;
// store original width and height
$(n).data('width', n.width).data('height', n.height).appendTo('#editor-image');
// create canvas object
$(n).canvas({
width: n.width,
height: n.height,
onfilterprogress: function (e, n) {
if (n == 1) {
self._removeLoader();
}
}
});
var canvas = $(n).canvas('getCanvas');
$(canvas).insertAfter(n);
//self._createImageSlider();
self.position();
// create transform toolbox
self._createToolBox();
// create FX tools
self._createFX();
// remove loader
self._removeLoader();
}).on('error', function () {
Wf.Modal.alert('Invalid image file');
return false;
}).hide();
$('#transform_tab').accordion().on('accordion.activate', function (e, tab, panel) {
var action = $(tab).data('action');
self.reset(true);
if (action) {
self._initTransform(action);
}
}).children('.uk-accordion-title').first().click;
$('#tabs').tabs().on('tabs.activate', function () {
self.reset(true);
// reset fx units
self._resetFX();
});
//$('#resize_constrain, #thumbnail_constrain').constrain();
$('button.save').on('click', function (e) {
self.save();
e.preventDefault();
}).prop('disabled', true);
$('button.revert').on('click', function (e) {
self.revert(e);
e.preventDefault();
}).prop('disabled', true);
$('button.undo').on('click', function (e) {
e.preventDefault();
self.undo(e);
}).prop('disabled', true);
$('button.apply', '#editor').on('click', function (e) {
e.preventDefault();
self._applyTransform($(this).data('function'));
});
$('button.reset', '#transform_tab').on('click', function (e) {
e.preventDefault();
self._resetTransform($(this).data('function'));
});
$('#effects_apply').on('click', function (e) {
e.preventDefault();
self._resetFX();
$('img', '#editor-image').canvas('update');
self.stack = [];
$('button.undo').prop('disabled', true);
});
$('#effects_reset').on('click', function (e) {
e.preventDefault();
self._resetFX();
self.revert(e);
});
},
_createToolBox: function () {
var self = this,
$img = $('img', '#editor-image'),
canvas = $img.canvas('getCanvas');
var iw = canvas.width;
var ih = canvas.height;
// setup presets
$('#crop_presets option, #resize_presets option').each(function () {
var v = $(this).val();
if (v && /[0-9]+x[0-9]+/.test(v)) {
v = v.split('x');
var w = parseFloat(v[0]),
h = parseFloat(v[1]);
if (w >= $img.data('width') && h >= $img.data('height')) {
$(this).remove();
}
}
});
// resize presets
$('#resize_presets').on('change', function () {
var v = $(this).val();
if (v) {
if (v.indexOf(':') != -1) {
var r = v.split(':'),
r1 = parseInt($.trim(r[0])),
r2 = parseInt($.trim(r[1]));
var ratio = r1 / r2;
if (r2 > r1) {
ratio = r2 / r1;
}
// landscape
if (iw > ih) {
if (r2 > r1) {
ratio = r2 / r1;
}
}
w = Math.round(iw / ratio);
h = Math.round(ih / ratio);
} else {
v = v.split('x');
var w = parseFloat($.trim(v[0])),
h = parseFloat($.trim(v[1]));
}
$('#resize_width').val(w).data('tmp', w);
$('#resize_height').val(h).data('tmp', h);
var ratio = $('#resize_constrain').prop('checked') ? w / h : false;
$(canvas).resize('setRatio', ratio);
$(canvas).resize('setSize', w, h);
}
});
// Add Original
$('option', '#resize_presets').first().text(function (i, txt) {
return '' + iw + ' x ' + ih + ' (' + txt + ')';
});
// resize values
$('#resize_width').val(iw).data('tmp', iw).on('change', function () {
var w = $(this).val(),
$height = $('#resize_height');
w = w || $(this).data('tmp');
// constrain is always on
var tw = $(this).data('tmp'),
h = $height.val();
var temp = ((h / tw) * w).toFixed(0);
$height.val(temp).data('tmp', temp);
// store new tmp value
$(this).data('tmp', w);
$(canvas).resize('setSize', w, $height.val());
});
$('#resize_height').val(ih).data('tmp', ih).on('change', function () {
var h = $(this).val(),
$width = $('#resize_width');
h = h || $(this).data('tmp');
// constrain is always on
var th = $(this).data('tmp'),
w = $width.val();
var temp = ((w / th) * h).toFixed(0);
$width.val(temp).data('tmp', temp);
// store new tmp value
$(this).data('tmp', h);
$(canvas).resize('setSize', $width.val(), h);
});
$.each(['width', 'height'], function (i, key) {
$('#crop_' + key).val(canvas[key]).data('tmp', canvas[key]);
});
// crop values - width and height
$('#crop_width, #crop_height').on('change', function () {
var w = $('#crop_width').val(),
h = $('#crop_height').val();
var s = { 'width': w, 'height': h };
var ratio = s.width / s.height;
if ($('#crop_constrain').is(':checked')) {
$(canvas).crop('setRatio', ratio);
}
$('#crop_presets').val(w + 'x' + h);
$(canvas).crop('setArea', s, true);
});
// crop values - x
$('#crop_x, #crop_y').val(0).on('change', function () {
var data = {};
$(this).parents('.uk-form').find('input[type="text"]').each(function () {
var key = this.id.replace('crop_', ''), val = $(this).val() || 0;
data[key] = parseInt(val);
});
var ratio = data.width / data.height;
if ($('#crop_constrain').is(':checked')) {
$(canvas).crop('setRatio', ratio);
}
$(canvas).crop('setArea', data, true);
});
// crop constrain
$('#crop_constrain').on('click', function () {
var state = $(this).is(':checked');
if (state) {
$('#crop_presets').trigger('change');
$(this).addClass('checked');
} else {
$(this).removeClass('checked');
}
$(canvas).crop('setConstrain', state);
});
$('#crop_presets').on('change', function () {
var img = $img.get(0);
var v = $(this).val();
if (!v) {
return;
}
var s = {
width: img.width,
height: img.height
};
if (v.indexOf(':') != -1) {
var r = v.split(':'),
r1 = parseInt($.trim(r[0])),
r2 = parseInt($.trim(r[1]));
var ratio = r1 / r2;
if (r2 > r1) {
ratio = r2 / r1;
}
// landscape
if (s.width > s.height) {
if (r2 > r1) {
ratio = r2 / r1;
}
s.height = Math.round(s.width / ratio);
// portrait
} else {
s.width = Math.round(s.height / ratio);
}
} else {
v = v.split('x');
s.width = parseInt($.trim(v[0])), s.height = parseInt($.trim(v[1]));
var ratio = s.width / s.height;
}
if ($('#crop_constrain').is(':checked')) {
$(canvas).crop('setRatio', ratio);
}
$('#crop_width').val(s.width).data('tmp', s.width);
$('#crop_height').val(s.height).data('tmp', s.height);
$(canvas).crop('setArea', s, false);
});
// Add Original
$('option', '#crop_presets').first().text(function (i, txt) {
return '' + iw + ' x ' + ih + ' (' + txt + ')';
}).val(iw + 'x' + ih);
$('#transform-crop-cancel').on('click', function () {
self.reset();
});
$('#rotate-angle-clockwise').on('click', function () {
self._applyTransform('rotate', 90);
});
$('#rotate-angle-anticlockwise').on('click', function () {
self._applyTransform('rotate', -90);
});
$('#rotate-flip-vertical').on('click', function () {
self._applyTransform('flip', 'vertical');
});
$('#rotate-flip-horizontal').on('click', function () {
self._applyTransform('flip', 'horizontal');
});
},
_createFX: function () {
var self = this,
$img = $('img', '#editor-image');
$('#editor_effects').empty();
var debounceApply = debounce(function (fx, amount) {
self._applyFx(fx, amount);
}, 500);
$.each({
'brightness': {
factor: 10,
preview: 150
},
'contrast': {
factor: 10,
preview: 2
},
'hue': {
factor: 1,
preview: 90,
min: -180,
max: 180
},
'saturation': {
factor: 10,
preview: 200,
filter: 'saturate',
min: -10,
max: 10,
step: 1,
value: 0
},
'sharpen': {
factor: 10,
preview: 70,
min: 0,
webgl: true
},
'blur': {
factor: 10,
preview: 70,
min: 0,
webgl: true
},
'gamma': {
factor: 1,
preview: 50,
min: -100,
max: 100,
value: 0,
step: 1
}
/*,
'exposure': {
factor: 1,
preview: 5,
min: 0,
max: 10,
value: 0,
step: 1
}*/
}, function (k, v) {
var canvas, fx = $img.clone().addClass('uk-responsive-width').appendTo('#editor_effects').wrap('<div class="editor_effect uk-width-1-1 uk-grid uk-grid-small"></div>').wrap('<div class="editor_effect_preview uk-width-1-4 uk-float-left"></div>');
var filter = v.filter || k;
v = $.extend({
"step": 1,
"min": -10,
"max": 10,
"value": 0
}, v);
if ($.support.filter) {
$(fx).show().cssFilter({ 'filter': filter, 'amount': v.preview });
canvas = fx;
} else {
$(fx).canvas().canvas('filter', [filter, v.preview]);
canvas = $(fx).canvas('getCanvas');
$(canvas).insertAfter(fx);
}
var controls = $('<div class="uk-form-row uk-width-3-4 uk-float-left"><label class="uk-form-label uk-width-7-10 uk-text-left uk-text-bold">' + tinyMCEPopup.getLang('dlg.fx_' + k, k) + '</label><div class="uk-width-3-10"><input type="number" class="uk-width-1-1" value="" /></div><div class="uk-width-1-1 uk-margin-small-top"><input type="range" class="uk-width-1-1" value="" /></div></div>').insertAfter($(fx).parent());
$('input[type="number"], input[type="range"]', controls).on('change', function (event) {
var x = parseInt(this.value);
$('input', controls).not(this).val(x);
debounceApply(filter, v.factor * x);
});
$('input[type="range"]', controls).on('input', function (event) {
$('input[type="number"]', controls).val(parseInt(this.value));
});
$.each(v, function (attr, value) {
if (attr === "preview" || attr === "factor" || attr === "filter") {
return;
}
$('input[type="number"], input[type="range"]', controls).attr(attr, value);
});
});
$.each({
'grayscale': 1,
'invert': 1,
'sepia': 1,
'polaroid': 1,
'vintage': 1,
'brownie': 1,
'kodachrome': 1,
'technicolor': 1
}, function (k, v) {
var canvas, fx = $img.clone().addClass('uk-responsive-width');
fx.appendTo('#editor_effects').wrap('<div class="editor_effect uk-width-1-3 uk-flex uk-flex-column uk-margin-top" role="button" aria-label="' + tinyMCEPopup.getLang('dlg.fx_' + k, k) + '"></div>').after('<span class="uk-label uk-text-small">' + tinyMCEPopup.getLang('dlg.fx_' + k, k) + '</span>').wrap('<div class="editor_effect_preview" role="presentation"></div>');
fx.on('load', function () {
if ($.support.filter && $.inArray(k, ['grayscale', 'invert', 'sepia']) >= 0) {
$(fx).show().cssFilter({ 'filter': k });
canvas = fx;
} else {
$(fx).canvas();
$(fx).canvas('filter', k);
canvas = $(fx).canvas('getCanvas');
$(canvas).insertAfter(fx);
}
$(canvas).on('click', function () {
self._applyFx(k, v);
});
});
});
},
_resetFX: function () {
$('input[type="range"], input[type="number"]', '#editor_effects').val(0);
},
_resizeWin: function () {
//this.position();
},
_initTransform: function (fn) {
var img = $('img', '#editor-image').get(0);
var canvas = $(img).canvas('getCanvas');
this.position();
switch (fn) {
case 'resize':
$(canvas).resize({
width: canvas.width,
height: canvas.height,
ratio: getRatio(canvas), // always proportionate
resize: function (e, size) {
$('#resize_width').val(size.width).data('tmp', size.width);
$('#resize_height').val(size.height).data('tmp', size.height);
},
stop: function () {
$('#resize_reset').prop('disabled', false);
}
});
break;
case 'crop':
$(canvas).crop({
width: canvas.width,
height: canvas.height,
ratio: $('#crop_constrain').is(':checked') ? getRatio(canvas) : false,
clone: $(img).canvas('copy'),
start: function () {
$('#crop_presets').val("");
},
stop: function (e, props) {
$('#crop_reset').prop('disabled', false);
$('#crop_presets').val(props.width + 'x' + props.height);
},
change: function (e, props) {
// null width and height if dragged
if (e.originalEvent && e.originalEvent.type == 'drag') {
props.width = props.height = null;
}
$(e.target).trigger('update', props);
},
reset: function (e, props) {
$(e.target).trigger('update', props);
}
}).on('update', function (e, props) {
$('#crop_width').val(function (i, value) {
return props.width || value;
});
$('#crop_height').val(function (i, value) {
return props.height || value;
});
$('#crop_x').val(props.x);
$('#crop_y').val(props.y);
});
break;
}
},
_resetTransform: function (fn) {
var self = this, img = $('img', '#editor-image').get(0),
canvas = $(img).canvas('getCanvas');
var w = canvas.width || $(canvas).width();
var h = canvas.height || $(canvas).height();
switch (fn) {
case 'resize':
this.position();
if ($.data(canvas, 'uiResize')) {
$(canvas).resize("reset");
$(canvas).resize("remove");
self._initTransform('resize');
}
$('#resize_reset').prop('disabled', true);
$('#resize_width').val(w).data('tmp', w);
$('#resize_height').val(h).data('tmp', h);
$('#resize_presets').val($('#resize_presets option:first').val());
break;
case 'crop':
if ($.data(canvas, 'uiCrop')) {
$(canvas).crop("reset");
}
$('#crop_reset').prop('disabled', true);
$('#crop_presets').val($('#crop_presets option:first').val());
$('#crop_width').val(w).data('tmp', w);
$('#crop_height').val(h).data('tmp', h);
// reset x & y
$('#crop_x, #crop_y').val(0);
break;
}
},
updateCSSTransform: function (k, v) {
$('#rotate_angle img, #rotate_flip img, #editor_effects img, #editor_effects canvas').cssTransform(k, v);
},
undoCSSTransform: function (revert) {
// remove css transform
var keys = ['transform', 'msTransform', 'Transform', 'WebkitTransform', 'OTransform'];
$('#rotate_angle img, #rotate_flip img').each(function () {
var n = $(this).get(0);
$.each(keys, function (i, s) {
var transforms = n.style[s] || [];
if (transforms.length) {
transforms = transforms.split(' ');
}
if (revert) {
transforms = [transforms.shift()];
} else {
transforms.pop();
}
n.style[s] = transforms.join(' ');
});
});
$('#editor_effects img, #editor_effects canvas').each(function () {
var n = $(this).get(0);
$.each(keys, function (i, s) {
var transforms = n.style[s] || [];
if (transforms.length) {
transforms = transforms.split(' ');
}
if (revert) {
transforms = [];
} else {
transforms.pop();
}
n.style[s] = transforms.join(' ');
});
});
},
undo: function (e) {
var data = this.stack.pop();
$('img', '#editor-image').canvas('undo');
if (!this.stack.length) {
$('button.undo, button.revert, button.save').prop('disabled', true);
}
if (data.task === 'resize' || data.task === 'crop') {
this.reset(true);
this._initTransform(data.task);
} else {
this.position();
if (e) {
this._resetFX();
}
}
},
revert: function (e) {
var $img = $('img', '#editor-image'),
img = $img.get(0);
$img.canvas('clear').canvas('draw', img, img.width, img.height);
this.stack = [];
$('button.undo, button.revert, button.save').prop('disabled', true);
if (e) {
this._resetFX();
}
this.reset(true);
},
reset: function (rw) {
var self = this,
$img = $('img', '#editor-image'),
canvas = $img.canvas('getCanvas');
$.each(['resize', 'crop', 'rotate'], function (i, fn) {
self._resetTransform(fn);
});
if (rw) {
if ($.data(canvas, 'uiResize')) {
$(canvas).resize("remove");
}
if ($.data(canvas, 'uiCrop')) {
$(canvas).crop("remove");
}
if ($.data(canvas, 'uiRotate')) {
$(canvas).rotate("remove");
}
}
//$('#transform_tab').trigger('accordion:reset');
this.position();
},
position: function () {
var $img = $('img', '#editor-image'),
canvas = $img.canvas('getCanvas');
var pw = $('#editor-image').width() - 20,
ph = $('#editor-image').height() - 20,
w, h;
var pct = 10;
$(canvas).css({
width: '',
height: ''
});
if ($(canvas).width() > pw) {
w = Math.round(pw - (pw / 100 * pct));
h = Math.round(canvas.height * (w / canvas.width));
$(canvas).width(w).height(h);
pct += 10;
}
if ($(canvas).height() > ph) {
h = Math.round(ph - (ph / 100 * pct));
w = Math.round(canvas.width * (h / canvas.height));
$(canvas).height(h).width(w);
pct += 10;
}
//$('#editor_image_slider input[type="range"]').val(Math.round(w / pw * 100, 1));
var ch = $(canvas).height() || canvas.height;
$(canvas).css({
'top': (ph - ch) / 2
});
},
_apply: function (k, v) {
var self = this,
deferred = $.Deferred(),
$img = $('img', '#editor-image');
var name = Wf.String.basename(self.src);
var src = tinyMCEPopup.getWindowArg('src');
var data = $img.canvas('output', self.getMime(name), 100, true);
self.sendBinary(data, { "method": "applyImageEdit", "id": uid(), "params": [src, k, v] }).then(function (o) {
if (o.files) {
var img = new Image();
img.onload = function () {
$img.canvas('draw', img, img.width, img.height);
self.position();
self._removeLoader();
// clean temp file
cleanTemp(src);
deferred.resolve();
return true;
};
img.onerror = function () {
self._removeLoader();
Wf.Modal.alert('Action "' + k + '" failed. Temp image could not be loaded.');
// clean temp file
cleanTemp(src);
deferred.reject();
return false;
};
var tmp = o.files[0] || '';
// clean tmp
tmp = tmp.replace(/[^\w\.\-\/\\\\\s ]/gi, '');
if (!tmp) {
// clean temp file
cleanTemp(src);
return false;
}
// load into image
img.src = self._loadImage(Wf.String.path(Wf.getURI(), tmp));
}
}).fail(function (s) {
if (s) {
Wf.Modal.alert(s);
}
// reject
deferred.reject();
// clean temp file
cleanTemp(src);
return false;
}).always(function () {
self._removeLoader();
});
function cleanTemp(src) {
Wf.JSON.request('cleanEditorTmp', {
'json': [src]
});
return;
}
return deferred;
},
_applyFx: function () {
var self = this,
length = this.stack.length,
$img = $('img', '#editor-image');
var args = $.makeArray(arguments);
var filter = args.shift();
var amount = args.shift();
self._setLoader();
// remove last filter if the same
if (length) {
var last = this.stack[length - 1];
if (args && last.task === filter) {
this.undo();
}
}
self.addUndo({
task: filter,
args: amount
});
$img.canvas('filter', filter, amount, true);
this._removeLoader();
$('button.undo, button.revert, button.save').prop('disabled', false);
},
addUndo: function (data) {
this.stack.push(data);
},
_applyTransform: function () {
var self = this,
$img = $('img', '#editor-image');
var args = $.makeArray(arguments);
var fn = args.shift();
var amount = args.shift();
self._setLoader();
// update buttons etc. when transform is finished
function done(fn) {
$('button.undo, button.revert, button.save').prop('disabled', false);
self.reset(true);
self._initTransform(fn);
}
switch (fn) {
case 'resize':
var w = $('#resize_width').val();
var h = $('#resize_height').val();
this._apply('resize', {
width: w,
height: h
}).then(function () {
args = [w, h];
self.addUndo({
task: fn,
args: args
});
}).always(function () {
done(fn);
});
break;
case 'crop':
var s = {
width: $('#crop_width').val(),
height: $('#crop_height').val(),
x: $('#crop_x').val(),
y: $('#crop_y').val()
};
this._apply('crop', s).then(function () {
args = [s.width, s.height, s.x, s.y];
self.addUndo({
task: fn,
args: args
});
}).always(function () {
done(fn);
});
break;
case 'rotate':
$img.canvas('rotate', amount, true);
self.position();
self._removeLoader();
this.addUndo({
task: fn,
args: amount
});
done(fn);
break;
case 'flip':
$img.canvas('flip', amount, true);
self.position();
self._removeLoader();
this.addUndo({
task: fn,
args: amount
});
done(fn);
break;
}
},
getMime: function (s) {
var mime = 'image/jpeg';
var ext = Wf.String.getExt(s);
switch (ext) {
case 'jpg':
case 'jpeg':
mime = 'image/jpeg';
break;
case 'png':
mime = 'image/png';
break;
case 'webp':
mime = 'image/webp';
break;
case 'bmp':
mime = 'image/bmp';
break;
}
return mime;
},
/**
* Create save stack
*/
save: function (name) {
var self = this,
$img = $('img', '#editor-image');
var extras = '<div class="uk-form-row uk-grid"><label for="image_quality" class="uk-form-label uk-width-3-10">' + tinyMCEPopup.getLang('dlg.quality', 'Quality') + '</label><div class="uk-form-controls uk-width-7-10 uk-margin-remove"><input type="range" min="1" max="100" id="image_quality_slider" value="100" class="uk-width-3-5" /><input type="number" id="image_quality" min="1" max="100" value="100" class="quality" /> %</div></div>';
var name = Wf.String.basename(this.src);
name = Wf.String.stripExt(name);
var ext = Wf.String.getExt(this.src);
// get filebrowser options
var options = FileBrowser.options;
Wf.Modal.prompt(tinyMCEPopup.getLang('dlg.save_image', 'Save Image'), function (name) {
// clean up name
name = Wf.String.safe(name, options.websafe_mode, options.websafe_spaces, options.websafe_textcase);
// get quality value
var quality = $('#image_quality').val() || 100;
// set loading message
self._setLoader();
// create new name
name = (name + '.' + ext) || Wf.String.basename(self.src);
// get original image source
var src = tinyMCEPopup.getWindowArg('src');
var data = $img.canvas('output', self.getMime(name), quality, true);
if (data) {
self.sendBinary(data, { 'method': 'saveImageEdit', 'id': uid(), 'params': [src, name] }).then(function (o) {
if (o.files) {
self.src = o.files[0] || '';
if (!self.src || !self._validatePath(self.src)) {
Wf.Modal.alert('Invalid image file');
return false;
}
var img = new Image();
img.onload = function () {
// refresh image and reset
$('img', '#editor-image').attr('src', img.src).on('load', function () {
self._createFX();
$(this).canvas('draw', img, img.width, img.height);
});
};
img.src = self._loadImage(Wf.getURI() + self.src);
var s = self.settings;
// fire onsave callback
s.onsave.apply(s.scope || self, [self.src]);
// clear stack
self.stack = [];
// disable undo / revert
$('button.undo, button.revert, button.save').prop('disabled', true);
}
}).fail(function (s) {
Wf.Modal.alert(s);
}).always(function () {
self._removeLoader();
});
}
}, {
text: tinyMCEPopup.getLang('dlg.name', 'Name'),
elements: extras,
height: 240,
value: name,
open: function () {
$('#dialog-prompt-input').parent().addClass('uk-form-icon uk-form-icon-flip').append('<span class="uk-text-muted uk-icon-none">.' + ext + '</span>');
$('#image_quality_slider').on('change', function () {
$('#image_quality').val(this.value);
});
$('#image_quality').on('change', function () {
$('#image_quality_slider').val(this.value);
});
},
validate: function (value) {
if (!value) {
return false;
}
return Wf.String.safe(value, options.websafe_mode, options.websafe_spaces, options.websafe_textcase);
}
});
},
/**
* Send image data as binary blob
* @param {string} data Image data
* @param {object} json JSON object
* @param {function} cb Callback
* @returns {void}
*/
sendBinary: function (data, json, cb) {
var ed = tinyMCEPopup.editor,
deferred = $.Deferred();
// get url
var url = document.location.href;
// strip token
url = url.replace(/&wf([a-z0-9]+)=1/, '');
// add query string
url += '&' + ed.settings.query;
var fd = new FormData();
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
// set request header
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onload = function () {
var r = {},
error = "An error occured processing this image.";
// clean up
data = fd = null;
if (this.status == 200) {
try {
r = JSON.parse(this.response);
} catch (e) {
deferred.reject('The server returned an invalid JSON response.');
return false;
}
}
// process object result
if ($.isPlainObject(r)) {
if (r.error) {
deferred.reject(r.error.message || error);
return false;
}
if (!r.result) {
deferred.reject(error);
return false;
}
deferred.resolve(r.result);
// show error
} else {
// check for malformed JSON
if (/[{}]/.test(r)) {
error = 'The server returned an invalid JSON response.';
}
deferred.reject(error);
}
};
// append json data
fd.append('json', JSON.stringify(json));
var name = Wf.String.basename(json.params[0]);
// append file
fd.append("file", data, name);
xhr.send(fd);
return deferred;
},
/**
*Check if the path is local and /or a valid local file url
*/
_validatePath: function (s) {
function _toUnicode(c) {
c = c.toString(16).toUpperCase();
while (c.length < 4) {
c = '0' + c;
}
return '\\u' + c;
}
// contains .. or is not local
if (/\.{2,}/.test(s) || (/:\/\//.test(s) && s.indexOf(Wf.getURI(true)) == -1)) {
return false;
}
// make relative if an absolute local file
if (/:\/\//.test(s)) {
s = Wf.URL.toRelative(s);
}
// contains non-standard characters
if (/[^\w~\.\-\s \/\\\\]/i.test(s)) {
for (var i = 0, ln = s.length; i < ln; i++) {
var ch = s[i];
// only process on possible restricted characters or utf-8 letters/numbers
if (/[^\w~\.\-\s \/\\\\]/i.test(ch)) {
// return false on character less than 127, eg: &?@* etc.
if (_toUnicode(ch.charCodeAt(0)) < '\\u007F') {
return false;
}
}
}
}
return true;
},
/**
* Create image source
* @param {string} src
* @returns {String}
*/
_loadImage: function (src) {
return src + '?' + new Date().getTime();
}
};
window.ImageEditor = ImageEditor;
})(jQuery, Wf);
})();