JackboxDrawer-Lite

Adds a button to import images into Jackbox drawing-based games!

Installa questo script?
Script suggerito dall'autore

Potresti essere interessato/a anche a JackboxDrawer

Installa questo script

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         JackboxDrawer-Lite
// @description  Adds a button to import images into Jackbox drawing-based games!
// @namespace    ipodtouch0218/JackboxDrawer-Lite
// @version      1.0.0
// @include      *://jackbox.tv/*
// ==/UserScript==

//Catch outgoing messages through stringify and replace drawing data.
//Has to be done through eval to break through GreaseMonkey's sandboxing.
window.eval(`
tempvar = null
ignore = 0
oldStringify = JSON.stringify
JSON.stringify = function(arg) {
  if (ignore > 0) {
    ignore--
    return oldStringify(arg);
  }
  if (typeof(arg.params) == 'undefined' || arg.params == null) {
    return oldStringify(arg);
  }
  data = arg.params
  if (typeof(tempvar) == 'undefined' || tempvar === null) {
    //No custom code ready, most likely a vanilla subimssion. Ignore this one.
    return oldStringify(arg);
  }
  eval(tempvar);
  tempvar = null;
  return oldStringify(arg);
}
`)

//Game variables
var currentGameId = null;
var button = null;
var games = {
  "drawful_1": {
    submitDrawing: function(img) {
      window.eval("tempvar = \"var test = '" + img + "'; if (typeof (data.body.picture) !== 'undefined') { data.body.picture = test; } else { data.body.drawing = test; }\"");
      document.getElementById("drawful-submitdrawing").click();
    },
    isInDrawingMode: function() {
      return !document.getElementsByClassName("state-draw")[0].getAttribute("class").includes("pt-page-off");
    },
    getSketchpad: function() {
      return document.getElementsByClassName("sketchpad")[0];
    },
    isActiveGame: function() {
      return document.getElementById("page-drawful") != null;
    },
    addImportButton: function() {
      button = document.createElement("input");
      button.setAttribute("type", "file");
      button.setAttribute("class", "button-drawful button-large pure-button pure-input-1");
      button.setAttribute("id", "import-button");
      button.setAttribute("accept", "image/*");
      button.setAttribute("style", "margin: 0 auto;");
      button.addEventListener("change", uploadBitmapImage);
      
      attach = document.getElementsByClassName("state-draw")[0];
      attach.appendChild(button);
    }
  },
  /*
  "drawful_2": {
    submitDrawing: function() {
      document.getElementById("submitdrawing").click()
    },
    isInDrawingMode: function() {
      return document.getElementsByClassName("Draw")[0] != null
    },
    getSketchpad: function() {
      return document.getElementById("fullLayer")
    }
  },
  */
  "bidiots": {
    submitDrawing: function(img) {
      window.eval("tempvar=\"data.body.drawing='" + img + "'\"");
      document.getElementById("auction-submitdrawing").click();
    },
    isInDrawingMode: function() {
      return !document.getElementById("state-draw").getAttribute("class").includes("pt-page-off");
    },
    getSketchpad: function() {
      return document.getElementById("auction-sketchpad");
    },
    isActiveGame: function() {
      return document.getElementById("page-auction") !== null;
    },
    addImportButton: function() {
      button = document.createElement("input");
      button.setAttribute("type", "file");
      button.setAttribute("class", "container button-auction button-large pure-button");
      button.setAttribute("id", "import-button");
      button.setAttribute("accept", "image/*");
      button.setAttribute("style", "margin: 0 auto;");
      button.addEventListener("change", uploadBitmapImage);
      
      attach = document.getElementById("state-draw");
      attach.appendChild(button);
    }
  },
  "tee_ko": {
	scaling: 5,
    submitDrawing: function(img) {
      window.eval("img=" + JSON.stringify(img).replace("\\",""));
      window.eval("tempvar='data.body.pictureLines=img'");
      document.getElementById("awshirt-submitdrawing").click();
    },
    isInDrawingMode: function() {
      return !document.getElementById("state-draw").getAttribute("class").includes("pt-page-off");
    },
    getSketchpad: function() {
      return document.getElementsByClassName("awshirt-sketchpad")[0];
    },
    isActiveGame: function() {
      return document.getElementById("page-awshirt") !== null;
    },
    addImportButton: function() {
      button = document.createElement("input");
      button.setAttribute("type", "file");
      button.setAttribute("class", "awshirt-button btn btn-block");
      button.setAttribute("id", "import-button");
      button.setAttribute("accept", "image/*");
      button.addEventListener("change", uploadVectorImage);
      
      attach = document.getElementsByClassName("post-sketchpad")[0];
      attach.appendChild(button);
    }
  },
  "push_the_button": {
	scaling: 6,
    submitDrawing: function(img) {
      img.forEach(line => {
        points = "";
        line["points"].forEach(point => {
          points += point["x"] + "," + point["y"] + "|";
        });
        line["points"] = points.substring(0,points.length-1)
      });
      window.eval("img=" + JSON.stringify(img).replace("\\",""));
      window.eval("tempvar='data.body.lines=img'");
      document.getElementById("submitdrawing").click();
    },
    isInDrawingMode: function() {
      return document.getElementsByClassName("Draw")[0] != null;
    },
    getSketchpad: function() {
      return document.getElementById("fullLayer");
    },
    isActiveGame: function() {
      return document.getElementsByClassName("pushthebutton")[0] != null;
    },
    addImportButton: function() {
      if (document.getElementsByClassName("Draw")[0] == null)
        return;
      button = document.createElement("input");
      button.setAttribute("type", "file");
      button.setAttribute("class", "button");
      button.setAttribute("id", "import-button");
      button.setAttribute("accept", "image/*");
      button.setAttribute("style", "margin: 0 auto;");
      button.addEventListener("change", uploadVectorImage);
      
      attach = document.getElementById("post-sketchpad");
      attach.appendChild(button);
    }
  },
  "trivia_murder_party_1": {
    submitDrawing: function(img) {
      window.eval("tempvar=\"data.body.drawing='" + img + "'\"");
      document.getElementById("enter-single-drawing-submit").click();
    },
    isInDrawingMode: function() {
      return !document.getElementById("state-enter-single-drawing").getAttribute("class").includes("pt-page-off");
    },
    getSketchpad: function() {
      return document.getElementById("sketchpad");
    },
    isActiveGame: function() {
      return document.getElementById("page-triviadeath") !== null;
    },
    addImportButton: function() {
      button = document.createElement("input");
      button.setAttribute("type", "file");
      button.setAttribute("class", "light-text button-game button-large pure-button pure-input-1");
      button.setAttribute("id", "import-button");
      button.setAttribute("accept", "image/*");
      button.setAttribute("style", "margin: 0 auto; color: white;");
      button.addEventListener("change", uploadBitmapImage);
      
      attach = document.getElementById("state-enter-single-drawing");
      attach.appendChild(button);
    }
  },
  /*
  "patentlystupid": {
    submitDrawing: function() {
      document.getElementById("submitdrawing").click()
    },
    isInDrawingMode: function() {
      return document.getElementsByClassName("Draw")[0] != null
    },
    getSketchpad: function() {
      return document.getElementById("fullLayer")
    }
  },
  */
  "champd_up": {
	scaling: 8,
    submitDrawing: function(img) {
      if (document.getElementsByClassName("button choice-button btn btn-lg")[0].innerText == "SUBMIT") {
        img.forEach(line => {
          points = "";
          line["points"].forEach(point => {
            points += point["x"] + "," + point["y"] + "|";
          });
          line["points"] = points.substring(0,points.length-1)
        });
        window.eval("img=" + JSON.stringify(img).replace("\\",""));
        window.eval("tempvar='data.val.lines=img'");
		document.getElementsByClassName("button choice-button btn btn-lg")[0].click();
      }
    },
    submitName: function() {
      btn = document.getElementsByClassName("button choice-button btn btn-lg")[0];
      if (btn.getAttribute("data-action") == "name") {
        btn.click();
        document.getElementsByClassName("swal2-input")[0].value = "test";
        document.getElementsByClassName("swal2-confirm swal2-styled")[0].click();
      }
    },
    canSubmitNormally: function() {
      return document.getElementsByClassName("button choice-button btn btn-lg")[0].innerText == "SUBMIT";
    },
    isInDrawingMode: function() {
      return document.getElementsByClassName("Draw")[0] != null;
    },
    getSketchpad: function() {
      return document.getElementsByClassName("sketchpad fullLayer")[0];
    },
    isActiveGame: function() {
      return document.getElementsByClassName("worldchamps")[0] != null;
    },
    addImportButton: function() {
      if (document.getElementsByClassName("Draw")[0] == null)
        return;
      button = document.createElement("input");
      button.setAttribute("type", "file");
      button.setAttribute("class", "button btn btn-lg");
      button.setAttribute("id", "import-button");
      button.setAttribute("accept", "image/*");
      button.setAttribute("style", "margin: 0 auto;");
      button.addEventListener("change", uploadVectorImage);
      
      attach = document.getElementsByClassName("choices")[0];
      attach.appendChild(button);
    }
  }
}

