Torn CT Map Draw Class

try to draw all the world!

// ==UserScript==
// @name         Torn CT Map Draw Class
// @namespace    http://tampermonkey.net/
// @version      0.9
// @description  try to draw all the world!
// @author       Jox [1714547]
// @grant        none
// @match        *
// ==/UserScript==

class ctMapDraw{

    constructor(element){
        this.element = element;

        this.ctx = null;
        this.ctxTemp = null;
        this.ctxBG = null;
        this.ctxGrid = null;

        this.drawSize = 10000;
        this.drawOffset = {x: 0, y:0};

        this.drawMap = false;

        this.addGlobalStyle('.ctMapDrawer {height: 600px; background-color: white; overflow: auto; padding: 5px;}');
        this.addGlobalStyle('.ctMapDrawerCanvasContainer {position: relative;}');
        this.addGlobalStyle('.ctMapDrawerCanvas {position: absolute; top: 0; left: 0;}');
        this.addGlobalStyle('.ctMapDrawerControls {position: sticky; top: 0; left: 0; background-color: rgb(255,255,255,0.8); padding: 5px; border-radius: 5px;}');
        this.addGlobalStyle('.ctMapDrawerControls > * {margin-left: 5px; vertical-align: middle;}');
        this.addGlobalStyle('.ctMapVisible{display: none;}');
        this.addGlobalStyle('.ctMapRing {animation: ring 4s .7s ease-in-out infinite; } @keyframes ring {0% { transform: rotate(0); } 1% { transform: rotate(15deg); } 3% { transform: rotate(-14deg); } 5% { transform: rotate(17deg); } 7% { transform: rotate(-16deg); } 9% { transform: rotate(15deg); } 11% { transform: rotate(-14deg); } 13% { transform: rotate(13deg); } 15% { transform: rotate(-12deg);} 17% { transform: rotate(11deg); } 19% { transform: rotate(-10deg); } 21% { transform: rotate(9deg); } 23% { transform: rotate(-8deg); } 25% { transform: rotate(7deg); } 27% { transform: rotate(-6deg); } 29% { transform: rotate(5deg); } 31% { transform: rotate(-4deg); } 33% { transform: rotate(3deg); } 35% { transform: rotate(-2deg); } 37% { transform: rotate(1de  39% { transform: rotate(0); } 100% { transform: rotate(0); }}');
        this.addGlobalStyle('#ctUser1714547 svg {fill: yellow;}');

        var div = document.createElement('div');
        div.style.marginBottom = '10px';

        //Create header
        let header = document.createElement('div');
        header.classList.add('title-black', 'm-top10', 'title-toggle', 'active', 'top-round');
        header.innerHTML = 'CT Map Drawing by Jox'
        header.onclick = function(e){
            document.getElementById('ctMapDrawer').classList.toggle('ctMapVisible');
            if(document.getElementById('ctMapDrawer').classList.contains('ctMapVisible')){
                e.srcElement.innerHTML = 'CT Map Drawing by Jox (expand)';
            }
            else{
                e.srcElement.innerHTML = 'CT Map Drawing by Jox (collapse)';
            }
        }

        //Create body
        let body = document.createElement('div');
        body.classList.add('cont-gray10', 'bottom-round', 'cont-toggle', 'unreset');

        body.id = 'ctMapDrawer';
        body.classList.add('ctMapDrawer', 'ctMapVisible');
        if(body.classList.contains('ctMapVisible')){
            header.innerHTML = 'CT Map Drawing by Jox (expand)';
        }
        else{
            header.innerHTML = 'CT Map Drawing by Jox (collapse)';
        }

        //Add header and body elements
        div.appendChild(header);
        div.appendChild(body);

        this.container = body;

        this.element.appendChild(div);

        this.addMapDrawer();
    }

