// Last time updated: 2019-02-08 7:50:22 AM UTC

// _______________
// Canvas-Designer

// https://github.com/muaz-khan/Canvas-Designer

'use strict';

(function() {

    var is = {
        isLine: false,
        isArrow: false,
        isArc: false,
        isDragLastPath: false,
        isDragAllPaths: false,
        isRectangle: false,
        isQuadraticCurve: false,
        isBezierCurve: false,
        isPencil: false,
        isMarker: true,
        isEraser: false,
        isText: false,
        isImage: false,
        isPdf: false,

        set: function(shape) {
            var cache = this;

            cache.isLine = cache.isArrow = cache.isArc = cache.isDragLastPath = cache.isDragAllPaths = cache.isRectangle = cache.isQuadraticCurve = cache.isBezierCurve = cache.isPencil = cache.isMarker = cache.isEraser = cache.isText = cache.isImage = cache.isPdf = false;
            cache['is' + shape] = true;
        }
    };

    function addEvent(element, eventType, callback) {
        if (eventType.split(' ').length > 1) {
            var events = eventType.split(' ');
            for (var i = 0; i < events.length; i++) {
                addEvent(element, events[i], callback);
            }
            return;
        }

        if (element.addEventListener) {
            element.addEventListener(eventType, callback, !1);
            return true;
        } else if (element.attachEvent) {
            return element.attachEvent('on' + eventType, callback);
        } else {
            element['on' + eventType] = callback;
        }
        return this;
    }

    function find(selector) {
        return document.getElementById(selector);
    }

    var points = [],
        textarea = find('code-text'),
        lineWidth = 2,
        strokeStyle = '#6c96c8',
        fillStyle = 'rgba(0,0,0,0)',
        globalAlpha = 1,
        globalCompositeOperation = 'source-over',
        lineCap = 'round',
        font = '15px "Arial"',
        lineJoin = 'round';

    function getContext(id) {
        var canv = find(id),
            ctx = canv.getContext('2d');

        canv.setAttribute('width', innerWidth);
        canv.setAttribute('height', innerHeight);

        ctx.lineWidth = lineWidth;
        ctx.strokeStyle = strokeStyle;
        ctx.fillStyle = fillStyle;
        ctx.font = font;

        return ctx;
    }

    var context = getContext('main-canvas'),
        tempContext = getContext('temp-canvas');

    var common = {
        updateTextArea: function() {
            var c = common,
                toFixed = c.toFixed,
                getPoint = c.getPoint,

                isAbsolutePoints = find('is-absolute-points').checked,
                isShortenCode = find('is-shorten-code').checked;

            if (isAbsolutePoints && isShortenCode) c.absoluteShortened();
            if (isAbsolutePoints && !isShortenCode) c.absoluteNOTShortened(toFixed);
            if (!isAbsolutePoints && isShortenCode) c.relativeShortened(toFixed, getPoint);
            if (!isAbsolutePoints && !isShortenCode) c.relativeNOTShortened(toFixed, getPoint);
        },
        toFixed: function(input) {
            return Number(input).toFixed(1);
        },
        getPoint: function(pointToCompare, compareWith, prefix) {
            if (pointToCompare > compareWith) pointToCompare = prefix + ' + ' + (pointToCompare - compareWith);
            else if (pointToCompare < compareWith) pointToCompare = prefix + ' - ' + (compareWith - pointToCompare);
            else pointToCompare = prefix;

            return pointToCompare;
        },
        absoluteShortened: function() {
            var output = '',
                length = points.length,
                i = 0,
                point;
            for (i; i < length; i++) {
                point = points[i];
                output += this.shortenHelper(point[0], point[1], point[2]);
            }

            output = output.substr(0, output.length - 2);
            textarea.value = 'var points = [' + output + '], length = points.length, point, p, i = 0;\n\n' + drawArrow.toString() + '\n\n' + this.forLoop;

            this.prevProps = null;
        },
        absoluteNOTShortened: function(toFixed) {
            var tempArray = [],
                i, point, p;

            for (i = 0; i < points.length; i++) {
                p = points[i];
                point = p[1];

                if (p[0] === 'pencil') {
                    tempArray[i] = ['context.beginPath();\n' + 'context.moveTo(' + point[0] + ', ' + point[1] + ');\n' + 'context.lineTo(' + point[2] + ', ' + point[3] + ');\n' + this.strokeOrFill(p[2])];
                }

                if (p[0] === 'marker') {
                    tempArray[i] = ['context.beginPath();\n' + 'context.moveTo(' + point[0] + ', ' + point[1] + ');\n' + 'context.lineTo(' + point[2] + ', ' + point[3] + ');\n' + this.strokeOrFill(p[2])];
                }

                if (p[0] === 'eraser') {
                    tempArray[i] = ['context.beginPath();\n' + 'context.moveTo(' + point[0] + ', ' + point[1] + ');\n' + 'context.lineTo(' + point[2] + ', ' + point[3] + ');\n' + this.strokeOrFill(p[2])];
                }

                if (p[0] === 'line') {
                    tempArray[i] = ['context.beginPath();\n' + 'context.moveTo(' + point[0] + ', ' + point[1] + ');\n' + 'context.lineTo(' + point[2] + ', ' + point[3] + ');\n' + this.strokeOrFill(p[2])];
                }

                if (p[0] === 'pencil') {
                    tempArray[i] = ['context.beginPath();\n' + 'context.moveTo(' + point[0] + ', ' + point[1] + ');\n' + 'context.lineTo(' + point[2] + ', ' + point[3] + ');\n' + this.strokeOrFill(p[2])];
                }

                if (p[0] === 'text') {
                    tempArray[i] = [this.strokeOrFill(p[2]) + '\ncontext.fillText(' + point[0] + ', ' + point[1] + ', ' + point[2] + ');'];
                }

                if (p[0] === 'arrow') {
                    tempArray[i] = ['drawArrow(' + point[0] + ', ' + point[1] + ', ' + point[2] + ', ' + point[3] + ', \'' + p[2].join('\',\'') + '\');'];
                }

                if (p[0] === 'arc') {
                    tempArray[i] = ['context.beginPath(); \n' + 'context.arc(' + toFixed(point[0]) + ',' + toFixed(point[1]) + ',' + toFixed(point[2]) + ',' + toFixed(point[3]) + ', 0,' + point[4] + '); \n' + this.strokeOrFill(p[2])];
                }

                if (p[0] === 'rect') {
                    tempArray[i] = [this.strokeOrFill(p[2]) + '\n' + 'context.strokeRect(' + point[0] + ', ' + point[1] + ',' + point[2] + ',' + point[3] + ');\n' + 'context.fillRect(' + point[0] + ', ' + point[1] + ',' + point[2] + ',' + point[3] + ');'];
                }

                if (p[0] === 'quadratic') {
                    tempArray[i] = ['context.beginPath();\n' + 'context.moveTo(' + point[0] + ', ' + point[1] + ');\n' + 'context.quadraticCurveTo(' + point[2] + ', ' + point[3] + ', ' + point[4] + ', ' + point[5] + ');\n' + this.strokeOrFill(p[2])];
                }

                if (p[0] === 'bezier') {
                    tempArray[i] = ['context.beginPath();\n' + 'context.moveTo(' + point[0] + ', ' + point[1] + ');\n' + 'context.bezierCurveTo(' + point[2] + ', ' + point[3] + ', ' + point[4] + ', ' + point[5] + ', ' + point[6] + ', ' + point[7] + ');\n' + this.strokeOrFill(p[2])];
                }

            }
            textarea.value = tempArray.join('\n\n') + this.strokeFillText + '\n\n' + drawArrow.toString();

            this.prevProps = null;
        },
        relativeShortened: function(toFixed, getPoint) {
            var i = 0,
                point, p, length = points.length,
                output = '',
                x = 0,
                y = 0;

            for (i; i < length; i++) {
                p = points[i];
                point = p[1];

                if (i === 0) {
                    x = point[0];
                    y = point[1];
                }

                if (p[0] === 'text') {
                    x = point[1];
                    y = point[2];
                }

                if (p[0] === 'pencil') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'marker') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'eraser') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'line') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'pencil') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'arrow') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'text') {
                    output += this.shortenHelper(p[0], [
                        point[0],
                        getPoint(point[1], x, 'x'),
                        getPoint(point[2], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'arc') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        point[2],
                        point[3],
                        point[4]
                    ], p[2]);
                }

                if (p[0] === 'rect') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'quadratic') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y'),
                        getPoint(point[4], x, 'x'),
                        getPoint(point[5], y, 'y')
                    ], p[2]);
                }

                if (p[0] === 'bezier') {
                    output += this.shortenHelper(p[0], [
                        getPoint(point[0], x, 'x'),
                        getPoint(point[1], y, 'y'),
                        getPoint(point[2], x, 'x'),
                        getPoint(point[3], y, 'y'),
                        getPoint(point[4], x, 'x'),
                        getPoint(point[5], y, 'y'),
                        getPoint(point[6], x, 'x'),
                        getPoint(point[7], y, 'y')
                    ], p[2]);
                }
            }

            output = output.substr(0, output.length - 2);
            textarea.value = 'var x = ' + x + ', y = ' + y + ', points = [' + output + '], length = points.length, point, p, i = 0;\n\n' + drawArrow.toString() + '\n\n' + this.forLoop;

            this.prevProps = null;
        },
        relativeNOTShortened: function(toFixed, getPoint) {
            var i, point, p, length = points.length,
                output = '',
                x = 0,
                y = 0;

            for (i = 0; i < length; i++) {
                p = points[i];
                point = p[1];

                if (i === 0) {
                    x = point[0];
                    y = point[1];

                    if (p[0] === 'text') {
                        x = point[1];
                        y = point[2];
                    }

                    output = 'var x = ' + x + ', y = ' + y + ';\n\n';
                }

                if (p[0] === 'arc') {
                    output += 'context.beginPath();\n' + 'context.arc(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ', ' + point[2] + ', ' + point[3] + ', 0, ' + point[4] + ');\n'

                        +
                        this.strokeOrFill(p[2]);
                }

                if (p[0] === 'pencil') {
                    output += 'context.beginPath();\n' + 'context.moveTo(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ');\n' + 'context.lineTo(' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ');\n'

                        +
                        this.strokeOrFill(p[2]);
                }

                if (p[0] === 'marker') {
                    output += 'context.beginPath();\n' + 'context.moveTo(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ');\n' + 'context.lineTo(' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ');\n'

                        +
                        this.strokeOrFill(p[2]);
                }

                if (p[0] === 'eraser') {
                    output += 'context.beginPath();\n' + 'context.moveTo(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ');\n' + 'context.lineTo(' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ');\n'

                        +
                        this.strokeOrFill(p[2]);
                }

                if (p[0] === 'line') {
                    output += 'context.beginPath();\n' + 'context.moveTo(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ');\n' + 'context.lineTo(' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ');\n' + this.strokeOrFill(p[2]);
                }

                if (p[0] === 'pencil') {
                    output += 'context.beginPath();\n' + 'context.moveTo(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ');\n' + 'context.lineTo(' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ');\n' + this.strokeOrFill(p[2]);
                }

                if (p[0] === 'arrow') {
                    output += 'drawArrow(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ', ' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ', \'' + p[2].join('\',\'') + '\');\n';
                }

                if (p[0] === 'text') {
                    output += this.strokeOrFill(p[2]) + '\n' + 'context.fillText(' + point[0] + ', ' + getPoint(point[1], x, 'x') + ', ' + getPoint(point[2], y, 'y') + ');';
                }

                if (p[0] === 'rect') {
                    output += this.strokeOrFill(p[2]) + '\n' + 'context.strokeRect(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ', ' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ');\n' + 'context.fillRect(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ', ' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ');';
                }

                if (p[0] === 'quadratic') {
                    output += 'context.beginPath();\n' + 'context.moveTo(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ');\n' + 'context.quadraticCurveTo(' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ', ' + getPoint(point[4], x, 'x') + ', ' + getPoint(point[5], y, 'y') + ');\n'

                        +
                        this.strokeOrFill(p[2]);
                }

                if (p[0] === 'bezier') {
                    output += 'context.beginPath();\n' + 'context.moveTo(' + getPoint(point[0], x, 'x') + ', ' + getPoint(point[1], y, 'y') + ');\n' + 'context.bezierCurveTo(' + getPoint(point[2], x, 'x') + ', ' + getPoint(point[3], y, 'y') + ', ' + getPoint(point[4], x, 'x') + ', ' + getPoint(point[5], y, 'y') + ', ' + getPoint(point[6], x, 'x') + ', ' + getPoint(point[7], y, 'y') + ');\n'

                        +
                        this.strokeOrFill(p[2]);
                }

                if (i !== length - 1) output += '\n\n';
            }
            textarea.value = output + this.strokeFillText + '\n\n' + drawArrow.toString();

            this.prevProps = null;
        },
        forLoop: 'for(i; i < length; i++) {\n' + '    p = points[i];\n' + '    point = p[1];\n' + '    context.beginPath();\n\n'

            // globals
            +
            '    if(p[2]) { \n' + '\tcontext.lineWidth = p[2][0];\n' + '\tcontext.strokeStyle = p[2][1];\n' + '\tcontext.fillStyle = p[2][2];\n'

            +
            '\tcontext.globalAlpha = p[2][3];\n' + '\tcontext.globalCompositeOperation = p[2][4];\n' + '\tcontext.lineCap = p[2][5];\n' + '\tcontext.lineJoin = p[2][6];\n' + '\tcontext.font = p[2][7];\n' + '    }\n\n'

            // line

            +
            '    if(p[0] === "line") { \n' + '\tcontext.moveTo(point[0], point[1]);\n' + '\tcontext.lineTo(point[2], point[3]);\n' + '    }\n\n'

            // arrow

            +
            '    if(p[0] === "arrow") { \n' + '\tdrawArrow(point[0], point[1], point[2], point[3], p[2]);\n' + '    }\n\n'

            // pencil

            +
            '    if(p[0] === "pencil") { \n' + '\tcontext.moveTo(point[0], point[1]);\n' + '\tcontext.lineTo(point[2], point[3]);\n' + '    }\n\n'

            // marker

            +
            '    if(p[0] === "marker") { \n' + '\tcontext.moveTo(point[0], point[1]);\n' + '\tcontext.lineTo(point[2], point[3]);\n' + '    }\n\n'


            // text

            +
            '    if(p[0] === "text") { \n' + '\tcontext.fillText(point[0], point[1], point[2]);\n' + '    }\n\n'

            // eraser

            +
            '    if(p[0] === "eraser") { \n' + '\tcontext.moveTo(point[0], point[1]);\n' + '\tcontext.lineTo(point[2], point[3]);\n' + '    }\n\n'

            // arc

            +
            '    if(p[0] === "arc") context.arc(point[0], point[1], point[2], point[3], 0, point[4]); \n\n'

            // rect

            +
            '    if(p[0] === "rect") {\n' + '\tcontext.strokeRect(point[0], point[1], point[2], point[3]);\n' + '\tcontext.fillRect(point[0], point[1], point[2], point[3]);\n'

            +
            '    }\n\n'

            // quadratic

            +
            '    if(p[0] === "quadratic") {\n' + '\tcontext.moveTo(point[0], point[1]);\n' + '\tcontext.quadraticCurveTo(point[2], point[3], point[4], point[5]);\n' + '    }\n\n'

            // bezier

            +
            '    if(p[0] === "bezier") {\n' + '\tcontext.moveTo(point[0], point[1]);\n' + '\tcontext.bezierCurveTo(point[2], point[3], point[4], point[5], point[6], point[7]);\n' + '    }\n\n'

            // end-fill

            +
            '    context.stroke();\n' + '    context.fill();\n'

            +
            '}',

        strokeFillText: '\n\nfunction strokeOrFill(lineWidth, strokeStyle, fillStyle, globalAlpha, globalCompositeOperation, lineCap, lineJoin, font) { \n' + '    if(lineWidth) { \n' + '\tcontext.globalAlpha = globalAlpha;\n' + '\tcontext.globalCompositeOperation = globalCompositeOperation;\n' + '\tcontext.lineCap = lineCap;\n' + '\tcontext.lineJoin = lineJoin;\n'

            +
            '\tcontext.lineWidth = lineWidth;\n' + '\tcontext.strokeStyle = strokeStyle;\n' + '\tcontext.fillStyle = fillStyle;\n' + '\tcontext.font = font;\n' + '    } \n\n'

            +
            '    context.stroke();\n' + '    context.fill();\n'

            +
            '}',
        strokeOrFill: function(p) {
            if (!this.prevProps || this.prevProps !== p.join(',')) {
                this.prevProps = p.join(',');

                return 'strokeOrFill(\'' + p.join('\', \'') + '\');';
            }

            return 'strokeOrFill();';
        },
        prevProps: null,
        shortenHelper: function(name, p1, p2) {
            var result = '[\'' + name + '\', [' + p1.join(', ') + ']';

            if (!this.prevProps || this.prevProps !== p2.join(',')) {
                this.prevProps = p2.join(',');
                result += ', [\'' + p2.join('\', \'') + '\']';
            }

            return result + '], ';
        }
    };

    function drawArrow(mx, my, lx, ly, options) {
        function getOptions(opt) {
            opt = opt || {};

            return [
                opt.lineWidth || 2,
                opt.strokeStyle || '#6c96c8',
                opt.fillStyle || 'rgba(0,0,0,0)',
                opt.globalAlpha || 1,
                opt.globalCompositeOperation || 'source-over',
                opt.lineCap || 'round',
                opt.lineJoin || 'round',
                opt.font || '15px "Arial"'
            ];
        }

        function handleOptions(opt, isNoFillStroke) {
            opt = opt || getOptions();

            context.globalAlpha = opt[3];
            context.globalCompositeOperation = opt[4];

            context.lineCap = opt[5];
            context.lineJoin = opt[6];
            context.lineWidth = opt[0];

            context.strokeStyle = opt[1];
            context.fillStyle = opt[2];

            context.font = opt[7];

            if (!isNoFillStroke) {
                context.stroke();
                context.fill();
            }
        }

        var arrowSize = 10;
        var angle = Math.atan2(ly - my, lx - mx);

        context.beginPath();
        context.moveTo(mx, my);
        context.lineTo(lx, ly);

        handleOptions();

        context.beginPath();
        context.moveTo(lx, ly);
        context.lineTo(lx - arrowSize * Math.cos(angle - Math.PI / 7), ly - arrowSize * Math.sin(angle - Math.PI / 7));
        context.lineTo(lx - arrowSize * Math.cos(angle + Math.PI / 7), ly - arrowSize * Math.sin(angle + Math.PI / 7));
        context.lineTo(lx, ly);
        context.lineTo(lx - arrowSize * Math.cos(angle - Math.PI / 7), ly - arrowSize * Math.sin(angle - Math.PI / 7));

        handleOptions();
    }

    function endLastPath() {
        var cache = is;

        if (cache.isArc) arcHandler.end();
        else if (cache.isQuadraticCurve) quadraticHandler.end();
        else if (cache.isBezierCurve) bezierHandler.end();

        drawHelper.redraw();

        if (textHandler.text && textHandler.text.length) {
            textHandler.appendPoints();
            textHandler.onShapeUnSelected();
        }
        textHandler.showOrHideTextTools('hide');
    }

    var copiedStuff = [],
        isControlKeyPressed;

    function copy() {
        endLastPath();

        dragHelper.global.startingIndex = 0;

        if (find('copy-last').checked) {
            copiedStuff = points[points.length - 1];
            setSelection(find('drag-last-path'), 'DragLastPath');
        } else {
            copiedStuff = points;
            setSelection(find('drag-all-paths'), 'DragAllPaths');
        }
    }

    function paste() {
        endLastPath();

        dragHelper.global.startingIndex = 0;

        if (find('copy-last').checked) {
            points[points.length] = copiedStuff;

            dragHelper.global = {
                prevX: 0,
                prevY: 0,
                startingIndex: points.length - 1
            };

            dragHelper.dragAllPaths(0, 0);
            setSelection(find('drag-last-path'), 'DragLastPath');
        } else {

            dragHelper.global.startingIndex = points.length;
            points = points.concat(copiedStuff);
            setSelection(find('drag-all-paths'), 'DragAllPaths');
        }
    }

    // marker + pencil
    function hexToR(h) {
        return parseInt((cutHex(h)).substring(0, 2), 16)
    }

    function hexToG(h) {
        return parseInt((cutHex(h)).substring(2, 4), 16)
    }

    function hexToB(h) {
        return parseInt((cutHex(h)).substring(4, 6), 16)
    }

    function cutHex(h) {
        return (h.charAt(0) == "#") ? h.substring(1, 7) : h
    }

    function clone(obj) {
        if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
            return obj;

        if (obj instanceof Date)
            var temp = new obj.constructor(); //or new Date(obj);
        else
            var temp = obj.constructor();

        for (var key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                obj['isActiveClone'] = null;
                temp[key] = clone(obj[key]);
                delete obj['isActiveClone'];
            }
        }

        return temp;
    }

    function hexToRGB(h) {
        return [
            hexToR(h),
            hexToG(h),
            hexToB(h)
        ]
    }

    var drawHelper = {
        redraw: function() {
            tempContext.clearRect(0, 0, innerWidth, innerHeight);
            context.clearRect(0, 0, innerWidth, innerHeight);

            var i, point, length = points.length;
            for (i = 0; i < length; i++) {
                point = points[i];
                // point[0] != 'pdf' && 
                if (point && point.length && this[point[0]]) {
                    this[point[0]](context, point[1], point[2]);
                }
                // else warn
            }
        },
        getOptions: function(opt) {
            opt = opt || {};
            return [
                opt.lineWidth || lineWidth,
                opt.strokeStyle || strokeStyle,
                opt.fillStyle || fillStyle,
                opt.globalAlpha || globalAlpha,
                opt.globalCompositeOperation || globalCompositeOperation,
                opt.lineCap || lineCap,
                opt.lineJoin || lineJoin,
                opt.font || font
            ];
        },
        handleOptions: function(context, opt, isNoFillStroke) {
            opt = opt || this.getOptions();

            context.globalAlpha = opt[3];
            context.globalCompositeOperation = opt[4];

            context.lineCap = opt[5];
            context.lineJoin = opt[6];
            context.lineWidth = opt[0];

            context.strokeStyle = opt[1];
            context.fillStyle = opt[2];

            context.font = opt[7];

            if (!isNoFillStroke) {
                context.stroke();
                context.fill();
            }
        },
        line: function(context, point, options) {
            context.beginPath();
            context.moveTo(point[0], point[1]);
            context.lineTo(point[2], point[3]);

            this.handleOptions(context, options);
        },
        pencil: function(context, point, options) {
            context.beginPath();
            context.moveTo(point[0], point[1]);
            context.lineTo(point[2], point[3]);

            this.handleOptions(context, options);
        },
        marker: function(context, point, options) {
            context.beginPath();
            context.moveTo(point[0], point[1]);
            context.lineTo(point[2], point[3]);

            this.handleOptions(context, options);
        },
        arrow: function(context, point, options) {
            var mx = point[0];
            var my = point[1];

            var lx = point[2];
            var ly = point[3];

            var arrowSize = arrowHandler.arrowSize;

            if (arrowSize == 10) {
                arrowSize = (options ? options[0] : lineWidth) * 5;
            }

            var angle = Math.atan2(ly - my, lx - mx);

            context.beginPath();
            context.moveTo(mx, my);
            context.lineTo(lx, ly);

            this.handleOptions(context, options);

            context.beginPath();
            context.moveTo(lx, ly);
            context.lineTo(lx - arrowSize * Math.cos(angle - Math.PI / 7), ly - arrowSize * Math.sin(angle - Math.PI / 7));
            context.lineTo(lx - arrowSize * Math.cos(angle + Math.PI / 7), ly - arrowSize * Math.sin(angle + Math.PI / 7));
            context.lineTo(lx, ly);
            context.lineTo(lx - arrowSize * Math.cos(angle - Math.PI / 7), ly - arrowSize * Math.sin(angle - Math.PI / 7));

            this.handleOptions(context, options);
        },
        text: function(context, point, options) {
            this.handleOptions(context, options);
            context.fillStyle = textHandler.getFillColor(options[2]);
            context.fillText(point[0].substr(1, point[0].length - 2), point[1], point[2]);
        },
        arc: function(context, point, options) {
            context.beginPath();
            context.arc(point[0], point[1], point[2], point[3], 0, point[4]);

            this.handleOptions(context, options);
        },
        rect: function(context, point, options) {
            this.handleOptions(context, options, true);

            context.strokeRect(point[0], point[1], point[2], point[3]);
            context.fillRect(point[0], point[1], point[2], point[3]);
        },
        image: function(context, point, options) {
            this.handleOptions(context, options, true);

            var image = imageHandler.images[point[5]];
            if (!image) {
                var image = new Image();
                image.onload = function() {
                    var index = imageHandler.images.length;

                    imageHandler.lastImageURL = image.src;
                    imageHandler.lastImageIndex = index;

                    imageHandler.images.push(image);
                    context.drawImage(image, point[1], point[2], point[3], point[4]);
                };
                image.src = point[0];
                return;
            }

            context.drawImage(image, point[1], point[2], point[3], point[4]);
        },
        pdf: function(context, point, options) {
            this.handleOptions(context, options, true);

            var image = pdfHandler.images[point[5]];
            if (!image) {
                var image = new Image();
                image.onload = function() {
                    var index = imageHandler.images.length;

                    pdfHandler.lastPage = image.src;
                    pdfHandler.lastIndex = index;

                    pdfHandler.images.push(image);
                    context.drawImage(image, point[1], point[2], point[3], point[4]);
                };
                image.src = point[0];
                return;
            }

            context.drawImage(image, point[1], point[2], point[3], point[4]);
            pdfHandler.reset_pos(point[1], point[2]);
        },
        quadratic: function(context, point, options) {
            context.beginPath();
            context.moveTo(point[0], point[1]);
            context.quadraticCurveTo(point[2], point[3], point[4], point[5]);

            this.handleOptions(context, options);
        },
        bezier: function(context, point, options) {
            context.beginPath();
            context.moveTo(point[0], point[1]);
            context.bezierCurveTo(point[2], point[3], point[4], point[5], point[6], point[7]);

            this.handleOptions(context, options);
        }
    };

    var dragHelper = {
        global: {
            prevX: 0,
            prevY: 0,
            ismousedown: false,
            pointsToMove: 'all',
            startingIndex: 0
        },
        mousedown: function(e) {
            if (isControlKeyPressed) {
                copy();
                paste();
                isControlKeyPressed = false;
            }

            var dHelper = dragHelper,
                g = dHelper.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            g.prevX = x;
            g.prevY = y;

            g.pointsToMove = 'all';

            if (points.length) {
                var p = points[points.length - 1],
                    point = p[1];

                if (p[0] === 'line') {

                    if (dHelper.isPointInPath(x, y, point[0], point[1])) {
                        g.pointsToMove = 'head';
                    }

                    if (dHelper.isPointInPath(x, y, point[2], point[3])) {
                        g.pointsToMove = 'tail';
                    }
                }

                if (p[0] === 'pencil') {

                    if (dHelper.isPointInPath(x, y, point[0], point[1])) {
                        g.pointsToMove = 'head';
                    }

                    if (dHelper.isPointInPath(x, y, point[2], point[3])) {
                        g.pointsToMove = 'tail';
                    }
                }

                if (p[0] === 'arrow') {

                    if (dHelper.isPointInPath(x, y, point[0], point[1])) {
                        g.pointsToMove = 'head';
                    }

                    if (dHelper.isPointInPath(x, y, point[2], point[3])) {
                        g.pointsToMove = 'tail';
                    }
                }

                if (p[0] === 'rect') {

                    if (dHelper.isPointInPath(x, y, point[0], point[1])) {
                        g.pointsToMove = 'stretch-first';
                    }

                    if (dHelper.isPointInPath(x, y, point[0] + point[2], point[1])) {
                        g.pointsToMove = 'stretch-second';
                    }

                    if (dHelper.isPointInPath(x, y, point[0], point[1] + point[3])) {
                        g.pointsToMove = 'stretch-third';
                    }

                    if (dHelper.isPointInPath(x, y, point[0] + point[2], point[1] + point[3])) {
                        g.pointsToMove = 'stretch-last';
                    }
                }

                if (p[0] === 'image') {

                    if (dHelper.isPointInPath(x, y, point[1], point[2])) {
                        g.pointsToMove = 'stretch-first';
                    }

                    if (dHelper.isPointInPath(x, y, point[1] + point[3], point[2])) {
                        g.pointsToMove = 'stretch-second';
                    }

                    if (dHelper.isPointInPath(x, y, point[1], point[2] + point[4])) {
                        g.pointsToMove = 'stretch-third';
                    }

                    if (dHelper.isPointInPath(x, y, point[1] + point[3], point[2] + point[4])) {
                        g.pointsToMove = 'stretch-last';
                    }
                }

                if (p[0] === 'pdf') {

                    if (dHelper.isPointInPath(x, y, point[1], point[2])) {
                        g.pointsToMove = 'stretch-first';
                    }

                    if (dHelper.isPointInPath(x, y, point[1] + point[3], point[2])) {
                        g.pointsToMove = 'stretch-second';
                    }

                    if (dHelper.isPointInPath(x, y, point[1], point[2] + point[4])) {
                        g.pointsToMove = 'stretch-third';
                    }

                    if (dHelper.isPointInPath(x, y, point[1] + point[3], point[2] + point[4])) {
                        g.pointsToMove = 'stretch-last';
                    }
                }

                if (p[0] === 'quadratic') {

                    if (dHelper.isPointInPath(x, y, point[0], point[1])) {
                        g.pointsToMove = 'starting-points';
                    }

                    if (dHelper.isPointInPath(x, y, point[2], point[3])) {
                        g.pointsToMove = 'control-points';
                    }

                    if (dHelper.isPointInPath(x, y, point[4], point[5])) {
                        g.pointsToMove = 'ending-points';
                    }
                }

                if (p[0] === 'bezier') {

                    if (dHelper.isPointInPath(x, y, point[0], point[1])) {
                        g.pointsToMove = 'starting-points';
                    }

                    if (dHelper.isPointInPath(x, y, point[2], point[3])) {
                        g.pointsToMove = '1st-control-points';
                    }

                    if (dHelper.isPointInPath(x, y, point[4], point[5])) {
                        g.pointsToMove = '2nd-control-points';
                    }

                    if (dHelper.isPointInPath(x, y, point[6], point[7])) {
                        g.pointsToMove = 'ending-points';
                    }
                }
            }

            g.ismousedown = true;
        },
        mouseup: function() {
            var g = this.global;

            if (is.isDragLastPath) {
                tempContext.clearRect(0, 0, innerWidth, innerHeight);
                context.clearRect(0, 0, innerWidth, innerHeight);
                this.end();
            }

            g.ismousedown = false;
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop,
                g = this.global;

            drawHelper.redraw();

            if (g.ismousedown) {
                this.dragShape(x, y);
            }

            if (is.isDragLastPath) this.init();
        },
        init: function() {
            if (!points.length) return;

            var p = points[points.length - 1],
                point = p[1],
                g = this.global;

            if (g.ismousedown) tempContext.fillStyle = 'rgba(255,85 ,154,.9)';
            else tempContext.fillStyle = 'rgba(255,85 ,154,.4)';

            if (p[0] === 'quadratic') {

                tempContext.beginPath();

                tempContext.arc(point[0], point[1], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[2], point[3], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[4], point[5], 10, Math.PI * 2, 0, !1);

                tempContext.fill();
            }

            if (p[0] === 'bezier') {

                tempContext.beginPath();

                tempContext.arc(point[0], point[1], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[2], point[3], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[4], point[5], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[6], point[7], 10, Math.PI * 2, 0, !1);

                tempContext.fill();
            }

            if (p[0] === 'line') {

                tempContext.beginPath();

                tempContext.arc(point[0], point[1], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[2], point[3], 10, Math.PI * 2, 0, !1);

                tempContext.fill();
            }

            if (p[0] === 'pencil') {

                tempContext.beginPath();

                tempContext.arc(point[0], point[1], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[2], point[3], 10, Math.PI * 2, 0, !1);

                tempContext.fill();
            }

            if (p[0] === 'arrow') {

                tempContext.beginPath();

                tempContext.arc(point[0], point[1], 10, Math.PI * 2, 0, !1);
                tempContext.arc(point[2], point[3], 10, Math.PI * 2, 0, !1);

                tempContext.fill();
            }

            if (p[0] === 'text') {
                tempContext.font = "15px Verdana";
                tempContext.fillText(point[0], point[1], point[2]);
            }

            if (p[0] === 'rect') {

                tempContext.beginPath();
                tempContext.arc(point[0], point[1], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[0] + point[2], point[1], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[0], point[1] + point[3], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[0] + point[2], point[1] + point[3], 10, Math.PI * 2, 0, !1);
                tempContext.fill();
            }

            if (p[0] === 'image') {
                tempContext.beginPath();
                tempContext.arc(point[1], point[2], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[1] + point[3], point[2], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[1], point[2] + point[4], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[1] + point[3], point[2] + point[4], 10, Math.PI * 2, 0, !1);
                tempContext.fill();
            }

            if (p[0] === 'pdf') {
                tempContext.beginPath();
                tempContext.arc(point[1], point[2], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[1] + point[3], point[2], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[1], point[2] + point[4], 10, Math.PI * 2, 0, !1);
                tempContext.fill();

                tempContext.beginPath();
                tempContext.arc(point[1] + point[3], point[2] + point[4], 10, Math.PI * 2, 0, !1);
                tempContext.fill();
            }
        },
        isPointInPath: function(x, y, first, second) {
            return x > first - 10 && x < first + 10 && y > second - 10 && y < second + 10;
        },
        getPoint: function(point, prev, otherPoint) {
            if (point > prev) {
                point = otherPoint + (point - prev);
            } else {
                point = otherPoint - (prev - point);
            }

            return point;
        },
        getXYWidthHeight: function(x, y, prevX, prevY, oldPoints) {
            if (oldPoints.pointsToMove == 'stretch-first') {
                if (x > prevX) {
                    oldPoints.x = oldPoints.x + (x - prevX);
                    oldPoints.width = oldPoints.width - (x - prevX);
                } else {
                    oldPoints.x = oldPoints.x - (prevX - x);
                    oldPoints.width = oldPoints.width + (prevX - x);
                }

                if (y > prevY) {
                    oldPoints.y = oldPoints.y + (y - prevY);
                    oldPoints.height = oldPoints.height - (y - prevY);
                } else {
                    oldPoints.y = oldPoints.y - (prevY - y);
                    oldPoints.height = oldPoints.height + (prevY - y);
                }
            }

            if (oldPoints.pointsToMove == 'stretch-second') {
                if (x > prevX) {
                    oldPoints.width = oldPoints.width + (x - prevX);
                } else {
                    oldPoints.width = oldPoints.width - (prevX - x);
                }

                if (y < prevY) {
                    oldPoints.y = oldPoints.y + (y - prevY);
                    oldPoints.height = oldPoints.height - (y - prevY);
                } else {
                    oldPoints.y = oldPoints.y - (prevY - y);
                    oldPoints.height = oldPoints.height + (prevY - y);
                }
            }

            if (oldPoints.pointsToMove == 'stretch-third') {
                if (x > prevX) {
                    oldPoints.x = oldPoints.x + (x - prevX);
                    oldPoints.width = oldPoints.width - (x - prevX);
                } else {
                    oldPoints.x = oldPoints.x - (prevX - x);
                    oldPoints.width = oldPoints.width + (prevX - x);
                }

                if (y < prevY) {
                    oldPoints.height = oldPoints.height + (y - prevY);
                } else {
                    oldPoints.height = oldPoints.height - (prevY - y);
                }
            }

            return oldPoints;
        },
        dragShape: function(x, y) {
            if (!this.global.ismousedown) return;

            tempContext.clearRect(0, 0, innerWidth, innerHeight);

            if (is.isDragLastPath) {
                this.dragLastPath(x, y);
            }

            if (is.isDragAllPaths) {
                this.dragAllPaths(x, y);
            }

            var g = this.global;

            g.prevX = x;
            g.prevY = y;
        },
        end: function() {
            if (!points.length) return;

            tempContext.clearRect(0, 0, innerWidth, innerHeight);

            var point = points[points.length - 1];
            drawHelper[point[0]](context, point[1], point[2]);
        },
        dragAllPaths: function(x, y) {
            var g = this.global,
                prevX = g.prevX,
                prevY = g.prevY,
                p, point,
                length = points.length,
                getPoint = this.getPoint,
                i = g.startingIndex;

            for (i; i < length; i++) {
                p = points[i];
                point = p[1];

                if (p[0] === 'line') {
                    points[i] = [p[0],
                        [
                            getPoint(x, prevX, point[0]),
                            getPoint(y, prevY, point[1]),
                            getPoint(x, prevX, point[2]),
                            getPoint(y, prevY, point[3])
                        ], p[2]
                    ];
                }

                if (p[0] === 'pencil') {
                    points[i] = [p[0],
                        [
                            getPoint(x, prevX, point[0]),
                            getPoint(y, prevY, point[1]),
                            getPoint(x, prevX, point[2]),
                            getPoint(y, prevY, point[3])
                        ], p[2]
                    ];
                }

                if (p[0] === 'arrow') {
                    points[i] = [p[0],
                        [
                            getPoint(x, prevX, point[0]),
                            getPoint(y, prevY, point[1]),
                            getPoint(x, prevX, point[2]),
                            getPoint(y, prevY, point[3])
                        ], p[2]
                    ];
                }

                if (p[0] === 'text') {
                    points[i] = [p[0],
                        [
                            point[0],
                            getPoint(x, prevX, point[1]),
                            getPoint(y, prevY, point[2])
                        ], p[2]
                    ];
                }

                if (p[0] === 'arc') {
                    points[i] = [p[0],
                        [
                            getPoint(x, prevX, point[0]),
                            getPoint(y, prevY, point[1]),
                            point[2],
                            point[3],
                            point[4]
                        ], p[2]
                    ];
                }

                if (p[0] === 'rect') {
                    points[i] = [p[0],
                        [
                            getPoint(x, prevX, point[0]),
                            getPoint(y, prevY, point[1]),
                            point[2],
                            point[3]
                        ], p[2]
                    ];
                }

                if (p[0] === 'image') {
                    points[i] = [p[0],
                        [
                            point[0],
                            getPoint(x, prevX, point[1]),
                            getPoint(y, prevY, point[2]),
                            point[3],
                            point[4],
                            point[5]
                        ], p[2]
                    ];
                }

                if (p[0] === 'pdf') {
                    points[i] = [p[0],
                        [
                            point[0],
                            getPoint(x, prevX, point[1]),
                            getPoint(y, prevY, point[2]),
                            point[3],
                            point[4],
                            point[5]
                        ], p[2]
                    ];
                }

                if (p[0] === 'quadratic') {
                    points[i] = [p[0],
                        [
                            getPoint(x, prevX, point[0]),
                            getPoint(y, prevY, point[1]),
                            getPoint(x, prevX, point[2]),
                            getPoint(y, prevY, point[3]),
                            getPoint(x, prevX, point[4]),
                            getPoint(y, prevY, point[5])
                        ], p[2]
                    ];
                }

                if (p[0] === 'bezier') {
                    points[i] = [p[0],
                        [
                            getPoint(x, prevX, point[0]),
                            getPoint(y, prevY, point[1]),
                            getPoint(x, prevX, point[2]),
                            getPoint(y, prevY, point[3]),
                            getPoint(x, prevX, point[4]),
                            getPoint(y, prevY, point[5]),
                            getPoint(x, prevX, point[6]),
                            getPoint(y, prevY, point[7])
                        ], p[2]
                    ];
                }
            }
        },
        dragLastPath: function(x, y) {
            // if last past is undefined?
            if (!points[points.length - 1]) return;

            var g = this.global,
                prevX = g.prevX,
                prevY = g.prevY,
                p = points[points.length - 1],
                point = p[1],
                getPoint = this.getPoint,
                getXYWidthHeight = this.getXYWidthHeight,
                isMoveAllPoints = g.pointsToMove === 'all';

            if (p[0] === 'line') {

                if (g.pointsToMove === 'head' || isMoveAllPoints) {
                    point[0] = getPoint(x, prevX, point[0]);
                    point[1] = getPoint(y, prevY, point[1]);
                }

                if (g.pointsToMove === 'tail' || isMoveAllPoints) {
                    point[2] = getPoint(x, prevX, point[2]);
                    point[3] = getPoint(y, prevY, point[3]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'pencil') {

                if (g.pointsToMove === 'head' || isMoveAllPoints) {
                    point[0] = getPoint(x, prevX, point[0]);
                    point[1] = getPoint(y, prevY, point[1]);
                }

                if (g.pointsToMove === 'tail' || isMoveAllPoints) {
                    point[2] = getPoint(x, prevX, point[2]);
                    point[3] = getPoint(y, prevY, point[3]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'arrow') {

                if (g.pointsToMove === 'head' || isMoveAllPoints) {
                    point[0] = getPoint(x, prevX, point[0]);
                    point[1] = getPoint(y, prevY, point[1]);
                }

                if (g.pointsToMove === 'tail' || isMoveAllPoints) {
                    point[2] = getPoint(x, prevX, point[2]);
                    point[3] = getPoint(y, prevY, point[3]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'text') {

                if (g.pointsToMove === 'head' || isMoveAllPoints) {
                    point[1] = getPoint(x, prevX, point[1]);
                    point[2] = getPoint(y, prevY, point[2]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'arc') {
                point[0] = getPoint(x, prevX, point[0]);
                point[1] = getPoint(y, prevY, point[1]);

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'rect') {

                if (isMoveAllPoints) {
                    point[0] = getPoint(x, prevX, point[0]);
                    point[1] = getPoint(y, prevY, point[1]);
                }

                if (g.pointsToMove === 'stretch-first') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[0],
                        y: point[1],
                        width: point[2],
                        height: point[3],
                        pointsToMove: g.pointsToMove
                    });

                    point[0] = newPoints.x;
                    point[1] = newPoints.y;
                    point[2] = newPoints.width;
                    point[3] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-second') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[0],
                        y: point[1],
                        width: point[2],
                        height: point[3],
                        pointsToMove: g.pointsToMove
                    });

                    point[1] = newPoints.y;
                    point[2] = newPoints.width;
                    point[3] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-third') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[0],
                        y: point[1],
                        width: point[2],
                        height: point[3],
                        pointsToMove: g.pointsToMove
                    });

                    point[0] = newPoints.x;
                    point[2] = newPoints.width;
                    point[3] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-last') {
                    point[2] = getPoint(x, prevX, point[2]);
                    point[3] = getPoint(y, prevY, point[3]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'image') {

                if (isMoveAllPoints) {
                    point[1] = getPoint(x, prevX, point[1]);
                    point[2] = getPoint(y, prevY, point[2]);
                }

                if (g.pointsToMove === 'stretch-first') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[1],
                        y: point[2],
                        width: point[3],
                        height: point[4],
                        pointsToMove: g.pointsToMove
                    });

                    point[1] = newPoints.x;
                    point[2] = newPoints.y;
                    point[3] = newPoints.width;
                    point[4] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-second') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[1],
                        y: point[2],
                        width: point[3],
                        height: point[4],
                        pointsToMove: g.pointsToMove
                    });

                    point[2] = newPoints.y;
                    point[3] = newPoints.width;
                    point[4] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-third') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[1],
                        y: point[2],
                        width: point[3],
                        height: point[4],
                        pointsToMove: g.pointsToMove
                    });

                    point[1] = newPoints.x;
                    point[3] = newPoints.width;
                    point[4] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-last') {
                    point[3] = getPoint(x, prevX, point[3]);
                    point[4] = getPoint(y, prevY, point[4]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'pdf') {

                if (isMoveAllPoints) {
                    point[1] = getPoint(x, prevX, point[1]);
                    point[2] = getPoint(y, prevY, point[2]);
                }

                if (g.pointsToMove === 'stretch-first') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[1],
                        y: point[2],
                        width: point[3],
                        height: point[4],
                        pointsToMove: g.pointsToMove
                    });

                    point[1] = newPoints.x;
                    point[2] = newPoints.y;
                    point[3] = newPoints.width;
                    point[4] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-second') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[1],
                        y: point[2],
                        width: point[3],
                        height: point[4],
                        pointsToMove: g.pointsToMove
                    });

                    point[2] = newPoints.y;
                    point[3] = newPoints.width;
                    point[4] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-third') {
                    var newPoints = getXYWidthHeight(x, y, prevX, prevY, {
                        x: point[1],
                        y: point[2],
                        width: point[3],
                        height: point[4],
                        pointsToMove: g.pointsToMove
                    });

                    point[1] = newPoints.x;
                    point[3] = newPoints.width;
                    point[4] = newPoints.height;
                }

                if (g.pointsToMove === 'stretch-last') {
                    point[3] = getPoint(x, prevX, point[3]);
                    point[4] = getPoint(y, prevY, point[4]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'quadratic') {

                if (g.pointsToMove === 'starting-points' || isMoveAllPoints) {
                    point[0] = getPoint(x, prevX, point[0]);
                    point[1] = getPoint(y, prevY, point[1]);
                }

                if (g.pointsToMove === 'control-points' || isMoveAllPoints) {
                    point[2] = getPoint(x, prevX, point[2]);
                    point[3] = getPoint(y, prevY, point[3]);
                }

                if (g.pointsToMove === 'ending-points' || isMoveAllPoints) {
                    point[4] = getPoint(x, prevX, point[4]);
                    point[5] = getPoint(y, prevY, point[5]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }

            if (p[0] === 'bezier') {

                if (g.pointsToMove === 'starting-points' || isMoveAllPoints) {
                    point[0] = getPoint(x, prevX, point[0]);
                    point[1] = getPoint(y, prevY, point[1]);
                }

                if (g.pointsToMove === '1st-control-points' || isMoveAllPoints) {
                    point[2] = getPoint(x, prevX, point[2]);
                    point[3] = getPoint(y, prevY, point[3]);
                }

                if (g.pointsToMove === '2nd-control-points' || isMoveAllPoints) {
                    point[4] = getPoint(x, prevX, point[4]);
                    point[5] = getPoint(y, prevY, point[5]);
                }

                if (g.pointsToMove === 'ending-points' || isMoveAllPoints) {
                    point[6] = getPoint(x, prevX, point[6]);
                    point[7] = getPoint(y, prevY, point[7]);
                }

                points[points.length - 1] = [p[0], point, p[2]];
            }
        }
    };

    var pencilHandler = {
        ismousedown: false,
        prevX: 0,
        prevY: 0,
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;

            // make sure that pencil is drawing shapes even 
            // if mouse is down but mouse isn't moving
            tempContext.lineCap = 'round';
            pencilDrawHelper.pencil(tempContext, [t.prevX, t.prevY, x, y]);

            points[points.length] = ['pencil', [t.prevX, t.prevY, x, y], pencilDrawHelper.getOptions(), 'start'];

            t.prevX = x;
            t.prevY = y;
        },
        mouseup: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            if (t.ismousedown) {
                tempContext.lineCap = 'round';
                pencilDrawHelper.pencil(tempContext, [t.prevX, t.prevY, x, y]);

                points[points.length] = ['pencil', [t.prevX, t.prevY, x, y], pencilDrawHelper.getOptions(), 'end'];

                t.prevX = x;
                t.prevY = y;
            }

            this.ismousedown = false;
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            if (t.ismousedown) {
                tempContext.lineCap = 'round';
                pencilDrawHelper.pencil(tempContext, [t.prevX, t.prevY, x, y]);

                points[points.length] = ['pencil', [t.prevX, t.prevY, x, y], pencilDrawHelper.getOptions()];

                t.prevX = x;
                t.prevY = y;
            }
        }
    }

    var pencilLineWidth = document.getElementById('pencil-stroke-style').value,
        pencilStrokeStyle = '#' + document.getElementById('pencil-fill-style').value;

    var pencilDrawHelper = clone(drawHelper);

    pencilDrawHelper.getOptions = function() {
        return [pencilLineWidth, pencilStrokeStyle, fillStyle, globalAlpha, globalCompositeOperation, lineCap, lineJoin, font];
    }

    var markerHandler = {
        ismousedown: false,
        prevX: 0,
        prevY: 0,
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;

            // make sure that pencil is drawing shapes even 
            // if mouse is down but mouse isn't moving
            tempContext.lineCap = 'round';
            markerDrawHelper.line(tempContext, [t.prevX, t.prevY, x, y]);

            points[points.length] = ['line', [t.prevX, t.prevY, x, y], markerDrawHelper.getOptions()];

            t.prevX = x;
            t.prevY = y;
        },
        mouseup: function(e) {
            this.ismousedown = false;
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            if (t.ismousedown) {
                tempContext.lineCap = 'round';
                markerDrawHelper.line(tempContext, [t.prevX, t.prevY, x, y]);

                points[points.length] = ['line', [t.prevX, t.prevY, x, y], markerDrawHelper.getOptions()];

                t.prevX = x;
                t.prevY = y;
            }
        }
    }

    var markerLineWidth = document.getElementById('marker-stroke-style').value,
        markerStrokeStyle = '#' + document.getElementById('marker-fill-style').value,
        markerGlobalAlpha = 0.7;

    var markerDrawHelper = clone(drawHelper);

    markerDrawHelper.getOptions = function() {
        return [markerLineWidth, markerStrokeStyle, fillStyle, markerGlobalAlpha, globalCompositeOperation, lineCap, lineJoin, font];
    }

    var eraserHandler = {
        ismousedown: false,
        prevX: 0,
        prevY: 0,
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;

            tempContext.lineCap = 'round';
            eraserDrawHelper.line(tempContext, [t.prevX, t.prevY, x, y]);

            points[points.length] = ['line', [t.prevX, t.prevY, x, y], eraserDrawHelper.getOptions()];

            t.prevX = x;
            t.prevY = y;
        },
        mouseup: function(e) {
            this.ismousedown = false;
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            if (t.ismousedown) {
                tempContext.lineCap = 'round';
                eraserDrawHelper.line(tempContext, [t.prevX, t.prevY, x, y]);

                points[points.length] = ['line', [t.prevX, t.prevY, x, y], eraserDrawHelper.getOptions()];

                t.prevX = x;
                t.prevY = y;
            }
        }
    };
    
    var eraserDrawHelper = clone(drawHelper);

    eraserDrawHelper.getOptions = function() {
        return [pencilLineWidth, strokeStyle, fillStyle, globalAlpha, globalCompositeOperation, lineCap, lineJoin, font];
    };

    var textHandler = {
        text: '',
        selectedFontFamily: 'Arial',
        selectedFontSize: '15',
        lastFillStyle: '',
        onShapeSelected: function() {
            tempContext.canvas.style.cursor = 'text';
            this.x = this.y = this.pageX = this.pageY = 0;
            this.text = '';
        },
        onShapeUnSelected: function() {
            this.text = '';
            this.showOrHideTextTools('hide');
            tempContext.canvas.style.cursor = 'default';

            if (typeof this.blinkCursorInterval !== 'undefined') {
                clearInterval(this.blinkCursorInterval);
            }
        },
        getFillColor: function(color) {
            color = (color || fillStyle).toLowerCase();

            if (color == 'rgba(255, 255, 255, 0)' || color == 'transparent' || color === 'white') {
                return 'black';
            }

            return color;
        },
        writeText: function(keyPressed, isBackKeyPressed) {
            if (!is.isText) return;

            if (isBackKeyPressed) {
                textHandler.text = textHandler.text.substr(0, textHandler.text.length - 1);
                textHandler.fillText(textHandler.text);
                return;
            }

            textHandler.text += keyPressed;
            textHandler.fillText(textHandler.text);
        },
        fillText: function(text) {
            if (!is.isText) return;

            tempContext.clearRect(0, 0, tempContext.canvas.width, tempContext.canvas.height);

            var options = textHandler.getOptions();
            drawHelper.handleOptions(tempContext, options);
            tempContext.fillStyle = textHandler.getFillColor(options[2]);
            tempContext.font = textHandler.selectedFontSize + 'px "' + textHandler.selectedFontFamily + '"';

            tempContext.fillText(text, textHandler.x, textHandler.y);
        },
        blinkCursorInterval: null,
        index: 0,
        blinkCursor: function() {
            textHandler.index++;
            if (textHandler.index % 2 == 0) {
                textHandler.fillText(textHandler.text + '|');
            } else {
                textHandler.fillText(textHandler.text);
            }
        },
        getOptions: function() {
            var options = {
                font: textHandler.selectedFontSize + 'px "' + textHandler.selectedFontFamily + '"',
                fillStyle: textHandler.getFillColor(),
                strokeStyle: '#6c96c8',
                globalCompositeOperation: 'source-over',
                globalAlpha: 1,
                lineJoin: 'round',
                lineCap: 'round',
                lineWidth: 2
            };
            font = options.font;
            return options;
        },
        appendPoints: function() {
            var options = textHandler.getOptions();
            points[points.length] = ['text', ['"' + textHandler.text + '"', textHandler.x, textHandler.y], drawHelper.getOptions(options)];
        },
        mousedown: function(e) {
            if (!is.isText) return;

            if (textHandler.text.length) {
                this.appendPoints();
            }

            textHandler.x = textHandler.y = 0;
            textHandler.text = '';

            textHandler.pageX = e.pageX;
            textHandler.pageY = e.pageY;

            textHandler.x = e.pageX - canvas.offsetLeft - 5;
            textHandler.y = e.pageY - canvas.offsetTop + 10;

            if (typeof textHandler.blinkCursorInterval !== 'undefined') {
                clearInterval(textHandler.blinkCursorInterval);
            }

            textHandler.blinkCursor();
            textHandler.blinkCursorInterval = setInterval(textHandler.blinkCursor, 700);

            this.showTextTools();
        },
        mouseup: function(e) {},
        mousemove: function(e) {},
        showOrHideTextTools: function(show) {
            if (show === 'hide') {
                if (this.lastFillStyle.length) {
                    fillStyle = this.lastFillStyle;
                    this.lastFillStyle = '';
                }
            } else if (!this.lastFillStyle.length) {
                this.lastFillStyle = fillStyle;
                fillStyle = 'black';
            }

            this.fontFamilyBox.style.display = show == 'show' ? 'block' : 'none';
            this.fontSizeBox.style.display = show == 'show' ? 'block' : 'none';

            this.fontSizeBox.style.left = this.x + 'px';
            this.fontFamilyBox.style.left = (this.fontSizeBox.clientWidth + this.x) + 'px';

            this.fontSizeBox.style.top = this.y + 'px';
            this.fontFamilyBox.style.top = this.y + 'px';
        },
        showTextTools: function() {
            if (!this.fontFamilyBox || !this.fontSizeBox) return;

            this.unselectAllFontFamilies();
            this.unselectAllFontSizes();

            this.showOrHideTextTools('show');

            this.eachFontFamily(function(child) {
                child.onclick = function(e) {
                    e.preventDefault();

                    //textHandler.showOrHideTextTools('hide');
                    textHandler.clearFonts();
                    
                    textHandler.selectedFontFamily = this.innerHTML;
                    this.className = 'font-family-selected';
                };
//                child.style.fontFamily = child.innerHTML;
            });

            this.eachFontSize(function(child) {
                child.onclick = function(e) {
                    e.preventDefault();

                    //textHandler.showOrHideTextTools('hide');
                    textHandler.clearSizes();
                    
                    textHandler.selectedFontSize = this.innerHTML;
                    this.className = 'font-size-selected';
                    
                    
                };
                // child.style.fontSize = child.innerHTML + 'px';
            });
        },
        eachFontFamily: function(callback) {
            var childs = this.fontFamilyBox.querySelectorAll('li');
            for (var i = 0; i < childs.length; i++) {
                callback(childs[i]);
            }
        },
        clearFonts: function() {
            this.eachFontFamily(function(child) {
                child.className = '';
            });
        },
        clearSizes: function() {
            this.eachFontSize(function(child) {
                child.className = '';
            });
        },
        unselectAllFontFamilies: function() {
            this.eachFontFamily(function(child) {
                child.className = '';
                if (child.innerHTML === textHandler.selectedFontFamily) {
                    child.className = 'font-family-selected';
                }
            });
        },
        eachFontSize: function(callback) {
            var childs = this.fontSizeBox.querySelectorAll('li');
            for (var i = 0; i < childs.length; i++) {
                callback(childs[i]);
            }
        },
        unselectAllFontSizes: function() {
            this.eachFontSize(function(child) {
                child.className = '';
                if (child.innerHTML === textHandler.selectedFontSize) {
                    child.className = 'font-size-selected';
                }
            });
        },
        onReturnKeyPressed: function() {
            if (!textHandler.text || !textHandler.text.length) return;
            var fontSize = parseInt(textHandler.selectedFontSize) || 15;
            this.mousedown({
                pageX: this.pageX,
                // pageY: parseInt(tempContext.measureText(textHandler.text).height * 2) + 10
                pageY: this.pageY + fontSize + 5
            });
            drawHelper.redraw();
        },
        fontFamilyBox: document.querySelector('.fontSelectUl'),
        fontSizeBox: document.querySelector('.fontSizeUl')
    };

    var arcHandler = {
        global: {
            ismousedown: false,
            prevX: 0,
            prevY: 0,
            prevRadius: 0,
            isCircleDrawn: false,
            isCircledEnded: true,
            isClockwise: false,
            arcRangeContainer: null,
            arcRange: null
        },
        mousedown: function(e) {
            var g = this.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            g.prevX = x;
            g.prevY = y;

            g.ismousedown = true;
        },
        mouseup: function(e) {
            var g = this.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            if (g.ismousedown) {
                if (!g.isCircleDrawn && g.isCircledEnded) {
                    var prevX = g.prevX,
                        prevY = g.prevY,
                        radius = ((x - prevX) + (y - prevY)) / 3;

                    g.prevRadius = radius;
                    g.isCircleDrawn = true;
                    g.isCircleEnded = false;

                    var c = (2 * Math.PI * radius) / 21,
                        angle,
                        xx = prevX > x ? prevX - x : x - prevX,
                        yy = prevY > y ? prevY - y : y - prevY;

                    angle = (xx + yy) / (2 * c);
                    points[points.length] = ['arc', [prevX + radius, prevY + radius, radius, angle, 1], drawHelper.getOptions()];

                    var arcRange = g.arcRange,
                        arcRangeContainer = g.arcRangeContainer;

                    arcRangeContainer.style.display = 'block';
                    arcRange.focus();

                    arcRangeContainer.style.top = (y + 20) + 'px';
                    arcRangeContainer.style.left = x + 'px';

                    arcRange.value = 2;
                } else if (g.isCircleDrawn && !g.isCircleEnded) {
                    this.end();
                }
            }

            g.ismousedown = false;

            this.fixAllPoints();
        },
        mousemove: function(e) {
            var g = this.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var ismousedown = g.ismousedown,
                isCircleDrawn = g.isCircleDrawn,
                isCircleEnded = g.isCircledEnded;

            if (ismousedown) {
                if (!isCircleDrawn && isCircleEnded) {
                    var prevX = g.prevX,
                        prevY = g.prevY,
                        radius = ((x - prevX) + (y - prevY)) / 3;

                    tempContext.clearRect(0, 0, 2000, 2000);

                    drawHelper.arc(tempContext, [prevX + radius, prevY + radius, radius, Math.PI * 2, true]);
                }
            }
        },
        fixAllPoints: function() {
            var toFixed = this.toFixed;

            for (var i = 0; i < points.length; i++) {
                var p = points[i],
                    point;
                if (p[0] === 'arc') {
                    point = p[1];
                    points[i] = ['arc', [toFixed(point[0]), toFixed(point[1]), toFixed(point[2]), toFixed(point[3]), point[4]],
                        p[2]
                    ];
                }
            }
        },
        init: function() {
            var markIsClockwise = find('is-clockwise'),
                g = this.global;

            g.arcRangeContainer = find('arc-range-container');
            g.arcRange = find('arc-range');

            addEvent(markIsClockwise, 'change', function(e) {
                g.isClockwise = markIsClockwise.checked;

                g.arcRange.value = arcHandler.toFixed(g.arcRange.value);
                g.arcRange.focus();

                arcHandler.arcRangeHandler(e);

                if (!points.length) return;

                var p = points[points.length - 1],
                    point = p[1];

                tempContext.clearRect(0, 0, innerWidth, innerHeight);
                drawHelper.arc(tempContext, [point[0], point[1], point[2], point[3], point[4]]);
            });

            var arcRange = g.arcRange;
            addEvent(arcRange, 'keydown', this.arcRangeHandler);
            addEvent(arcRange, 'focus', this.arcRangeHandler);
        },
        arcRangeHandler: function(e) {
            var g = arcHandler.global,
                arcRange = g.arcRange;

            var key = e.keyCode,
                value = +arcRange.value;
            if (key == 39 || key == 40) arcRange.value = (value < 2 ? value : 1.98) + .02;
            if (key == 37 || key == 38) arcRange.value = (value > 0 ? value : .02) - .02;

            if (!key || key == 13 || key == 39 || key == 40 || key == 37 || key == 38) {
                var range = Math.PI * arcHandler.toFixed(value);
                var p = points[points.length - 1];

                if (p[0] === 'arc') {
                    var point = p[1];
                    points[points.length - 1] = ['arc', [point[0], point[1], point[2], range, g.isClockwise ? 1 : 0],
                        p[2]
                    ];

                    drawHelper.redraw();
                }
            }
        },
        toFixed: function(input) {
            return Number(input).toFixed(1);
        },
        end: function() {
            var g = this.global;

            g.arcRangeContainer.style.display = 'none';
            g.arcRange.value = 2;

            g.isCircleDrawn = false;
            g.isCircleEnded = true;

            drawHelper.redraw();
        }
    };

    arcHandler.init();

    var lineHandler = {
        ismousedown: false,
        prevX: 0,
        prevY: 0,
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;
        },
        mouseup: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                points[points.length] = ['line', [t.prevX, t.prevY, x, y], drawHelper.getOptions()];

                t.ismousedown = false;
            }
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            if (t.ismousedown) {
                tempContext.clearRect(0, 0, innerWidth, innerHeight);

                drawHelper.line(tempContext, [t.prevX, t.prevY, x, y]);
            }
        }
    };

    var arrowHandler = {
        ismousedown: false,
        prevX: 0,
        prevY: 0,
        arrowSize: 10,
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;
        },
        mouseup: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                points[points.length] = ['arrow', [t.prevX, t.prevY, x, y], drawHelper.getOptions()];

                t.ismousedown = false;
            }
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            if (t.ismousedown) {
                tempContext.clearRect(0, 0, innerWidth, innerHeight);

                drawHelper.arrow(tempContext, [t.prevX, t.prevY, x, y]);
            }
        }
    };

    var rectHandler = {
        ismousedown: false,
        prevX: 0,
        prevY: 0,
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;
        },
        mouseup: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                points[points.length] = ['rect', [t.prevX, t.prevY, x - t.prevX, y - t.prevY], drawHelper.getOptions()];

                t.ismousedown = false;
            }

        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                tempContext.clearRect(0, 0, innerWidth, innerHeight);

                drawHelper.rect(tempContext, [t.prevX, t.prevY, x - t.prevX, y - t.prevY]);
            }
        }
    };

    var quadraticHandler = {
        global: {
            ismousedown: false,
            prevX: 0,
            prevY: 0,
            controlPointX: 0,
            controlPointY: 0,
            isFirstStep: true,
            isLastStep: false
        },
        mousedown: function(e) {
            var g = this.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            if (!g.isLastStep) {
                g.prevX = x;
                g.prevY = y;
            }

            g.ismousedown = true;

            if (g.isLastStep && g.ismousedown) {
                this.end(x, y);
            }
        },
        mouseup: function(e) {
            var g = this.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            if (g.ismousedown && g.isFirstStep) {
                g.controlPointX = x;
                g.controlPointY = y;

                g.isFirstStep = false;
                g.isLastStep = true;
            }
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var g = this.global;

            tempContext.clearRect(0, 0, innerWidth, innerHeight);

            if (g.ismousedown && g.isFirstStep) {
                drawHelper.quadratic(tempContext, [g.prevX, g.prevY, x, y, x, y]);
            }

            if (g.isLastStep) {
                drawHelper.quadratic(tempContext, [g.prevX, g.prevY, g.controlPointX, g.controlPointY, x, y]);
            }
        },
        end: function(x, y) {
            var g = this.global;

            if (!g.ismousedown) return;

            g.isLastStep = false;

            g.isFirstStep = true;
            g.ismousedown = false;

            x = x || g.controlPointX || g.prevX;
            y = y || g.controlPointY || g.prevY;

            points[points.length] = ['quadratic', [g.prevX, g.prevY, g.controlPointX, g.controlPointY, x, y], drawHelper.getOptions()];
        }
    };

    var bezierHandler = {
        global: {
            ismousedown: false,
            prevX: 0,
            prevY: 0,

            firstControlPointX: 0,
            firstControlPointY: 0,
            secondControlPointX: 0,
            secondControlPointY: 0,

            isFirstStep: true,
            isSecondStep: false,
            isLastStep: false
        },
        mousedown: function(e) {
            var g = this.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            if (!g.isLastStep && !g.isSecondStep) {
                g.prevX = x;
                g.prevY = y;
            }

            g.ismousedown = true;

            if (g.isLastStep && g.ismousedown) {
                this.end(x, y);
            }

            if (g.ismousedown && g.isSecondStep) {
                g.secondControlPointX = x;
                g.secondControlPointY = y;

                g.isSecondStep = false;
                g.isLastStep = true;
            }
        },
        mouseup: function(e) {
            var g = this.global;

            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            if (g.ismousedown && g.isFirstStep) {
                g.firstControlPointX = x;
                g.firstControlPointY = y;

                g.isFirstStep = false;
                g.isSecondStep = true;
            }
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var g = this.global;

            tempContext.clearRect(0, 0, innerWidth, innerHeight);

            if (g.ismousedown && g.isFirstStep) {
                drawHelper.bezier(tempContext, [g.prevX, g.prevY, x, y, x, y, x, y]);
            }

            if (g.ismousedown && g.isSecondStep) {
                drawHelper.bezier(tempContext, [g.prevX, g.prevY, g.firstControlPointX, g.firstControlPointY, x, y, x, y]);
            }

            if (g.isLastStep) {
                drawHelper.bezier(tempContext, [g.prevX, g.prevY, g.firstControlPointX, g.firstControlPointY, g.secondControlPointX, g.secondControlPointY, x, y]);
            }
        },
        end: function(x, y) {
            var g = this.global;

            if (!g.ismousedown) return;

            g.isLastStep = g.isSecondStep = false;

            g.isFirstStep = true;
            g.ismousedown = false;

            g.secondControlPointX = g.secondControlPointX || g.firstControlPointX;
            g.secondControlPointY = g.secondControlPointY || g.firstControlPointY;

            x = x || g.secondControlPointX;
            y = y || g.secondControlPointY;

            points[points.length] = ['bezier', [g.prevX, g.prevY, g.firstControlPointX, g.firstControlPointY, g.secondControlPointX, g.secondControlPointY, x, y], drawHelper.getOptions()];
        }
    };

    var zoomHandler = {
        scale: 1.0,
        up: function(e) {
            this.scale += .01;
            this.apply();
            this.scale = 1.0;
        },
        down: function(e) {
            this.scale -= .01;
            this.apply();
            this.scale = 1.0;
        },
        apply: function() {
            tempContext.scale(this.scale, this.scale);
            context.scale(this.scale, this.scale);
            drawHelper.redraw();
        },
        icons: {
            up: function(ctx) {
                ctx.font = '22px Verdana';
                ctx.strokeText('+', 10, 30);
            },
            down: function(ctx) {
                ctx.font = '22px Verdana';
                ctx.strokeText('-', 15, 30);
            }
        }
    };

    var FileSelector = function() {
        var selector = this;

        selector.selectSingleFile = selectFile;
        selector.selectMultipleFiles = function(callback) {
            selectFile(callback, true);
        };

        function selectFile(callback, multiple, accept) {
            var file = document.createElement('input');
            file.type = 'file';

            if (multiple) {
                file.multiple = true;
            }

            file.accept = accept || 'image/*';

            file.onchange = function() {
                if (multiple) {
                    if (!file.files.length) {
                        console.error('No file selected.');
                        return;
                    }
                    callback(file.files);
                    return;
                }

                if (!file.files[0]) {
                    console.error('No file selected.');
                    return;
                }

                callback(file.files[0]);

                file.parentNode.removeChild(file);
            };
            file.style.display = 'none';
            (document.body || document.documentElement).appendChild(file);
            fireClickEvent(file);
        }

        function fireClickEvent(element) {
            var evt = new window.MouseEvent('click', {
                view: window,
                bubbles: true,
                cancelable: true,
                button: 0,
                buttons: 0,
                mozInputSource: 1
            });

            var fired = element.dispatchEvent(evt);
        }
    };

    var imageHandler = {
        lastImageURL: null,
        lastImageIndex: 0,
        images: [],

        ismousedown: false,
        prevX: 0,
        prevY: 0,
        load: function(width, height) {
            var t = imageHandler;
            points[points.length] = ['image', [imageHandler.lastImageURL, t.prevX, t.prevY, width, height, imageHandler.lastImageIndex], drawHelper.getOptions()];
            document.getElementById('drag-last-path').click();

            // share to webrtc
            syncPoints(true);
        },
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;
        },
        mouseup: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                points[points.length] = ['image', [imageHandler.lastImageURL, t.prevX, t.prevY, x - t.prevX, y - t.prevY, imageHandler.lastImageIndex], drawHelper.getOptions()];

                t.ismousedown = false;
            }

        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                tempContext.clearRect(0, 0, innerWidth, innerHeight);

                drawHelper.image(tempContext, [imageHandler.lastImageURL, t.prevX, t.prevY, x - t.prevX, y - t.prevY, imageHandler.lastImageIndex]);
            }
        }
    };

    var pdfHandler = {
        lastPdfURL: null,
        lastIndex: 0,
        lastPointIndex: 0,
        removeWhiteBackground: false,
        pdfPageContainer: document.getElementById('pdf-page-container'),
        pdfPagesList: document.getElementById('pdf-pages-list'),
        pdfNext: document.getElementById('pdf-next'),
        pdfPrev: document.getElementById('pdf-prev'),
        pdfClose: document.getElementById('pdf-close'),
        pageNumber: 1,

        images: [],
        ismousedown: false,
        prevX: 0,
        prevY: 0,
        getPage: function(pageNumber, callback) {
            pageNumber = parseInt(pageNumber) || 1;

            if (!pdfHandler.pdf) {
                pdfjsLib.disableWorker = false;
                pdfjsLib.getDocument(pdfHandler.lastPdfURL).then(function(pdf) {
                    pdfHandler.pdf = pdf;
                    pdfHandler.getPage(pageNumber, callback);
                });
                return;
            }

            var pdf = pdfHandler.pdf;
            pdf.getPage(pageNumber).then(function(page) {
                pdfHandler.pageNumber = pageNumber;

                var scale = 1.5;
                var viewport = page.getViewport(scale);

                var cav = document.createElement('canvas');
                var ctx = cav.getContext('2d');
                cav.height = viewport.height;
                cav.width = viewport.width;

                var renderContext = {
                    canvasContext: ctx,
                    viewport: viewport
                };

                if (pdfHandler.removeWhiteBackground === true) {
                    renderContext.background = 'rgba(0,0,0,0)';
                }

                page.render(renderContext).then(function() {
                    if (pdfHandler.removeWhiteBackground === true) {
                        var imgd = ctx.getImageData(0, 0, cav.width, cav.height);
                        var pix = imgd.data;
                        var newColor = {
                            r: 0,
                            g: 0,
                            b: 0,
                            a: 0
                        };

                        for (var i = 0, n = pix.length; i < n; i += 4) {
                            var r = pix[i],
                                g = pix[i + 1],
                                b = pix[i + 2];

                            if (r == 255 && g == 255 && b == 255) {
                                pix[i] = newColor.r;
                                pix[i + 1] = newColor.g;
                                pix[i + 2] = newColor.b;
                                pix[i + 3] = newColor.a;
                            }
                        }
                        ctx.putImageData(imgd, 0, 0);
                    }

                    pdfHandler.lastPage = cav.toDataURL('image/png');
                    callback(pdfHandler.lastPage, cav.width, cav.height, pdf.numPages);
                });
            });
        },
        load: function(lastPdfURL) {
            pdfHandler.lastPdfURL = lastPdfURL;
            pdfHandler.getPage(parseInt(pdfHandler.pdfPagesList.value || 1), function(lastPage, width, height, numPages) {
                pdfHandler.prevX = canvas.width - width - parseInt(width / 2);

                var t = pdfHandler;
                pdfHandler.lastIndex = pdfHandler.images.length;
                var point = [lastPage, 60, 20, width, height, pdfHandler.lastIndex];

                pdfHandler.lastPointIndex = points.length;
                points[points.length] = ['pdf', point, drawHelper.getOptions()];

                pdfHandler.pdfPagesList.innerHTML = '';
                for (var i = 1; i <= numPages; i++) {
                    var option = document.createElement('option');
                    option.value = i;
                    option.innerHTML = 'Page #' + i;
                    pdfHandler.pdfPagesList.appendChild(option);

                    if (pdfHandler.pageNumber.toString() == i.toString()) {
                        option.selected = true;
                    }
                }

                pdfHandler.pdfPagesList.onchange = function() {
                    pdfHandler.load(lastPdfURL);
                };

                pdfHandler.pdfNext.onclick = function() {
                    pdfHandler.pdfPagesList.selectedIndex++;
                    pdfHandler.pdfPagesList.onchange();
                };

                pdfHandler.pdfPrev.onclick = function() {
                    pdfHandler.pdfPagesList.selectedIndex--;
                    pdfHandler.pdfPagesList.onchange();
                };

                pdfHandler.pdfClose.onclick = function() {
                    pdfHandler.pdfPageContainer.style.display = 'none';
                };

                document.getElementById('drag-last-path').click();

                pdfHandler.pdfPrev.src = data_uris.pdf_next;
                pdfHandler.pdfNext.src = data_uris.pdf_prev;
                pdfHandler.pdfClose.src = data_uris.pdf_close;

                pdfHandler.pdfPageContainer.style.top = '20px';
                pdfHandler.pdfPageContainer.style.left = (point[3] - parseInt(point[3] / 2)) + 'px';
                pdfHandler.pdfPageContainer.style.display = 'block';

                // share to webrtc
                syncPoints(true);
            });
        },
        mousedown: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;

            t.prevX = x;
            t.prevY = y;

            t.ismousedown = true;
        },
        mouseup: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                if (points[pdfHandler.lastPointIndex]) {
                    points[pdfHandler.lastPointIndex] = ['pdf', [pdfHandler.lastPage, t.prevX, t.prevY, x - t.prevX, y - t.prevY, pdfHandler.lastIndex], drawHelper.getOptions()];
                }

                t.ismousedown = false;
            }
        },
        mousemove: function(e) {
            var x = e.pageX - canvas.offsetLeft,
                y = e.pageY - canvas.offsetTop;

            var t = this;
            if (t.ismousedown) {
                tempContext.clearRect(0, 0, innerWidth, innerHeight);
                drawHelper.pdf(tempContext, [pdfHandler.lastPage, t.prevX, t.prevY, x - t.prevX, y - t.prevY, pdfHandler.lastIndex]);
            }
        },
        reset_pos: function(x, y) {
            pdfHandler.pdfPageContainer.style.top = y + 'px';
            if (!points[pdfHandler.lastPointIndex]) return;
            var point = points[pdfHandler.lastPointIndex][1];
            pdfHandler.pdfPageContainer.style.left = (point[1] + point[3] - parseInt(point[3] / 2) - parseInt(pdfHandler.pdfPageContainer.clientWidth / 2)) + 'px';
        },
        end: function() {
            // pdfHandler.pdfPageContainer.style.display = 'none';
        }
    };

    var icons = {};
    if (params.icons) {
        try {
            icons = JSON.parse(params.icons);
        } catch (e) {
            icons = {};
        }
    }

    var data_uris = {
        line: icons.line || 'data:image/png;base64, ',
        arrow: icons.arrow || 'data:image/png;base64,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',
        pencil: icons.pencil || 'data:image/png;base64, ',
        marker: icons.marker || 'data:image/png;base64, ',
        dragSingle: icons.dragSingle || 'data:image/png;base64,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',
        dragMultiple: icons.dragMultiple || 'data:image/png;base64, ',
        eraser: icons.eraser || 'data:image/png;base64, ',
        rectangle: icons.rectangle || 'data:image/png;base64, ',
        arc: icons.arc || 'data:image/png;base64, ',
        bezier: icons.bezier || 'data:image/png;base64,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',
        quadratic: icons.quadratic || 'data:image/png;base64,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',
        text: icons.text || 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAWVJREFUeJzt28ENgzAQAEETpf+WSQfJA6xD2ZkCjCWv7mFgLQAAAKDimN4A69y8/tczfm1+OA8ngDgBxAkgTgBxAogTQJx7gOf7dU9w6QxNgDgBxAkgTgBxAogTQJwA4t43rLH7ffa/G72LMQHiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIu+N7gOl/C65+jzC9/1EmQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxx/QGWOfm9b+esQkQJ4A4AcQJIE4AcQKIEwAAAABAxAcVTAXjELyg1wAAAABJRU5ErkJggg==',
        image: icons.image || 'data:image/png;base64, ',
        pdf: icons.pdf || 'data:image/png;base64, ',
        zoom_in: icons.zoom_in || 'data:image/png;base64, ',
        zoom_out: icons.zoom_out || 'data:image/png;base64, ',
        lineWidth: icons.lineWidth || 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAABgUlEQVR4nO3csU0DMRgF4AdKoKFhGsa5YViCHTIIomEVGlJAAwWhoYAj57N98vdJr7XQ70jBlvMSAAAAAAAAAAAAAAAAYHsuCq93k+QuyW3hdfnykuQpyWvrP+SnfZL7JMckH7JqjqdZ72ftTAWXSQ5pP5jRcjjNvrkp7YcxaqYZ+7O6x7QfxKh5nLE/vyrxT+BbkqsC6/B/70mulyzQxXcI7ZT4ADwXWIPzLJ59iQ/AQ4E1OE8Xs3cMbJNujoFJsouLoFr5vgjazdqZP7gK3pZur4IBAAAAAAAAAACAtjwK3ZZuH4XqB6j/LFw/wODp5ochU9oPY9RMM/ZndfoB2kU/wOD0A7CMfoBt0w8wuC5m7xjYJt0cAxP9ADWjH2Bg3V4FAwAAAAAAAAAAAG15FLot3T4K1Q9Q/1m4foDB080PQ6a0H8aomWbsz+r0A7SLfoDB6QdgGf0A26YfYHBdzN4xsE26OQYm+gFqRj/AwLq9CgYAAAAAAAAAAAAAAABq+gRzFKdEQnj1fQAAAABJRU5ErkJggg==',
        colorsPicker: icons.colorsPicker || 'data:image/png;base64, ',
        extraOptions: icons.extraOptions || 'data:image/png;base64,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',
        undo: icons.undo || 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABPxJREFUeNrsnVuIVVUYx9dpQiIdvOBtDGa8RILSQz4koWYvRZeXEkRztJd8ExGkSPBRSxDMB0ESxBBpSnyLMCGJChV6SSURpEkcZia8RjqpmTTT97HXYQYcZ87Za5+z917f7wd/BhT1eNZvfWutvdbeuzI0NOTALk/wFSAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAlBq9LyAkJeY5yWeSmabbz6gA70n6JX9LplkWwNoQME/yleSwZI7ktn6HzAFsoL3+R8kaBv5hnjTS63fR8DYrAL3eaAWg1xuuAPR6oxWAXm+4AtDrjVYA7fWfSNbSlPYqQLXX0/jGKgC93nAFoNcbrQD0esMVgF5vtALQ6w1XAHq90QqQR6//jwpgt9dXJE9LWqwKUAk91lWpVEI/Q4fkY0lnDv9/7f2/Sf5yyfEw/XlLck3SI+mT9PqfA0VswOD2y1mATt/4HQXuJPcl170oFyVnJeckl/zvIUDKsV4b/t2SVs5/Jb9Lzki+l/wg+aOMAuRxKniDL69DEUUrxDE/f5nWbAHKcix8ruTLyBp+tFyW7JY8jwDDrI+w14+Xu5IjkhctCzDPSK8fK/9IDrrkTiRTAmww2OvHii4tt/nrDlELoGP9FzT4Y6Mrh5diFaCTXl9T9KLShzEJMJexPlW6JDPKLsBGfxGEBk2XXyQL8xIgi82gQQchvCA5IVma27XkDIaADiZ+mawSXolhEniFxkydm5KVLANt56pkSQwXgqgG6dMtaY/hUjBzg/TR01ETY9kMWkc1SJW9sW0HUw3qiy6x34lFAOYG6aLf1eyYBGBuUH8OxCYAc4P6omcQl8UoQN5zg0G/M6enex8WXILv3Cj3cYS2XxHuCxh5gGRnrevfjPhT8qo/tTPJZ6pLnh/c7uXU4WqBZLrL/0aatyTHy34qeLxq0NXEXqWneVvH+UwtXoiXJZslR3M883DSJXczRTUEPG6l0Iwv+Ybv2fUyRfKGZJ9L7g9olgB6J9MKCwI0a26QVoCRTJa87ZL7AgaaIMEhKwJUWd/AlUIWAoxkkZ/HXG7wjmGbJQEaWQ2yFqCKHvPa6jd1GiHBRmsCNGpu0CgBqsxyydNL72UswNdWBci6GjRagCp6DPznDAXQW9jnWBUgy7lBswSorhz2ZyjBKusCZFENmilAlS2SBxkIsAcBwncY8xBAWe2G31eUNj/plUkECNthzEsA5U1/KTrkFPFMBAirBnkKoLwuuROwkbWc18Y9ilaBlX5PoejoDSHv++3euvfhXHL7fRCxvjSqx1eC6oMpioxeQv4g5Z99NvhfN/Dm0LFWCnkPASPZk2IY+Jw5QNjcoEgCTJB8W6cA3yBA2EqhSAIoz7j6NpJOI0D6atDnL8jMKNhne81PCmsR4FcESM98l7w+vq2An21HjQJ0I0A4RVwJPSU5VYMAvVwHCKeID7jQQ6qbXPIA67FoidF+SDjvkrME5sofDPOpS84RNKx6IUCx0aFgq1+tUAGMog+WPIAAttFl4ZVRfr2CADbQo+DbEcA2+iTW4whgF73w85HL+OVVCFAuLjh/GNQzIfQvLNLt4VAbrX4o0FfS9Ev7Lc5VACg3DAEIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgApeN/AQYAUjtq/Zx6jVYAAAAASUVORK5CYII=',
        pdf_next: icons.pdf_next || 'data:image/png;base64,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',
        pdf_prev: icons.pdf_prev || 'data:image/png;base64,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',
        pdf_close: icons.pdf_close || 'data:image/png;base64, '
    };

    var tools = {
        line: true,
        arrow: true,
        pencil: true,
        marker: true,
        dragSingle: true,
        dragMultiple: true,
        eraser: true,
        rectangle: true,
        arc: true,
        bezier: true,
        quadratic: true,
        text: true,
        image: true,
        pdf: true,
        zoom: true,
        lineWidth: true,
        colorsPicker: true,
        extraOptions: true,
        code: true,
        undo: true
    };

    if (params.tools) {
        try {
            var t = JSON.parse(params.tools);
            tools = t;
        } catch (e) {}
    }

    if (tools.code === true) {
        document.querySelector('.preview-panel').style.display = 'block';
    }

    function setSelection(element, prop) {
        endLastPath();
//        hideContainers();

        is.set(prop);

        var selected = document.getElementsByClassName('selected-shape')[0];
        if (selected) selected.className = selected.className.replace(/selected-shape/g, '');

        if (!element.className) {
            element.className = '';
        }

        element.className += ' selected-shape';
    }

    /* Default: setting default selected shape!! */
    is.set(window.selectedIcon);

    function setDefaultSelectedIcon() {
        var toolBox = document.getElementById('tool-box');
        var canvasElements = toolBox.getElementsByTagName('canvas');
        var shape = window.selectedIcon.toLowerCase();

        var firstMatch;
        for (var i = 0; i < canvasElements.length; i++) {
            if (!firstMatch && (canvasElements[i].id || '').indexOf(shape) !== -1) {
                firstMatch = canvasElements[i];
            }
        }
        if (!firstMatch) {
            window.selectedIcon = 'Pencil';
            firstMatch = document.getElementById('pencil-icon');
        }

        setSelection(firstMatch, window.selectedIcon);
    }

    window.addEventListener('load', function() {
        setDefaultSelectedIcon();
    }, false);

    (function() {
        var cache = {};

        var lineCapSelect = find('lineCap-select');
        var lineJoinSelect = find('lineJoin-select');

        function getContext(id) {
            var context = find(id).getContext('2d');
            context.lineWidth = 2;
            context.strokeStyle = '#6c96c8';
            return context;
        }

        function bindEvent(context, shape) {
            if (shape === 'Pencil' || shape === 'Marker') {
                lineCap = lineJoin = 'round';
            }

            addEvent(context.canvas, 'click', function() {
                // pdfHandler.pdfPageContainer.style.display = 'none';

                if (textHandler.text.length) {
                    textHandler.appendPoints();
                }

                if (shape === 'Text') {
                    textHandler.onShapeSelected();
                } else {
                    textHandler.onShapeUnSelected();
                }

                if (shape === 'Pencil' || shape === 'Marker') {
                    lineCap = lineJoin = 'round';
                }

                dragHelper.global.startingIndex = 0;

                setSelection(this, shape);

                if (this.id === 'drag-last-path') {
                    find('copy-last').checked = true;
                    find('copy-all').checked = false;
                } else if (this.id === 'drag-all-paths') {
                    find('copy-all').checked = true;
                    find('copy-last').checked = false;
                }

                if (this.id === 'image-icon') {
                    var selector = new FileSelector();
                    selector.accept = 'image/*';
                    selector.selectSingleFile(function(file) {
                        if (!file) return;

                        var reader = new FileReader();
                        reader.onload = function(event) {
                            var image = new Image();
                            image.onload = function() {
                                var index = imageHandler.images.length;

                                imageHandler.lastImageURL = image.src;
                                imageHandler.lastImageIndex = index;

                                imageHandler.images.push(image);
                                imageHandler.load(image.clientWidth, image.clientHeight);
                            };
                            image.style = 'position: absolute; top: -99999999999; left: -999999999;'
                            document.body.appendChild(image);
                            image.src = event.target.result;
                        };
                        reader.readAsDataURL(file);
                    });
                }

                if (this.id === 'pdf-icon') {
                    var selector = new FileSelector();
                    selector.selectSingleFile(function(file) {
                        if (!file) return;

                        function onGettingPdf() {
                            var reader = new FileReader();
                            reader.onload = function(event) {
                                pdfHandler.pdf = null; // to make sure we call "getDocument" again
                                pdfHandler.load(event.target.result);
                            };
                            reader.readAsDataURL(file);
                        }
                        onGettingPdf();
                    }, null, 'application/pdf');
                }

                if (this.id === 'pencil-icon' || this.id === 'eraser-icon' || this.id === 'marker-icon') {
                    cache.lineCap = lineCap;
                    cache.lineJoin = lineJoin;

                    lineCap = lineJoin = 'round';
                } else if (cache.lineCap && cache.lineJoin) {
                    lineCap = cache.lineCap;
                    lineJoin = cache.lineJoin;
                }

                if (this.id === 'eraser-icon') {
                    cache.strokeStyle = strokeStyle;
                    cache.fillStyle = fillStyle;
                    cache.lineWidth = lineWidth;

                    strokeStyle = 'White';
                    fillStyle = 'White';
                    lineWidth = 10;
                } else if (cache.strokeStyle && cache.fillStyle && typeof cache.lineWidth !== 'undefined') {
                    strokeStyle = cache.strokeStyle;
                    fillStyle = cache.fillStyle;
                    lineWidth = cache.lineWidth;
                }
            });
        }

        var toolBox = find('tool-box');
        toolBox.style.height = (innerHeight) + 'px'; // -toolBox.offsetTop - 77

        function decorateDragLastPath() {
            var context = getContext('drag-last-path');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'DragLastPath');
            };
            image.src = data_uris.dragSingle;
        }

        decorateDragLastPath();

        if (tools.dragSingle === true) {
            document.getElementById('drag-last-path').style.display = 'block';
        }

        function decorateDragAllPaths() {
            var context = getContext('drag-all-paths');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'DragAllPaths');
            };
            image.src = data_uris.dragMultiple;
        }

        decorateDragAllPaths();

        if (tools.dragMultiple === true) {
            document.getElementById('drag-all-paths').style.display = 'block';
        }

        function decorateLine() {
            var context = getContext('line');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Line');
            };
            image.src = data_uris.line;
        }

        if (tools.line === true) {
            decorateLine();
            document.getElementById('line').style.display = 'block';
        }

        function decorateUndo() {
            var context = getContext('undo');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);

                document.querySelector('#undo').onclick = function() {
                    if (points.length) {
                        points.length = points.length - 1;
                        drawHelper.redraw();
                    }

                    // share to webrtc
                    syncPoints(true);
                };
            };
            image.src = data_uris.undo;
        }

        if (tools.undo === true) {
            decorateUndo();
            document.getElementById('undo').style.display = 'block';
        }

        function decorateArrow() {
            var context = getContext('arrow');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Arrow');
            };
            image.src = data_uris.arrow;
        }

        if (tools.arrow === true) {
            decorateArrow();
            document.getElementById('arrow').style.display = 'block';
        }

        function decoreZoomUp() {
            var context = getContext('zoom-up');
            // zoomHandler.icons.up(context);
            addEvent(context.canvas, 'click', function() {
                zoomHandler.up();
            });

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
            };
            image.src = data_uris.zoom_in;
        }

        function decoreZoomDown() {
            var context = getContext('zoom-down');
            // zoomHandler.icons.down(context);
            addEvent(context.canvas, 'click', function() {
                zoomHandler.down();
            });

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
            };
            image.src = data_uris.zoom_out;
        }

        if (tools.zoom === true) {
            decoreZoomUp();
            decoreZoomDown();

            document.getElementById('zoom-up').style.display = 'block';
            document.getElementById('zoom-down').style.display = 'block';
        }

        function decoratePencil() {

            function hexToRGBA(h, alpha) {
                return 'rgba(' + hexToRGB(h).join(',') + ',1)';
            }

            var colors = [
                ['FFFFFF', '006600', '000099', 'CC0000', '8C4600'],
                ['CCCCCC', '00CC00', '6633CC', 'FF0000', 'B28500'],
                ['666666', '66FFB2', '006DD9', 'FF7373', 'FF9933'],
                ['333333', '26FF26', '6699FF', 'CC33FF', 'FFCC99'],
                ['000000', 'CCFF99', 'BFDFFF', 'FFBFBF', 'FFFF33']
            ];

            var context = getContext('pencil-icon');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Pencil');
            };
            image.src = data_uris.pencil;

            var pencilContainer = find('pencil-container'),
                pencilColorContainer = find('pencil-fill-colors'),
                strokeStyleText = find('pencil-stroke-style'),
                pencilColorsList = find("pencil-colors-list"),
                fillStyleText = find('pencil-fill-style'),
                pencilSelectedColor = find('pencil-selected-color'),
                pencilSelectedColor2 = find('pencil-selected-color-2'),
                btnPencilDone = find('pencil-done'),
                canvas = context.canvas,
                alpha = 0.2;

            // START INIT PENCIL



            pencilStrokeStyle = hexToRGBA(fillStyleText.value, alpha)

            pencilSelectedColor.style.backgroundColor =
                pencilSelectedColor2.style.backgroundColor = '#' + fillStyleText.value;

            colors.forEach(function(colorRow) {
                var row = '<tr>';

                colorRow.forEach(function(color) {
                    row += '<td style="background-color:#' + color + '" data-color="' + color + '"></td>';
                })
                row += '</tr>';

                pencilColorsList.innerHTML += row;
            })

            Array.prototype.slice.call(pencilColorsList.getElementsByTagName('td')).forEach(function(td) {
                addEvent(td, 'mouseover', function() {
                    var elColor = td.getAttribute('data-color');
                    pencilSelectedColor2.style.backgroundColor = '#' + elColor;
                    fillStyleText.value = elColor
                });

                addEvent(td, 'click', function() {
                    var elColor = td.getAttribute('data-color');
                    pencilSelectedColor.style.backgroundColor =
                        pencilSelectedColor2.style.backgroundColor = '#' + elColor;

                    fillStyleText.value = elColor;


                    pencilColorContainer.style.display = 'none';
                });
            })

            // END INIT PENCIL

            addEvent(canvas, 'click', function() {
                hideContainers();
                pencilContainer.style.display = 'block';
                pencilContainer.style.top = (0) + 'px';
                pencilContainer.style.left = (canvas.offsetLeft + canvas.clientWidth) + 'px';

                fillStyleText.focus();
            });

            addEvent(btnPencilDone, 'click', function() {
                pencilContainer.style.display = 'none';
                pencilColorContainer.style.display = 'none';

                pencilLineWidth = strokeStyleText.value;
                pencilStrokeStyle = hexToRGBA(fillStyleText.value, alpha);
            });

            addEvent(pencilSelectedColor, 'click', function() {
                pencilColorContainer.style.display = 'block';
            });
        }

        if (tools.pencil === true) {
            decoratePencil();
            document.getElementById('pencil-icon').style.display = 'block';
        }

        function decorateMarker() {

            function hexToRGBA(h, alpha) {
                return 'rgba(' + hexToRGB(h).join(',') + ',' + alpha + ')';
            }
            var colors = [
                ['FFFFFF', '006600', '000099', 'CC0000', '8C4600'],
                ['CCCCCC', '00CC00', '6633CC', 'FF0000', 'B28500'],
                ['666666', '66FFB2', '006DD9', 'FF7373', 'FF9933'],
                ['333333', '26FF26', '6699FF', 'CC33FF', 'FFCC99'],
                ['000000', 'CCFF99', 'BFDFFF', 'FFBFBF', 'FFFF33']
            ];

            var context = getContext('marker-icon');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Marker');
            };
            image.src = data_uris.marker;

            var markerContainer = find('marker-container'),
                markerColorContainer = find('marker-fill-colors'),
                strokeStyleText = find('marker-stroke-style'),
                markerColorsList = find("marker-colors-list"),
                fillStyleText = find('marker-fill-style'),
                markerSelectedColor = find('marker-selected-color'),
                markerSelectedColor2 = find('marker-selected-color-2'),
                btnMarkerDone = find('marker-done'),
                canvas = context.canvas,
                alpha = 0.2;

            // START INIT MARKER



            markerStrokeStyle = hexToRGBA(fillStyleText.value, alpha)

            markerSelectedColor.style.backgroundColor =
                markerSelectedColor2.style.backgroundColor = '#' + fillStyleText.value;

            colors.forEach(function(colorRow) {
                var row = '<tr>';

                colorRow.forEach(function(color) {
                    row += '<td style="background-color:#' + color + '" data-color="' + color + '"></td>';
                })
                row += '</tr>';

                markerColorsList.innerHTML += row;
            })

            Array.prototype.slice.call(markerColorsList.getElementsByTagName('td')).forEach(function(td) {
                addEvent(td, 'mouseover', function() {
                    var elColor = td.getAttribute('data-color');
                    markerSelectedColor2.style.backgroundColor = '#' + elColor;
                    fillStyleText.value = elColor
                });

                addEvent(td, 'click', function() {
                    var elColor = td.getAttribute('data-color');
                    markerSelectedColor.style.backgroundColor =
                        markerSelectedColor2.style.backgroundColor = '#' + elColor;

                    fillStyleText.value = elColor;


                    markerColorContainer.style.display = 'none';
                });
            })

            // END INIT MARKER

            addEvent(canvas, 'click', function() {
                hideContainers();

                markerContainer.style.display = 'block';
                markerContainer.style.top = (0) + 'px';
                markerContainer.style.left = (canvas.offsetLeft + canvas.clientWidth) + 'px';

                fillStyleText.focus();
            });

            addEvent(btnMarkerDone, 'click', function() {
                markerContainer.style.display = 'none';
                markerColorContainer.style.display = 'none';

                markerLineWidth = strokeStyleText.value;
                markerStrokeStyle = hexToRGBA(fillStyleText.value, alpha);
            });

            addEvent(markerSelectedColor, 'click', function() {
                markerColorContainer.style.display = 'block';
            });
        }

        if (tools.marker === true) {
            decorateMarker();
            document.getElementById('marker-icon').style.display = 'block';
        }

        function decorateEraser() {
            var context = getContext('eraser-icon');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Eraser');
            };
            image.src = data_uris.eraser;
        }

        if (tools.eraser === true) {
            decorateEraser();
            document.getElementById('eraser-icon').style.display = 'block';
        }

        function decorateText() {
            var context = getContext('text-icon');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Text');
            };
            image.src = data_uris.text;
        }

        if (tools.text === true) {
            decorateText();
            document.getElementById('text-icon').style.display = 'block';
        }

        function decorateImage() {
            var context = getContext('image-icon');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Image');
            };
            image.src = data_uris.image;
        }

        if (tools.image === true) {
            decorateImage();
            document.getElementById('image-icon').style.display = 'block';
        }


        function decoratePDF() {
            var context = getContext('pdf-icon');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Pdf');
            };
            image.src = data_uris.pdf;
        }

        if (tools.pdf === true) {
            decoratePDF();
            document.getElementById('pdf-icon').style.display = 'block';
        }

        function decorateArc() {
            var context = getContext('arc');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Arc');
            };
            image.src = data_uris.arc;
        }

        if (tools.arc === true) {
            decorateArc();
            document.getElementById('arc').style.display = 'block';
        }

        function decorateRect() {
            var context = getContext('rectangle');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Rectangle');
            };
            image.src = data_uris.rectangle;
        }

        if (tools.rectangle === true) {
            decorateRect();
            document.getElementById('rectangle').style.display = 'block';
        }

        function decorateQuadratic() {
            var context = getContext('quadratic-curve');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'QuadraticCurve');
            };
            image.src = data_uris.quadratic;
        }

        if (tools.quadratic === true) {
            decorateQuadratic();
            document.getElementById('quadratic-curve').style.display = 'block';
        }

        function decorateBezier() {
            var context = getContext('bezier-curve');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
                bindEvent(context, 'Bezier');
            };
            image.src = data_uris.bezier;
        }

        if (tools.bezier === true) {
            decorateBezier();
            document.getElementById('bezier-curve').style.display = 'block';
        }

        function tempStrokeTheLine(context, width, mx, my, lx, ly) {
            context.beginPath();
            context.lineWidth = width;
            context.moveTo(mx, my);
            context.lineTo(lx, ly);
            context.stroke();
        }

        function decorateLineWidth() {
            var context = getContext('line-width');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
            };
            image.src = data_uris.lineWidth;

            var lineWidthContainer = find('line-width-container'),
                lineWidthText = find('line-width-text'),
                btnLineWidthDone = find('line-width-done'),
                h1 = document.getElementsByTagName('h1')[0],
                canvas = context.canvas;

            addEvent(canvas, 'click', function() {
                hideContainers();

                lineWidthContainer.style.display = 'block';
                lineWidthContainer.style.top = (0) + 'px';
                lineWidthContainer.style.left = (canvas.offsetLeft + canvas.clientWidth) + 'px';

                lineWidthText.focus();
            });

            addEvent(btnLineWidthDone, 'click', function() {
                lineWidthContainer.style.display = 'none';
                lineWidth = lineWidthText.value;
            });
        }

        if (tools.lineWidth === true) {
            decorateLineWidth();
            document.getElementById('line-width').style.display = 'block';
        }

        function decorateColors() {
            var context = getContext('colors');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
            };
            image.src = data_uris.colorsPicker;

            var colorsContainer = find('colors-container'),
                strokeStyleText = find('stroke-style'),
                fillStyleText = find('fill-style'),
                btnColorsDone = find('colors-done'),
                h1 = document.getElementsByTagName('h1')[0],
                canvas = context.canvas;

            addEvent(canvas, 'click', function() {
                hideContainers();

                colorsContainer.style.display = 'block';
                colorsContainer.style.top = (0) + 'px';
                colorsContainer.style.left = (canvas.offsetLeft + canvas.clientWidth) + 'px';

                strokeStyleText.focus();
            });

            addEvent(btnColorsDone, 'click', function() {
                colorsContainer.style.display = 'none';
                strokeStyle = strokeStyleText.value;
                fillStyle = fillStyleText.value;
            });
        }

        if (tools.colorsPicker === true) {
            decorateColors();
            document.getElementById('colors').style.display = 'block';
        }

        function decorateAdditionalOptions() {
            var context = getContext('additional');

            var image = new Image();
            image.onload = function() {
                context.drawImage(image, 4, 4, 32, 32);
            };
            image.src = data_uris.extraOptions;

            var additionalContainer = find('additional-container'),
                btnAdditionalClose = find('additional-close'),
                h1 = document.getElementsByTagName('h1')[0],
                canvas = context.canvas,
                globalAlphaSelect = find('globalAlpha-select'),
                globalCompositeOperationSelect = find('globalCompositeOperation-select');

            addEvent(canvas, 'click', function() {
                hideContainers();

                additionalContainer.style.display = 'block';
                additionalContainer.style.top = (0) + 'px';
                additionalContainer.style.left = (canvas.offsetLeft + canvas.clientWidth) + 'px';
            });

            addEvent(btnAdditionalClose, 'click', function() {
                additionalContainer.style.display = 'none';

                globalAlpha = globalAlphaSelect.value;
                globalCompositeOperation = globalCompositeOperationSelect.value;
                lineCap = lineCapSelect.value;
                lineJoin = lineJoinSelect.value;
            });
        }

        if (tools.extraOptions === true) {
            decorateAdditionalOptions();
            document.getElementById('additional').style.display = 'block';
        }

        var designPreview = find('design-preview'),
            codePreview = find('code-preview');

        // todo: use this function in share-drawings.js
        // to sync buttons' states
        window.selectBtn = function(btn, isSkipWebRTCMessage) {
            codePreview.className = designPreview.className = '';

            if (btn == designPreview) designPreview.className = 'preview-selected';
            else codePreview.className = 'preview-selected';

            if (!isSkipWebRTCMessage && window.connection && connection.numberOfConnectedUsers >= 1) {
                connection.send({
                    btnSelected: btn.id
                });
            } else {
                // to sync buttons' UI-states
                if (btn == designPreview) btnDesignerPreviewClicked();
                else btnCodePreviewClicked();
            }
        };

        addEvent(designPreview, 'click', function() {
            selectBtn(designPreview);
            btnDesignerPreviewClicked();
        });

        function btnDesignerPreviewClicked() {
            codeText.parentNode.style.display = 'none';
            optionsContainer.style.display = 'none';

            hideContainers();
            endLastPath();
        }

        addEvent(codePreview, 'click', function() {
            selectBtn(codePreview);
            btnCodePreviewClicked();
        });

        function btnCodePreviewClicked() {
            codeText.parentNode.style.display = 'block';
            optionsContainer.style.display = 'block';

            codeText.focus();
            common.updateTextArea();

            setHeightForCodeAndOptionsContainer();

            hideContainers();
            endLastPath();
        }

        var codeText = find('code-text'),
            optionsContainer = find('options-container');

        function setHeightForCodeAndOptionsContainer() {
            codeText.style.width = (innerWidth - optionsContainer.clientWidth - 30) + 'px';
            codeText.style.height = (innerHeight - 40) + 'px';

            codeText.style.marginLeft = (optionsContainer.clientWidth) + 'px';
            optionsContainer.style.height = (innerHeight) + 'px';
        }

        var isAbsolute = find('is-absolute-points'),
            isShorten = find('is-shorten-code');

        addEvent(isShorten, 'change', common.updateTextArea);
        addEvent(isAbsolute, 'change', common.updateTextArea);
    })();

    function hideContainers() {
        var additionalContainer = find('additional-container'),
            colorsContainer = find('colors-container'),
            markerContainer = find('marker-container'),
            markerColorContainer = find('marker-fill-colors'),
            pencilContainer = find('pencil-container'),
            pencilColorContainer = find('pencil-fill-colors'),
            lineWidthContainer = find('line-width-container');

        additionalContainer.style.display =
            colorsContainer.style.display =
            markerColorContainer.style.display =
            markerContainer.style.display =
            pencilColorContainer.style.display =
            pencilContainer.style.display =
            lineWidthContainer.style.display = 'none';
    }

    function setTemporaryLine() {
        var arr = ["line", [139, 261, 170, 219],
            [1, "rgba(0,0,0,0)", "rgba(0,0,0,0)", 1, "source-over", "round", "round", "15px \"Arial\""]
        ];
        points.push(arr);
        drawHelper.redraw();

        setTimeout(function() {
            setSelection(document.getElementById('line'), 'Line');
        }, 1000);

        setTimeout(setDefaultSelectedIcon, 2000);
    }

    var canvas = tempContext.canvas;

    var isTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
    
    addEvent(canvas, isTouch ? 'touchstart mousedown' : 'mousedown', function(e) {
        if (isTouch) e = e.pageX ? e : e.touches.length ? e.touches[0] : {
            pageX: 0,
            pageY: 0
        };

        var cache = is;

        if (cache.isLine) lineHandler.mousedown(e);
        else if (cache.isArc) arcHandler.mousedown(e);
        else if (cache.isRectangle) rectHandler.mousedown(e);
        else if (cache.isQuadraticCurve) quadraticHandler.mousedown(e);
        else if (cache.isBezierCurve) bezierHandler.mousedown(e);
        else if (cache.isDragLastPath || cache.isDragAllPaths) dragHelper.mousedown(e);
        else if (cache.isPencil) pencilHandler.mousedown(e);
        else if (cache.isEraser) eraserHandler.mousedown(e);
        else if (cache.isText) textHandler.mousedown(e);
        else if (cache.isImage) imageHandler.mousedown(e);
        else if (cache.isPdf) pdfHandler.mousedown(e);
        else if (cache.isArrow) arrowHandler.mousedown(e);
        else if (cache.isMarker) markerHandler.mousedown(e);

        !cache.isPdf && drawHelper.redraw();

        preventStopEvent(e);
    });

    function preventStopEvent(e) {
        if (!e) {
            return;
        }

        if (typeof e.preventDefault === 'function') {
            e.preventDefault();
        }

        if (typeof e.stopPropagation === 'function') {
            e.stopPropagation();
        }
    }

    addEvent(canvas, isTouch ? 'touchend touchcancel mouseup' : 'mouseup', function(e) {
        if (isTouch && (!e || !('pageX' in e))) {
            if (e && e.touches && e.touches.length) {
                e = e.touches[0];
            } else if (e && e.changedTouches && e.changedTouches.length) {
                e = e.changedTouches[0];
            } else {
                e = {
                    pageX: 0,
                    pageY: 0
                }
            }
        }

        var cache = is;

        if (cache.isLine) lineHandler.mouseup(e);
        else if (cache.isArc) arcHandler.mouseup(e);
        else if (cache.isRectangle) rectHandler.mouseup(e);
        else if (cache.isQuadraticCurve) quadraticHandler.mouseup(e);
        else if (cache.isBezierCurve) bezierHandler.mouseup(e);
        else if (cache.isDragLastPath || cache.isDragAllPaths) dragHelper.mouseup(e);
        else if (cache.isPencil) pencilHandler.mouseup(e);
        else if (cache.isEraser) eraserHandler.mouseup(e);
        else if (cache.isText) textHandler.mouseup(e);
        else if (cache.isImage) imageHandler.mouseup(e);
        else if (cache.isPdf) pdfHandler.mousedown(e);
        else if (cache.isArrow) arrowHandler.mouseup(e);
        else if (cache.isMarker) markerHandler.mouseup(e);

        !cache.isPdf && drawHelper.redraw();

        syncPoints(is.isDragAllPaths || is.isDragLastPath ? true : false);

        preventStopEvent(e);
    });

    addEvent(canvas, isTouch ? 'touchmove mousemove' : 'mousemove', function(e) {
        if (isTouch) e = e.pageX ? e : e.touches.length ? e.touches[0] : {
            pageX: 0,
            pageY: 0
        };

        var cache = is;

        if (cache.isLine) lineHandler.mousemove(e);
        else if (cache.isArc) arcHandler.mousemove(e);
        else if (cache.isRectangle) rectHandler.mousemove(e);
        else if (cache.isQuadraticCurve) quadraticHandler.mousemove(e);
        else if (cache.isBezierCurve) bezierHandler.mousemove(e);
        else if (cache.isDragLastPath || cache.isDragAllPaths) dragHelper.mousemove(e);
        else if (cache.isPencil) pencilHandler.mousemove(e);
        else if (cache.isEraser) eraserHandler.mousemove(e);
        else if (cache.isText) textHandler.mousemove(e);
        else if (cache.isImage) imageHandler.mousemove(e);
        else if (cache.isPdf) pdfHandler.mousedown(e);
        else if (cache.isArrow) arrowHandler.mousemove(e);
        else if (cache.isMarker) markerHandler.mousemove(e);

        preventStopEvent(e);
    });

    var keyCode;

    function onkeydown(e) {
        keyCode = e.which || e.keyCode || 0;

        if (keyCode == 8 || keyCode == 46) {
            if (isBackKey(e, keyCode)) {
                // back key pressed
            }
            return;
        }

        if (e.metaKey) {
            isControlKeyPressed = true;
            keyCode = 17;
        }

        if (!isControlKeyPressed && keyCode === 17) {
            isControlKeyPressed = true;
        }
    }

    function isBackKey(e, keyCode) {
        var doPrevent = false;
        var d = e.srcElement || e.target;
        if ((d.tagName.toUpperCase() === 'INPUT' &&
                (
                    d.type.toUpperCase() === 'TEXT' ||
                    d.type.toUpperCase() === 'PASSWORD' ||
                    d.type.toUpperCase() === 'FILE' ||
                    d.type.toUpperCase() === 'SEARCH' ||
                    d.type.toUpperCase() === 'EMAIL' ||
                    d.type.toUpperCase() === 'NUMBER' ||
                    d.type.toUpperCase() === 'DATE')
            ) ||
            d.tagName.toUpperCase() === 'TEXTAREA') {
            doPrevent = d.readOnly || d.disabled;
        } else {
            doPrevent = true;
        }

        if (doPrevent) {
            e.preventDefault();
        }
        return doPrevent;
    }

    addEvent(document, 'keydown', onkeydown);

    function onkeyup(e) {
        if (e.which == null && (e.charCode != null || e.keyCode != null)) {
            e.which = e.charCode != null ? e.charCode : e.keyCode;
        }

        keyCode = e.which || e.keyCode || 0;

        if (keyCode === 13 && is.isText) {
            textHandler.onReturnKeyPressed();
            return;
        }

        if (keyCode == 8 || keyCode == 46) {
            if (isBackKey(e, keyCode)) {
                textHandler.writeText(textHandler.lastKeyPress, true);
            }
            return;
        }

        // Ctrl + t
        if (isControlKeyPressed && keyCode === 84 && is.isText) {
            textHandler.showTextTools();
            return;
        }

        // Ctrl + z
        if (isControlKeyPressed && keyCode === 90) {
            if (points.length) {
                points.length = points.length - 1;
                drawHelper.redraw();

                syncPoints(is.isDragAllPaths || is.isDragLastPath ? true : false);
            }
        }

        // Ctrl + a
        if (isControlKeyPressed && keyCode === 65) {
            dragHelper.global.startingIndex = 0;

            endLastPath();

            setSelection(find('drag-all-paths'), 'DragAllPaths');
        }

        // Ctrl + c
        if (isControlKeyPressed && keyCode === 67 && points.length) {
            copy();
        }

        // Ctrl + v
        if (isControlKeyPressed && keyCode === 86 && copiedStuff.length) {
            paste();

            syncPoints(is.isDragAllPaths || is.isDragLastPath ? true : false);
        }

        // Ending the Control Key
        if (typeof e.metaKey !== 'undefined' && e.metaKey === false) {
            isControlKeyPressed = false;
            keyCode = 17;
        }

        if (keyCode === 17) {
            isControlKeyPressed = false;
        }
    }

    addEvent(document, 'keyup', onkeyup);

    function onkeypress(e) {
        if (e.which == null && (e.charCode != null || e.keyCode != null)) {
            e.which = e.charCode != null ? e.charCode : e.keyCode;
        }

        keyCode = e.which || e.keyCode || 0;

        var inp = String.fromCharCode(keyCode);
        textHandler.writeText(String.fromCharCode(keyCode));
//        if (/[а-ьА-Яa-zA-Z0-9-_ !?|\/'",.=:;(){}\[\]`~@#$%^&*+-]/.test(inp)) {
//            textHandler.writeText(String.fromCharCode(keyCode));
//        }
    }

    addEvent(document, 'keypress', onkeypress);

    function onTextFromClipboard(e) {
        if (!is.isText) return;
        var pastedText = undefined;
        if (window.clipboardData && window.clipboardData.getData) { // IE
            pastedText = window.clipboardData.getData('Text');
        } else if (e.clipboardData && e.clipboardData.getData) {
            pastedText = e.clipboardData.getData('text/plain');
        }
        if (pastedText && pastedText.length) {
            textHandler.writeText(pastedText);
        }
    }

    addEvent(document, 'paste', onTextFromClipboard);

    // scripts on this page directly touches DOM-elements
    // removing or altering anything may cause failures in the UI event handlers
    // it is used only to bring collaboration for canvas-surface
    var lastPointIndex = 0;

    var uid;

    window.addEventListener('message', function(event) {
        if (!event.data) return;

        if (!uid) {
            uid = event.data.uid;
        }

        if (event.data.captureStream) {
            webrtcHandler.createOffer(function(sdp) {
                sdp.uid = uid;
                window.parent.postMessage(sdp, '*');
            });
            return;
        }

        if (event.data.renderStream) {
            setTemporaryLine();
            return;
        }

        if (event.data.sdp) {
            webrtcHandler.setRemoteDescription(event.data);
            return;
        }

        if (event.data.genDataURL) {
            var dataURL = context.canvas.toDataURL(event.data.format, 1);
            window.parent.postMessage({
                dataURL: dataURL,
                uid: uid
            }, '*');
            return;
        }

        if (event.data.undo && points.length) {
            var index = event.data.index;

            if (event.data.tool) {
                var newArray = [];
                var length = points.length;
                var reverse = points.reverse();
                for (var i = 0; i < length; i++) {
                    var point = reverse[i];
                    if (point[0] !== event.data.tool) {
                        newArray.push(point);
                    }
                }
                points = newArray.reverse();
                drawHelper.redraw();
                syncPoints(true);
                return;
            }

            if (index === 'all') {
                points = [];
                drawHelper.redraw();
                syncPoints(true);
                return;
            }

            if (index.numberOfLastShapes) {
                try {
                    points.length -= index.numberOfLastShapes;
                } catch (e) {
                    points = [];
                }

                drawHelper.redraw();
                syncPoints(true);
                return;
            }

            if (index === -1) {
                if (points.length && points[points.length - 1][0] === 'pencil') {
                    var newArray = [];
                    var length = points.length;
                    var reverse = points.reverse();
                    var ended;
                    for (var i = 0; i < length; i++) {
                        var point = reverse[i];
                        if (point[3] == 'start') {
                            ended = true;
                        } else if (ended) {
                            newArray.push(point);
                        }
                    }

                    points = newArray.reverse();
                    drawHelper.redraw();
                    syncPoints(true);
                    return;
                }

                points.length = points.length - 1;
                drawHelper.redraw();
                syncPoints(true);
                return;
            }

            if (points[index]) {
                var newPoints = [];
                for (var i = 0; i < points.length; i++) {
                    if (i !== index) {
                        newPoints.push(points[i]);
                    }
                }
                points = newPoints;
                drawHelper.redraw();
                syncPoints(true);
            }
            return;
        }

        if (event.data.syncPoints) {
            syncPoints(true);
            return;
        }

        if (event.data.clearCanvas) {
            points = [];
            drawHelper.redraw();
            return;
        }

        if (!event.data.canvasDesignerSyncData) return;

        // drawing is shared here (array of points)
        var d = event.data.canvasDesignerSyncData;

        if (d.startIndex !== 0) {
            for (var i = 0; i < d.points.length; i++) {
                points[i + d.startIndex] = d.points[i];
            }
        } else {
            points = d.points;
        }

        lastPointIndex = points.length;

        // redraw the <canvas> surfaces
        drawHelper.redraw();
    }, false);

    function syncPoints(isSyncAll) {
        if (isSyncAll) {
            lastPointIndex = 0;
        }

        if (lastPointIndex == points.length) return;

        var pointsToShare = [];
        for (var i = lastPointIndex; i < points.length; i++) {
            pointsToShare[i - lastPointIndex] = points[i];
        }

        if (pointsToShare.length) {
            syncData({
                points: pointsToShare || [],
                startIndex: lastPointIndex
            });
        }

        if (!pointsToShare.length && points.length) return;

        lastPointIndex = points.length;
    }

    function syncData(data) {
        window.parent.postMessage({
            canvasDesignerSyncData: data,
            uid: uid
        }, '*');
    }

    var webrtcHandler = {
        createOffer: function(callback) {
            var captureStream = document.getElementById('main-canvas').captureStream();
            var peer = this.getPeer();

            captureStream.getTracks().forEach(function(track) {
                peer.addTrack(track, captureStream);
            });

            peer.onicecandidate = function(event) {
                if (!event || !!event.candidate) {
                    return;
                }

                callback({
                    sdp: peer.localDescription.sdp,
                    type: peer.localDescription.type
                });
            };
            peer.createOffer({
                OfferToReceiveAudio: false,
                OfferToReceiveVideo: false
            }).then(function(sdp) {
                peer.setLocalDescription(sdp);
            });
        },
        setRemoteDescription: function(sdp) {
            this.peer.setRemoteDescription(new RTCSessionDescription(sdp)).then(function() {
                if (typeof setTemporaryLine === 'function') {
                    setTemporaryLine();
                }
            });
        },
        createAnswer: function(sdp, callback) {
            var peer = this.getPeer();
            peer.onicecandidate = function(event) {
                if (!event || !!event.candidate) {
                    return;
                }

                callback({
                    sdp: peer.localDescription.sdp,
                    type: peer.localDescription.type
                });
            };
            this.peer.setRemoteDescription(new RTCSessionDescription(sdp)).then(function() {
                peer.createAnswer({
                    OfferToReceiveAudio: false,
                    OfferToReceiveVideo: true
                }).then(function(sdp) {
                    peer.setLocalDescription(sdp);
                });
            });

            peer.ontrack = function(event) {
                callback({
                    stream: event.streams[0]
                });
            };
        },
        getPeer: function() {
            var WebRTC_Native_Peer = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
            var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription;
            var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate;

            var peer = new WebRTC_Native_Peer(null);
            this.peer = peer;
            return peer;
        }
    };
})();