function uploadBitmapImage() {
  debug("Bitmap upload");
  createImageBitmap(button.files[0]).then(function(img) {
    sketchpad = games[currentGameId]["getSketchpad"]();
    ctx = sketchpad.getContext("2d");
    ctx.drawImage(img, 0, 0, sketchpad.width, sketchpad.height);
    debug("Canvas populated");

    uri = sketchpad.toDataURL('image/png');
    uri = uri.replace(/^data:image.+;base64,/, '');
      
    submitImage(uri);
  });
}

function uploadVectorImage() {
  scale = games[currentGameId]["scaling"];
  debug("Vector upload");
  
  createImageBitmap(button.files[0]).then(function(img) {
    sketchpad = games[currentGameId]["getSketchpad"]();
    ctx = sketchpad.getContext("2d");
    ctx.drawImage(img, 0, 0, (sketchpad.width/scale), (sketchpad.height/scale));
    debug("Canvas populated");
    
    resizedImage = ctx.getImageData(0, 0, (sketchpad.width/scale), (sketchpad.height/scale));
    
    submitImage(vectorizeImage(resizedImage, (sketchpad.width/scale), (sketchpad.height/scale), scale));
  });
}

function colorDistanceSquared(color1, color2) {
  redAvg = (color1["red"] + color2["red"]) / 2;
  redDiff = color2["red"]-color1["red"];
  greenDiff = color2["green"]-color1["green"];
  blueDiff = color2["blue"]-color1["blue"];
  if (redAvg < 128) {
    return (2 * (redDiff * redDiff)) + 
      (4 * (greenDiff * greenDiff)) +
      (3 * (blueDiff * blueDiff)); 
  } else {
    return (3 * (redDiff * redDiff)) + 
      (4 * (greenDiff * greenDiff)) +
      (2 * (blueDiff * blueDiff)); 
  }
}

