Lex_The_Great / InkMods

// ==UserScript==
// @name         InkMods
// @license      MIT
// @copyright    2017, Lex_The_Great (https://openuserjs.org/users/Lex_The_Great)
// @namespace    https://www.reddit.com/r/inkarnate/
// @updateURL    https://openuserjs.org/meta/Lex_The_Great/InkMods.meta.js
// @version      1.6
// @description  Inkarnate Mods!
// @author       Lex_The_Great
// @include      http*://*/maps*
// @require      http://code.jquery.com/jquery-latest.js
// @require      https://openuserjs.org/src/libs/sizzle/GM_config.js
// @require      https://gist.githubusercontent.com/PizzaBrandon/5709010/raw/e539a6f16c10465eb948b9ef6b0fe1d4c17a7c3e/jquery.waituntilexists.js
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_webRequest
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==
// Some reason /^(.*)\/maps\#\/(.*)$/ doesn't work so we will use *....

var ImageBank = {
    Storage: [], // ["imageurl", imagedata]
    load: function () {
        window.ImageBank.Storage = gmGet("ImageBank", []);
        console.log("Load Storage");
    },
    save: function () {
        gmSet("ImageBank", this.Storage);
        console.log("Save Storage");
    },
    add: function(url, data) {
        window.ImageBank.Storage.push([url,data]);
        console.log("Saved Image In Storage: ", url);
    },
    contains: function(url) {
        for(var i = 0; i < this.Storage.length; i++) {
            var itemObject = window.ImageBank.Storage[i];
            // [0] url, [1] data
            if (itemObject[0] == url) {
                return i;
            }
        }
        return false;
    },
    get: function(url) {
        var ObjectId = window.ImageBank.contains(url);
        if (ObjectId != false) {
            console.log("Load From Storage: ", url);
            return this.Storage[ObjectId][1];
        } else {
            console.log("Saved to storage: ", url);
            var image = new Image();
            image.crossOrigin = "anonymous";
            image.onload = function () {
                var canvas = document.createElement('canvas');
                canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size
                canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size

                canvas.getContext('2d').drawImage(this, 0, 0);

                window.ImageBank.add(url,canvas.toDataURL('image/png'));
            };
            image.src = url;
            return url;
        }
    }
};
window.ImageBank = ImageBank;

function gmGet(name) {
    var theValue = GM_getValue(name);
    return theValue;
}

function gmSet(name, valuee) {
    GM_setValue(name, valuee);
}

unsafeWindow.saveImageBank = function() {
    console.log("Saved Image Bank!")
    gmSet("ImageBank", ImageBank.Storage);
}

//unsafeWindow.ImageBankObject = cloneInto (ImageBank, unsafeWindow, {cloneFunctions: true});

unsafeWindow.Image.prototype.hijack = function (url) {
    /*if (url.search("ocean-1") > 0) {
        console.log("Found background.");
        if (localStorage.getItem("BackgroundImage") !== null && localStorage.getItem("BackgroundImage") !== '') {
            this.src = localStorage.getItem("BackgroundImage");
            console.log("Hijacked Background: " + url);
        }
    } else if (url.search("ct-") > 0) {
        this.src = url;
    } else {
        this.src = window.ImageBank.get(url);
    }*/
    this.src = window.ImageBank.get(url);
};

function isFireFox() {
    //Check if browser is Firefox or not
    if (navigator.userAgent.search("Firefox") >= 0)
        return true;
    return false;
}

function injectScript(text) {
    var newScript = document.createElement('script');
    newScript.type = "text/javascript";
    newScript.textContent = text;
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(newScript);
}

