JackboxDrawer-Lite

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

Εγκατάσταση αυτού του κώδικαΒοήθεια
Κώδικας προτεινόμενος από τον δημιιουργό

Μπορεί, επίσης, να σας αρέσει ο κώδικας JackboxDrawer.

Εγκατάσταση αυτού του κώδικα

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==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);
}