    addMapDrawer(){
        var that = this;

        //reseting variable
        this.drawMap = false;

        //clearing container
        this.container.innerHTML = '';

        let a = document.createElement('a');
        a.href = '#';
        a.innerHTML = 'Start Drawing';
        this.container.appendChild(a);

        a.addEventListener("click", function(e) {
            e.preventDefault();
            that.startDrawing();
        }, false);


        let p = document.createElement('p');
        p.innerHTML = ' or select saved image to continue...';
        p.style.margin = '5px 0';
        this.container.appendChild(p);

        let input = document.createElement('input');
        input.type = 'file';
        input.id = 'savedMapImage';
        this.container.appendChild(input);

        input.addEventListener("change", function(event) {
            var files = input.files;
            if (files.length) {
                if(files[0].type == 'image/png'){
                    that.startDrawing(files[0]);
                }
            }

        }, false);
    }

    startDrawing(file){
        var that = this;

        let world = document.getElementById('world');
        let worldTransform = world.style.transform.replace('translate(','').replace(')','').replace(/px/g,'').split(',').map(x => {return Number(x) * -1});

        //Making drawOffset
        this.drawOffset.x = worldTransform[0] - (this.drawSize / 2);
        this.drawOffset.y = worldTransform[1] - (this.drawSize / 2);

        if(this.drawOffset.x < 0){
            this.drawOffset.x = 0;
        }

        if(this.drawOffset.y < 0){
            this.drawOffset.y = 0;
        }

        let canvasContainer = document.createElement('div');
        canvasContainer.classList.add('ctMapDrawerCanvasContainer');
        canvasContainer.id = 'ctMapDrawerCanvasContainer';
        canvasContainer.style.zoom = '50%';

        //Main canvas for image
        let canvas = document.createElement('canvas');
        canvas.id = 'ctMapDrawerCanvas'
        canvas.width = this.drawSize; //Number(world.style.width.replace('px',''));
        canvas.height = this.drawSize; //Number(world.style.height.replace('px',''));
        canvas.classList.add('ctMapDrawerCanvas');
        canvas.style.zIndex = 1

        //temp canvas for drawing
        let canvasTemp = document.createElement('canvas');
        canvasTemp.id = 'ctMapDrawerCanvasTemp'
        canvasTemp.width = this.drawSize; //Number(world.style.width.replace('px',''));
        canvasTemp.height = this.drawSize; //Number(world.style.height.replace('px',''));
        canvasTemp.style.display = 'none';

        //background canvas for drawing background
        let canvasBG = document.createElement('canvas');
        canvasBG.id = 'ctMapDrawerCanvasBG'
        canvasBG.width = this.drawSize; //Number(world.style.width.replace('px',''));
        canvasBG.height = this.drawSize; //Number(world.style.height.replace('px',''));
        canvasBG.classList.add('ctMapDrawerCanvas');
        canvasBG.style.zIndex = 0

        //grid canvas for drawing grid lines
        let canvasGrid = document.createElement('canvas');
        canvasGrid.id = 'ctMapDrawerCanvasGrid'
        canvasGrid.width = this.drawSize; //Number(world.style.width.replace('px','')) ;
        canvasGrid.height = this.drawSize; //Number(world.style.height.replace('px',''));
        canvasGrid.classList.add('ctMapDrawerCanvas');
        canvasGrid.style.zIndex = 2

        //Contols to show hide backgorund and grid
        let div = document.createElement('div');
        div.classList.add('ctMapDrawerControls');
        div.style.zIndex = 10;

        let cbBG = document.createElement('input');
        cbBG.id = 'ctMapDrawerControlCbBG';
        cbBG.type = 'checkbox';
        cbBG.checked = true;
        cbBG.addEventListener('click', function(e){
            if(this.checked){
                document.getElementById('ctMapDrawerCanvasBG').style.display = null;
            }
            else{
                document.getElementById('ctMapDrawerCanvasBG').style.display = 'none';
            }
        })
        let lblBG = document.createElement('label');
        lblBG.htmlFor = 'ctMapDrawerControlCbBG';
        lblBG.innerHTML = 'Background';

        let cbGrid = document.createElement('input');
        cbGrid.id = 'ctMapDrawerControlCbGrid';
        cbGrid.type = 'checkbox';
        cbGrid.checked = true;
        cbGrid.addEventListener('click', function(e){
            if(this.checked){
                document.getElementById('ctMapDrawerCanvasGrid').style.display = null;
            }
            else{
                document.getElementById('ctMapDrawerCanvasGrid').style.display = 'none';
            }
        })
        let lblGrid = document.createElement('label');
        lblGrid.htmlFor = 'ctMapDrawerControlCbGrid';
        lblGrid.innerHTML = 'Grid';

        let cbPlayer = document.createElement('input');
        cbPlayer.id = 'ctMapDrawerControlCbPlayer';
        cbPlayer.type = 'checkbox';
        cbPlayer.checked = true;
        cbPlayer.addEventListener('click', function(e){
            if(this.checked){
                document.getElementById('ctMapDivPlayer').style.display = null;
            }
            else{
                document.getElementById('ctMapDivPlayer').style.display = 'none';
            }
        })
        let lblPlayer = document.createElement('label');
        lblPlayer.htmlFor = 'ctMapDrawerControlCbPlayer';
        lblPlayer.innerHTML = 'Player';

        let linkSave = document.createElement('a');
        linkSave.href = '#';
        linkSave.innerHTML = 'Save image';
        linkSave.style.float = 'right';
        linkSave.addEventListener('click', e => {that.saveImage(e, 'save')});

        let linkExport = document.createElement('a');
        linkExport.href = '#';
        linkExport.innerHTML = 'Export image';
        linkExport.style.float = 'right';
        linkExport.addEventListener('click', e => {that.saveImage(e, 'export')});

        //Contols to show zoom map
        let aZoom25 = document.createElement('a');
        aZoom25.href = '#';
        aZoom25.innerHTML = '25%';
        aZoom25.addEventListener('click', function(e){
            e.preventDefault();
            document.getElementById('ctMapDrawerCanvasContainer').style.zoom = '25%';
            that.autoScrollMap();
        });

        let aZoom50 = document.createElement('a');
        aZoom50.href = '#';
        aZoom50.innerHTML = '50%';
        aZoom50.addEventListener('click', function(e){
            e.preventDefault();
            document.getElementById('ctMapDrawerCanvasContainer').style.zoom = '50%';
            that.autoScrollMap();
        });

        let aZoom75 = document.createElement('a');
        aZoom75.href = '#';
        aZoom75.innerHTML = '75%';
        aZoom75.addEventListener('click', function(e){
            e.preventDefault();
            document.getElementById('ctMapDrawerCanvasContainer').style.zoom = '75%';
            that.autoScrollMap();
        });

        let aZoom100 = document.createElement('a');
        aZoom100.href = '#';
        aZoom100.innerHTML = '100%';
        aZoom100.addEventListener('click', function(e){
            e.preventDefault();
            document.getElementById('ctMapDrawerCanvasContainer').style.zoom = '100%';
            that.autoScrollMap();
        });

        let aZoom125 = document.createElement('a');
        aZoom125.href = '#';
        aZoom125.innerHTML = '125%';
        aZoom125.addEventListener('click', function(e){
            e.preventDefault();
            document.getElementById('ctMapDrawerCanvasContainer').style.zoom = '125%';
            that.autoScrollMap();
        });

        let aZoom150 = document.createElement('a');
        aZoom150.href = '#';
        aZoom150.innerHTML = '150%';
        aZoom150.addEventListener('click', function(e){
            e.preventDefault();
            document.getElementById('ctMapDrawerCanvasContainer').style.zoom = '150%';
            that.autoScrollMap();
        });

        //Action to restart map drawing
        let aRestart = document.createElement('a');
        aRestart.href = '#';
        aRestart.innerHTML = 'Restart';
        aRestart.addEventListener('click', function(e){
            e.preventDefault();
            if (confirm("Restart drawing? All unsaved changes will be lost!")) {
                that.addMapDrawer();
            }
        });

        //Add filters to image
        let aFilters = document.createElement('a');
        aFilters.href = '#';
        aFilters.innerHTML = 'Blueprint';
        aFilters.addEventListener('click', function(e){
            e.preventDefault();
            if(that.ctx.canvas.style.filter == '' || that.ctx.canvas.style.filter == 'none'){
                that.ctx.canvas.style.filter = 'brightness(150%) contrast(10%)';
            }
            else{
                that.ctx.canvas.style.filter = null;
            }
        });

        //div to show playr position
        let divPlayer = document.createElement('div');
        divPlayer.id = 'ctMapDivPlayer';
        divPlayer.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" filter="" fill="lime" stroke="black" stroke-width="0.3" viewBox="0 0 16 16"><g><path class="iconPath" d="M 8 4.2773 C 8.9482 4.2773 9.7173 3.5437 9.7173 2.6389 C 9.7173 1.7336 8.9482 1 8 1 C 7.0515 1 6.2825 1.7336 6.2825 2.6389 C 6.2825 3.5437 7.0515 4.2773 8 4.2773 ZM 10.9873 8.1191 L 10.6292 5.6429 C 10.5574 5.1545 10.0758 4.7334 9.5591 4.7334 L 6.4407 4.7334 C 5.9236 4.7334 5.4418 5.1545 5.3706 5.6429 L 5.0128 8.1084 C 4.9413 8.5972 5.177 9.0024 5.5363 9.0024 C 5.8955 9.0024 6.2214 9.4077 6.2595 9.8997 L 6.5886 14.104 C 6.6267 14.5962 7.0815 15 7.5988 15 L 8.4008 15 C 8.9182 15 9.3727 14.5973 9.4109 14.1055 L 9.7396 9.9025 C 9.7782 9.4103 10.1041 9.0076 10.4633 9.0076 C 10.8225 9.0076 11.0585 8.6079 10.9873 8.1191 Z"></path></g></svg>'
        divPlayer.style.position = 'absolute';
        divPlayer.style.zIndex = 5;
        divPlayer.classList.add('ctMapRing');
        //divPlayer.querySelector('svg').style.fill = 'lime';


        div.appendChild(aRestart);

        div.appendChild(cbBG);
        div.appendChild(lblBG);
        div.appendChild(cbGrid);
        div.appendChild(lblGrid);
        div.appendChild(cbPlayer);
        div.appendChild(lblPlayer);

        div.appendChild(aZoom25);
        div.appendChild(aZoom50);
        div.appendChild(aZoom75);
        div.appendChild(aZoom100);
        div.appendChild(aZoom125);
        div.appendChild(aZoom150);

        div.appendChild(aFilters);

        div.appendChild(linkExport);
        div.appendChild(linkSave);

        canvasContainer.appendChild(canvas);
        canvasContainer.appendChild(canvasTemp);
        canvasContainer.appendChild(canvasBG);
        canvasContainer.appendChild(canvasGrid);
        canvasContainer.appendChild(divPlayer);

        this.container.innerHTML = '';
        this.container.appendChild(canvasContainer);
        this.container.appendChild(div);

        this.ctx = canvas.getContext("2d");
        this.ctxTemp = canvasTemp.getContext("2d");
        this.ctxBG = canvasBG.getContext("2d");
        this.ctxGrid = canvasGrid.getContext("2d");

        //Draw background
        var imgBG = new Image();
        imgBG.onload = function(){
            // create pattern
            var ptrn = that.ctxBG.createPattern(imgBG, 'repeat'); // Create a pattern with this image, and set it to "repeat".
            that.ctxBG.fillStyle = ptrn;
            that.ctxBG.fillRect(0, 0, that.ctxBG.canvas.width, that.ctxBG.canvas.height); // context.fillRect(x, y, width, height);
        }
        imgBG.src = 'https://www.torn.com/images/v2/christmas_town/snow.jpg';

        //Draw grid
        var imgGrid = new Image();
        imgGrid.onload = function(){
            // create pattern
            var ptrn = that.ctxGrid.createPattern(imgGrid, 'repeat'); // Create a pattern with this image, and set it to "repeat".
            that.ctxGrid.fillStyle = ptrn;
            that.ctxGrid.fillRect(0, 0, that.ctxGrid.canvas.width, that.ctxGrid.canvas.height); // context.fillRect(x, y, width, height);
        }
        imgGrid.src = 'https://www.torn.com/images/v2/christmas_town/map_grid.png';

        if(file){
            var img = new Image;
            img.onload = function() {
                that.ctx.drawImage(img, 0, 0);

                that.drawMap = true;
                that.draw();
            }
            img.src = URL.createObjectURL(file);
        }
        else{
            this.drawMap = true;
            this.draw();
        }
    }

