AnonSec Shell
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 :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/coopiak/amisdesseniors-fr/media/plg_system_jcepro/editor/js/editor/image.min.js
(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);

})();

Anon7 - 2022
AnonSec Team