function mixColors(color1, color2, t) {
  if (color1 == null) return color2;
  if (color2 == null) return color1;
  t2 = 1-t;
  return createColor(Math.floor((t * color1["red"]) + (t2 * color2["red"])), 
    Math.floor((t * color1["green"]) + (t2 * color2["green"])),
    Math.floor((t * color1["blue"]) + (t2 * color2["blue"])),
    1);
}

function createColor(red, green, blue, alpha) {
  return {"red": red, "green": green, "blue": blue, "alpha": alpha};
}

function colorToHex(color) {
  r = Number(color["red"]).toString(16);
  g = Number(color["green"]).toString(16);
  b = Number(color["blue"]).toString(16);
  return "#" + 
    (r.length == 1 ? "0" : "") + r + 
    (g.length == 1 ? "0" : "") + g + 
    (b.length == 1 ? "0" : "") + b;
}

function createPoint(x, y) {
  return {"x": x, "y": y};
}

function createLine(thickness, points, color) {
  return {"thickness": thickness, "points": points, "color": colorToHex(color)};
}    

function vectorizeImage(img, w, h, thickness) {
  lines = [];
  data = img.data;
  for (x = 0; x < w; x++) {
    currentLine = null;
    currentColor = null;
    colorCount = 1;
    for (y = 0; y < h; y++) {
      point = createPoint(x * thickness, y * thickness);
      pixelColor = createColor(data[(x + (y * w))*4 + 0],
        data[(x + (y * w))*4 + 1],
        data[(x + (y * w))*4 + 2],
        data[(x + (y * w))*4 + 3]);
      
      if (pixelColor.alpha < 70) {
        if (currentLine != null) {
          currentLine["points"].push(point);
          currentLine = null;
        }
        continue;
      }
      
      //first line of a row
      if (currentLine == null) {
        currentLine = createLine(thickness+1, [point], pixelColor);
        currentColor = pixelColor;
        colorCount = 1;
        lines.push(currentLine);
        continue;
      }
      
      if (y+1 >= h) {
        currentLine["points"].push(createPoint(x*thickness, y*thickness));
        break;
      }
      
      colorDistance = colorDistanceSquared(currentColor, pixelColor);
      //debug(colorDistance);
      if (colorDistance > 6400) {
        //too different to be grouped
        currentLine["points"].push(point);
        
        colorCount = 1;
        currentLine = createLine(thickness+1, [point], pixelColor);
        currentColor = pixelColor;
        lines.push(currentLine);
      } else {
        //group points... but not for some games. ugh.
        currentColor = mixColors(currentColor, pixelColor, 1-(1/++colorCount));
        currentLine["color"] = colorToHex(currentColor);
        
        if (currentGameId == "champd_up" || currentGameId == "push_the_button") {
          currentLine["points"].push(point);
        }
      }
    }
  }
  return lines;
}