    drawOnMap(){
        if(this.drawMap){
            let drawingObjects = [];

            let adminMap = document.querySelector('.admin-map');

            let world = document.getElementById('world');
            let worldTransform = world.style.transform.replace('translate(','').replace(')','').replace(/px/g,'').split(',').map(x => {return Number(x) * -1});

            let negativeCoordinates = document.querySelector('.negative-coordinates');
            let objectsLayer = document.querySelector('.objects-layer');
            let objects = objectsLayer.querySelectorAll('.ct-object');

            let negativeX = Number(negativeCoordinates.style.left.replace('px','') || 0);
            let negativeY = Number(negativeCoordinates.style.top.replace('px','') || 0);

            let mapOverview = document.querySelector('.map-overview');

            let tilesaround = (adminMap ? 0 : 1);
            let tileSize = 30;

            let player = {
                x: worldTransform[0] + (mapOverview.clientWidth / 2) - this.drawOffset.x,
                y: worldTransform[1] + (mapOverview.clientWidth / 2) - this.drawOffset.y,
                halfW: (mapOverview.clientWidth / 2) + (tilesaround * tileSize),
                halfH: (mapOverview.clientWidth / 2) + (tilesaround * tileSize)
            };

            this.ctxTemp.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

            for(let object of objects){
                let dx = negativeX + Number(object.style.left.replace('px','')) - this.drawOffset.x;
                let dy = negativeY + Number(object.style.top.replace('px','')) - this.drawOffset.y;
                let image = object.querySelector('img');

                if(image && image.complete && image.naturalHeight !== 0){
                    this.ctxTemp.drawImage(image, dx, dy);
                }
            }

            this.ctx.clearRect(player.x-player.halfW, player.y-player.halfH, player.halfW * 2, player.halfH * 2);

            this.ctx.drawImage(this.ctxTemp.canvas,
                               player.x-player.halfW, player.y-player.halfH, player.halfW * 2, player.halfH * 2, //source
                               player.x-player.halfW, player.y-player.halfH, player.halfW * 2, player.halfH * 2 //destination
                              );

            this.autoScrollMap();
        }
    }