function injectMods(text) {
    // Inject MapSize Editor
    text = text.replace(/!function\(t,e\){/g, 'var xsize=1024;var ysize=768;if(localStorage.getItem("mapX")!==undefined&&localStorage.getItem("mapY")!==undefined){console.log("Debug:Settingsize:"+xsize+":"+ysize);xsize=localStorage.getItem("mapX");ysize=localStorage.getItem("mapY");}window.constX=xsize;window.constY=ysize;!function(t,e){');
    text = text.replace(/width:e,height:i,/g, 'width:constX,height:constY,');
    //text = text.replace(/this\.w=1024\*this\.s\.map\.scale,this\.h=1024\*this\.s\.map\.scale,/g, 'this.w=constX*this.s.map.scale,this.h=constY*this.s.map.scale,');
    //text = text.replace(/width:t\.map\.width\*t\.map\.scale,height:t\.map\.height\*t\.map\.scale/g, 'width:constX*t.map.scale,height:constY*t.map.scale');
    //text = text.replace(/e.attr\(\{width:1024\*t\.map\.scale,height:1024\*t\.map\.scale}\),/g, 'e.attr({width:constX*t.map.scale,height:constY*t.map.scale}),');
    //text = text.replace(/"fantasy-world",1024,768/g, '"fantasy-world",constX,constY');
    // Comercial Support
    text = text.replace(/1024,768/g, 'constX,constY');
    //text = text.replace(/1024/g, 'constX');
    // ImageHijack
    if (localStorage.getItem("QuickLoad") == 'true') {
        console.log("quickload")
        text = text.replace(/s\.src=n,/g, 's.hijack(n),');
        text = text.replace(/console\.log\(\"skin loaded\"\)\,/g, 'console.log("skin loaded"),window.saveImageBank(),');
    }
    /* Debug
  text = text.replace(/console\.log\("loaded",e\)/g, 'console.log("loaded",e,i)');
  text = text.replace(/console\.log\("loading",e\)/g, 'console.log("loading",e,i)');
  text = text.replace(/console\.log\("texture load",n\)/g, 'console.log("texture load",n,s)');
  text = text.replace(/console\.log\("texture loaded",n\)/g, 'console.log("texture loaded",n,s)');
  */
    // Todo something else usefull..

    injectScript(text);
}

var InkMods = {
    Config: false,
    ConfigOpen: false,
    Button: $('<div id="InkModsMenu">Settings</div>'),

    init: function () {
        this.Config = GM_config.init({
            'id': 'Inkarnate',
            'fields': {
                'Init': {
                    'label': 'Init (Hidden)',
                    'type': 'checkbox',
                    'default': false
                },
                'LargeMode': {
                    'label': 'Move around UI to work with Large Maps. (Will break Map Selection Menu!)<br>',
                    'type': 'checkbox',
                    'default': false
                },
                'QuickLoad': {
                    'label': 'Quick Load/Save Images (After first load, is faster) Uncheck to clear storage!<br>',
                    'type': 'checkbox',
                    'default': false
                },
                'MapX': {
                    'label': 'Map Width (When creating new map.)<br>',
                    'type': 'int',
                    'min': 1024,
                    'default': 1024
                },
                'MapY': {
                    'label': 'Map Height (When creating new map.)<br>',
                    'type': 'int',
                    'min': 768,
                    'default': 768
                },
                'BackgroundImage': {
                    'label': 'Background Image, Blank for defualt. (When creating new map.) *NotWorking for now*<br>',
                    'type': 'text',
                    'default': ''
                }
            },
            'events': {
                'init': function () {
                    window.InkMods.onInit(this);
                },
                'open': function () {
                    console.log('onOpen()');
                },
                'save': function () {
                    window.InkMods.onSave(this);
                },
                'close': function () {
                    window.InkMods.onClose(this);
                },
                'reset': function () {
                    window.InkMods.setupStorage(this, true);
                    console.log('onReset()');
                }
            },
            'css': '#Inkarnate_Init_var { display: none !important; }'
        });
    },
    setupStorage: function (cfg, reset) {
        console.log("InkMods: Init Storage.");
        if (!cfg.get("Init") || reset === true || GM_info.script.version !== localStorage.getItem("Version")) {
            console.log("InkMods: New Storage.");
            cfg.set("Init", true);
            cfg.save();

            localStorage.setItem("Version", GM_info.script.version);
            localStorage.setItem("LargeMode", false);
            localStorage.setItem("QuickLoad", false);
            localStorage.setItem("mapX", 1024);
            localStorage.setItem("mapY", 768);
            localStorage.setItem("BackgroundImage", '');
        }

        window.ImageBank.load();
    },

    onInit: function (cfg) {
        this.setupStorage(cfg);

        console.log('onInit()');
        this.Button.css({
            "position": "fixed",
            "right": "100px",
            "bottom": "10px",
            "border": "1px solid #6b5326",
            "padding": "5px 10px",
            "color": "orange",
            "cursor": "pointer",
            "z-index": "99999"
        });
        this.Button.insertAfter("#fb-root");

        var p = this;
        this.Button.click(function () {
            if (p.ConfigOpen)
                return;

            p.ConfigOpen = true;
            cfg.open();
        });
    },

    onClose: function (cfg) {
        console.log('onClose()');
        this.ConfigOpen = false;
    },

    onSave: function (cfg) {
        console.log('onSave()');
        this.Options.MapSize(cfg.get("MapX"), cfg.get("MapY"));
        this.Options.BackgroundImage(cfg.get("BackgroundImage"));
        this.Options.LargeMode(cfg.get("LargeMode"));
        this.Options.QuickLoad(cfg.get("QuickLoad"));
    },

    Options: {
        MapSize: function (x, y) {
            if (typeof x === 'number' && isFinite(x) && typeof y === 'number' && isFinite(y)) {
                localStorage.setItem("mapX", x);
                localStorage.setItem("mapY", y);
                constX = x;
                constY = y;
            }
        },
        BackgroundImage: function (url) {
            localStorage.setItem("BackgroundImage", url);
        },
        QuickLoad: function (flag) {
            localStorage.setItem("QuickLoad", flag);

            if (flag) {
// Reload the page?
            } else {
                window.ImageBank.storage = [];
                window.ImageBank.save();
            }
        },
        LargeMode: function (flag) {
            localStorage.setItem("LargeMode", flag);

            if (flag && this.style === undefined) {
                console.log("Injected Styles.");
                this.style = GM_addStyle('#map-builder-view { position: absolute; }' +
                                         '.map-builder-container { position: absolute; width: auto; left: 80px;}' +
                                         '#left-sidebar, #tool-options, #logo-menu, #selected-tool { position: fixed; z-index: 999;}' +
                                         '#tool-options, #selected-tool { left: 22%;}' +
                                         '#logo-menu { left: 1px; top: 20px;}' +
                                         '#left-sidebar { left: 10px; top: 15%;}');
            }
            else if (!flag && this.style !== undefined) {
                this.style.remove();
                this.style = undefined;
            }
        }
    }
};

function doMenu() {
    $(window).load(function (e) {
        if (typeof (Storage) !== "undefined") {
            console.log("InkMods: Init");
            window.InkMods = InkMods;
            window.InkMods.init();
            window.InkMods.Options.LargeMode((localStorage.getItem("LargeMode") == 'true'));
        }
        else {
            console.log("InkMods: Error, Storage is undefined.");
        }

        if (!$("#logo-menu").length) {
            console.log("InkMods: Debug: Can't find #logo-menu. (Page loaded to fast?) " + $("#logo-menu").length);
            return;
        }

        $("#ui").waitUntilExists(function (e) {
            $("#map-container > canvas").css({
                width: constX + 'px',
                height: constY + 'px'
            });
        });

        $("#objects > ul").waitUntilExists(function (e) {
            $("#objects > ul").css({
                overflow: 'scroll'
            });
        });
        $("#textures > ul").waitUntilExists(function (e) {
            $("#textures > ul").css({
                overflow: 'scroll'
            });
        });
    });
}
window.doMenu = doMenu;

if (isFireFox()) {
    console.log("Mozzila!");

    window.addEventListener('beforescriptexecute', function (e) {
        src = e.target.src;
        if (src.search(/map-builder-app(.*)\.js/) != -1) {
            e.preventDefault();
            e.stopPropagation();
            console.log("Block: " + e.target.src);
            window.mapjs = e.target.src;

            GM_xmlhttpRequest({
                method: "GET",
                url: window.mapjs,
                synchronous: true,
                onload: function (response) {
                    console.log("Hijacked: " + window.mapjs);
                    injectMods(response.responseText);
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: "https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular-route.js",
                        onload: function (response) {
                            console.log("Hijacked: " + e.target.src);
                            injectScript(response.responseText);
                            window.doMenu();
                        }
                    });
                }
            });
        }

        if (src.search(/angular-route(.*)\.js/) != -1) {
            e.preventDefault();
            e.stopPropagation();
        }
    });
}
else {
    console.log("Something else?");
    alert("You're not using firefox. Chrome doesn't have beforescriptexecute.");
    // Going to need to edit this file your self if you have crhome. Chrome doesn't have a working interceptor :( basicly replaces are above. Host on your own local server. Do not redistrubute inkarnate!
    // Also, put in userscript above for chrome!
    // @webRequest   [{"selector":"*/assets/map-builder-app*.js","action":{"redirect":"https://localhost/map-builder-app.js"}}]
    GM_webRequest([{
        selector: '*/assets/map-builder-app*.js',
        action: {
            redirect: 'https://localhost/map-builder-app.js'
        }
    }, ], function (info, message, details) {
        console.log(info, message, details);
    });
}