function submitImage(data) {
  submit = true;
  if (currentGameId == "patentlystupid") {
    window.eval("ignore = 1");
  } else if (currentGameId == "champd_up") {
    if (games[currentGameId]["canSubmitNormally"]()) {
      window.eval("ignore = 1");
    } else {
      alert("You must submit a name first!\nUse the text box and \"Submit\" button under the color picker first!");
      submit = false;
	  button.value = null;
    }
  }
  
  //Simulate drawing on the sketchpad with mouse events. We can't access the sketchpad's info directly
  //as it's kept track of internally, and the game never attempts to send any data if it's blank.
  var rect = sketchpad.getBoundingClientRect();
  var mouseEvent = document.createEvent('MouseEvents');
  
  mouseEvent.clientX = rect.x + rect.width / 2;
  mouseEvent.clientY = rect.y + rect.height / 2;
  mouseEvent.initEvent("mousedown", true, false);
  sketchpad.dispatchEvent(mouseEvent);
  mouseEvent.clientX += 2;
  mouseEvent.initEvent("mousemove", true, false);
  sketchpad.dispatchEvent(mouseEvent);
  mouseEvent.initEvent("mouseup", true, false);
  sketchpad.dispatchEvent(mouseEvent);
    
  //Submit drawing and get ready to switch-a-roo.
  if (submit) {
    games[currentGameId]["submitDrawing"](data);
    button.value = null;
  }
}

//Observer stuff
var callback = function(mutations, observer) {
  for (var game in games) {
    if (games[game]["isActiveGame"]()) {
      currentGameId = game;
      debug("Game set to " + game);
      if (document.getElementById("import-button") == null)
        games[game]["addImportButton"]();
      break;
    }
  }
}

setTimeout(function() {
  observer = new MutationObserver(callback);
  targetNode = document.getElementById("app");
  config = { attributes: false, childList: true, subtree: true };
  observer.observe(targetNode, config);
  
  debug("Started observer");
}, 500);

function debug(message) {
  console.log("[JackboxDrawer-Lite] " + message);
}