    autoScrollMap(){

        let world = document.getElementById('world');
        let worldTransform = world.style.transform.replace('translate(','').replace(')','').replace(/px/g,'').split(',').map(x => {return Number(x) * -1});

        let negativeCoordinates = document.querySelector('.negative-coordinates');
        let mapOverview = document.querySelector('.map-overview');
        //let mapControls = document.querySelector('.map-controls');

        let negativeX = Number(negativeCoordinates.style.left.replace('px','') || 0);
        let negativeY = Number(negativeCoordinates.style.top.replace('px','') || 0);

        let player = {
            //x: (mapControls ? Number(mapControls.style.left.replace('px','')) : 0) + negativeX + mapOverview.clientWidth / 2,
            //y: (mapControls ? Number(mapControls.style.top.replace('px','')) : 0) + negativeY + mapOverview.clientWidth / 2,
            x: worldTransform[0] + (mapOverview.clientWidth / 2) - this.drawOffset.x,
            y: worldTransform[1] + (mapOverview.clientWidth / 2) - this.drawOffset.y,
            halfW: (mapOverview.clientWidth / 2),
            halfH: mapOverview.clientWidth / 2
        };

        let zoom = Number(document.getElementById('ctMapDrawerCanvasContainer').style.zoom.replace('%','')) / 100;

        this.container.scroll({
            top: player.y * zoom - this.container.clientHeight/2,
            left: player.x * zoom - this.container.clientWidth/2,
            behavior: 'smooth'
        })

        //Update player size / position
        let divPlayer = document.getElementById('ctMapDivPlayer');
        let divPlayerW = 20 * ((1/zoom) < 1 ? 1 : (1/zoom));
        let divPlayerH = 20 * ((1/zoom) < 1 ? 1 : (1/zoom));
        divPlayer.style.width = divPlayerW + 'px';
        divPlayer.style.height = divPlayerH + 'px';
        divPlayer.style.top = player.y - (divPlayerH/2) + 'px';
        divPlayer.style.left = player.x - (divPlayerW/2) + 'px';
    }

    addGlobalStyle(css) {
        var head, style;
        head = document.getElementsByTagName('head')[0];
        if (!head) { return; }
        style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = css;
        head.appendChild(style);
    }

    saveImage(e, mode){
        e.preventDefault();

        let world = document.getElementById('world');

        let exportCanvas = document.createElement('canvas');
        exportCanvas.width = this.drawSize//Number(world.style.width.replace('px',''));
        exportCanvas.height = this.drawSize//Number(world.style.height.replace('px',''));

        let ctxExport = exportCanvas.getContext("2d");

        if(mode == 'export'){
            //check for blueprint mode
            if(confirm("Export image in blueprint mode?")){
                ctxExport.filter = 'brightness(150%) contrast(10%)';
            }

            //check for background
            if(confirm("Add background to export image?")){
                //Draw background image
                ctxExport.drawImage(this.ctxBG.canvas,
                                    0, 0, exportCanvas.width, exportCanvas.height, //source
                                    0, 0, exportCanvas.width, exportCanvas.height //destination
                                   );
            }
        }

        //Draw main image
        ctxExport.drawImage(this.ctx.canvas,
                            0, 0, exportCanvas.width, exportCanvas.height, //source
                            0, 0, exportCanvas.width, exportCanvas.height //destination
                           );



        if(mode == 'export'){
            //check for grid
            if(confirm("Add grid to export image?")){
                //Draw background image
                ctxExport.drawImage(this.ctxGrid.canvas,
                                    0, 0, exportCanvas.width, exportCanvas.height, //source
                                    0, 0, exportCanvas.width, exportCanvas.height //destination
                                   );
            }
        }


        let mapTitle = document.querySelector('#christmastownroot .status-title');
        let mapTitle2 = document.querySelector('#christmastownroot .title-wrap .text');
        let mapName = "ctMap.png";

        if(mapTitle && mapTitle.childNodes[0]){
            mapName = mapTitle.childNodes[0].data + '.png';
        }
        if(mapTitle2 && mapTitle2.childNodes[0]){
            mapName = mapTitle2.innerHTML + '.png';
        }


        //Download image
        exportCanvas.toBlob(function(blob) {
            let link = document.createElement("a");
            link.download = mapName;
            link.href = URL.createObjectURL(blob);
            link.click();
        }, "image/png");
    }

    draw(){
        var that = this;
        setTimeout(function(){that.drawOnMap();},200);
    }
}