Skip to content

Instantly share code, notes, and snippets.

@f-o
Created January 27, 2024 08:39
Show Gist options
  • Save f-o/1bf2dd8a3364d51c5a666119eb334a9b to your computer and use it in GitHub Desktop.
Save f-o/1bf2dd8a3364d51c5a666119eb334a9b to your computer and use it in GitHub Desktop.
Userscript to streamline Jigidi solving
// ==UserScript==
// @name Jigidi Magic Stripes
// @namespace me.danq.com.jigidi.magicstripes
// @match https://www.jigidi.com/solve/*
// @grant GM_getValue
// @grant GM_setValue
// @version 1.1
// @author Dan Q <https://danq.me>
// @description 23/03/2023, 14:32:30
// ==/UserScript==
/*
RainbowVis-JS ()Eclipse Public License - v 1.0) | https://github.com/anomal/RainbowVis-JS/blob/master/rainbowvis.js
*/
function Rainbow(){"use strict";var gradients=null;var minNum=0;var maxNum=100;var colours=['ff0000','ffff00','00ff00','0000ff'];setColours(colours);function setColours(spectrum){if(spectrum.length<2){throw new Error('Rainbow must have two or more colours.')}else{var increment=(maxNum-minNum)/(spectrum.length-1);var firstGradient=new ColourGradient();firstGradient.setGradient(spectrum[0],spectrum[1]);firstGradient.setNumberRange(minNum,minNum+increment);gradients=[firstGradient];for(var i=1;i<spectrum.length-1;i++){var colourGradient=new ColourGradient();colourGradient.setGradient(spectrum[i],spectrum[i+1]);colourGradient.setNumberRange(minNum+increment*i,minNum+increment*(i+1));gradients[i]=colourGradient}
colours=spectrum}}
this.setSpectrum=function(){setColours(arguments);return this}
this.setSpectrumByArray=function(array){setColours(array);return this}
this.colourAt=function(number){if(isNaN(number)){throw new TypeError(number+' is not a number')}else if(gradients.length===1){return gradients[0].colourAt(number)}else{var segment=(maxNum-minNum)/(gradients.length);var index=Math.min(Math.floor((Math.max(number,minNum)-minNum)/segment),gradients.length-1);return gradients[index].colourAt(number)}}
this.colorAt=this.colourAt;this.setNumberRange=function(minNumber,maxNumber){if(maxNumber>minNumber){minNum=minNumber;maxNum=maxNumber;setColours(colours)}else{throw new RangeError('maxNumber ('+maxNumber+') is not greater than minNumber ('+minNumber+')')}
return this}}
function ColourGradient(){"use strict";var startColour='ff0000';var endColour='0000ff';var minNum=0;var maxNum=100;this.setGradient=function(colourStart,colourEnd){startColour=getHexColour(colourStart);endColour=getHexColour(colourEnd)}
this.setNumberRange=function(minNumber,maxNumber){if(maxNumber>minNumber){minNum=minNumber;maxNum=maxNumber}else{throw new RangeError('maxNumber ('+maxNumber+') is not greater than minNumber ('+minNumber+')')}}
this.colourAt=function(number){return calcHex(number,startColour.substring(0,2),endColour.substring(0,2))+calcHex(number,startColour.substring(2,4),endColour.substring(2,4))+calcHex(number,startColour.substring(4,6),endColour.substring(4,6))}
function calcHex(number,channelStart_Base16,channelEnd_Base16){var num=number;if(num<minNum){num=minNum}
if(num>maxNum){num=maxNum}
var numRange=maxNum-minNum;var cStart_Base10=parseInt(channelStart_Base16,16);var cEnd_Base10=parseInt(channelEnd_Base16,16);var cPerUnit=(cEnd_Base10-cStart_Base10)/numRange;var c_Base10=Math.round(cPerUnit*(num-minNum)+cStart_Base10);return formatHex(c_Base10.toString(16))}
function formatHex(hex){if(hex.length===1){return'0'+hex}else{return hex}}
function isHexColour(string){var regex=/^#?[0-9a-fA-F]{6}$/i;return regex.test(string)}
function getHexColour(string){if(isHexColour(string)){return string.substring(string.length-6,string.length)}else{var name=string.toLowerCase();if(colourNames.hasOwnProperty(name)){return colourNames[name]}
throw new Error(string+' is not a valid colour.')}}
var colourNames={aliceblue:"F0F8FF",antiquewhite:"FAEBD7",aqua:"00FFFF",aquamarine:"7FFFD4",azure:"F0FFFF",beige:"F5F5DC",bisque:"FFE4C4",black:"000000",blanchedalmond:"FFEBCD",blue:"0000FF",blueviolet:"8A2BE2",brown:"A52A2A",burlywood:"DEB887",cadetblue:"5F9EA0",chartreuse:"7FFF00",chocolate:"D2691E",coral:"FF7F50",cornflowerblue:"6495ED",cornsilk:"FFF8DC",crimson:"DC143C",cyan:"00FFFF",darkblue:"00008B",darkcyan:"008B8B",darkgoldenrod:"B8860B",darkgray:"A9A9A9",darkgreen:"006400",darkgrey:"A9A9A9",darkkhaki:"BDB76B",darkmagenta:"8B008B",darkolivegreen:"556B2F",darkorange:"FF8C00",darkorchid:"9932CC",darkred:"8B0000",darksalmon:"E9967A",darkseagreen:"8FBC8F",darkslateblue:"483D8B",darkslategray:"2F4F4F",darkslategrey:"2F4F4F",darkturquoise:"00CED1",darkviolet:"9400D3",deeppink:"FF1493",deepskyblue:"00BFFF",dimgray:"696969",dimgrey:"696969",dodgerblue:"1E90FF",firebrick:"B22222",floralwhite:"FFFAF0",forestgreen:"228B22",fuchsia:"FF00FF",gainsboro:"DCDCDC",ghostwhite:"F8F8FF",gold:"FFD700",goldenrod:"DAA520",gray:"808080",green:"008000",greenyellow:"ADFF2F",grey:"808080",honeydew:"F0FFF0",hotpink:"FF69B4",indianred:"CD5C5C",indigo:"4B0082",ivory:"FFFFF0",khaki:"F0E68C",lavender:"E6E6FA",lavenderblush:"FFF0F5",lawngreen:"7CFC00",lemonchiffon:"FFFACD",lightblue:"ADD8E6",lightcoral:"F08080",lightcyan:"E0FFFF",lightgoldenrodyellow:"FAFAD2",lightgray:"D3D3D3",lightgreen:"90EE90",lightgrey:"D3D3D3",lightpink:"FFB6C1",lightsalmon:"FFA07A",lightseagreen:"20B2AA",lightskyblue:"87CEFA",lightslategray:"778899",lightslategrey:"778899",lightsteelblue:"B0C4DE",lightyellow:"FFFFE0",lime:"00FF00",limegreen:"32CD32",linen:"FAF0E6",magenta:"FF00FF",maroon:"800000",mediumaquamarine:"66CDAA",mediumblue:"0000CD",mediumorchid:"BA55D3",mediumpurple:"9370DB",mediumseagreen:"3CB371",mediumslateblue:"7B68EE",mediumspringgreen:"00FA9A",mediumturquoise:"48D1CC",mediumvioletred:"C71585",midnightblue:"191970",mintcream:"F5FFFA",mistyrose:"FFE4E1",moccasin:"FFE4B5",navajowhite:"FFDEAD",navy:"000080",oldlace:"FDF5E6",olive:"808000",olivedrab:"6B8E23",orange:"FFA500",orangered:"FF4500",orchid:"DA70D6",palegoldenrod:"EEE8AA",palegreen:"98FB98",paleturquoise:"AFEEEE",palevioletred:"DB7093",papayawhip:"FFEFD5",peachpuff:"FFDAB9",peru:"CD853F",pink:"FFC0CB",plum:"DDA0DD",powderblue:"B0E0E6",purple:"800080",red:"FF0000",rosybrown:"BC8F8F",royalblue:"4169E1",saddlebrown:"8B4513",salmon:"FA8072",sandybrown:"F4A460",seagreen:"2E8B57",seashell:"FFF5EE",sienna:"A0522D",silver:"C0C0C0",skyblue:"87CEEB",slateblue:"6A5ACD",slategray:"708090",slategrey:"708090",snow:"FFFAFA",springgreen:"00FF7F",steelblue:"4682B4",tan:"D2B48C",teal:"008080",thistle:"D8BFD8",tomato:"FF6347",turquoise:"40E0D0",violet:"EE82EE",wheat:"F5DEB3",white:"FFFFFF",whitesmoke:"F5F5F5",yellow:"FFFF00",yellowgreen:"9ACD32"}}
// Are we logged-in?
const loggedIn = !document.querySelector('account-status.guest');
console.log(`Magic Stripes: loggedIn=${loggedIn ? 'true' : 'false'}`);
// Prepare Magic Stripes UI
const jigidiMagicStripes = document.createElement('div');
jigidiMagicStripes.id = 'jigidi-magic-stripes';
// Inject Magic Stripes UI
const creatorElem = document.getElementById('info-creator');
creatorElem.before(jigidiMagicStripes);
if(!loggedIn){
// Magic Stripes UI only works if logged in (we need Jigidi's state saving through page refreshes)
jigidiMagicStripes.innerHTML = '<p><strong>Magic Stripes</strong><br>Disabled. <a href="https://www.jigidi.com/login.php">Sign in to Jigidi</a> to enable.</p>';
} else {
// Logged in: jigsaw ID and settings for this jigsaw, enumerate lengths of jigsaw dimensions, and update Magic Stripes UI
const jigsawId = window.location.href.match(/solve\/(\w+)\//)[1];
const magicStripesSettings = GM_getValue(`magicStripesSettings_${jigsawId}`, { col: 0 });
const jDimensions = creatorElem.innerText.match(/(\d+)×(\d+)/);
const jCols = parseInt(jDimensions[1]);
const jRows = parseInt(jDimensions[2]);
console.log(`Magic Stripes: jCols=${jCols} jRows=${jRows}`);
jigidiMagicStripes.innerHTML = `
<p>
<strong>Magic Stripes</strong><br>
<label for="magicStripesCol">Help with column? (0 to disable)</label><br>
<input type="number" id="magicStripesCol" value="${magicStripesSettings.col}" min="0" max="${jCols}" style="width: 4em;">
<button id="magicStripesGo">Go</button>
<button id="magicStripesPlusOne">+1 and Go</button>
</p>
`;
const magicStripesCol = document.getElementById('magicStripesCol');
const magicStripesGo = document.getElementById('magicStripesGo');
magicStripesGo.addEventListener('click', ()=>{
magicStripesSettings.col = parseInt(magicStripesCol.value);
GM_setValue(`magicStripesSettings_${jigsawId}`, magicStripesSettings);
window.location.reload();
});
document.getElementById('magicStripesPlusOne').addEventListener('click', ()=>{
magicStripesCol.value = (parseInt(magicStripesCol.value) + 1) % (jCols + 1);
magicStripesGo.dispatchEvent(new Event('click'));
});
// Generate spectrum to use:
var rainbow = new Rainbow();
let jColors = [];
rainbow.setNumberRange(1, jRows);
//rainbow.setSpectrum('red', 'violet');
rainbow.setSpectrum('ff0000','ffff00','00ff00','0000ff');
for(var i = 1; i <= jRows; i++) {
jColors.push(`#${rainbow.colourAt(i)}`);
}
let jC = 0;
const targetCol = parseInt(magicStripesSettings.col);
if(targetCol > 0) {
// Override putImageData with a manipulated version for THIS page load
CanvasRenderingContext2D.prototype.putImageData = function(imageData, dx, dy){
console.log('Magic Stripes: putImageData');
const targetCol = parseInt(magicStripesSettings.col);
const col = jC % jCols;
const row = Math.floor(jC / jCols);
if((col + 1) === targetCol) {
// Target column: color and number
this.fillStyle = jColors[row % jColors.length];
this.fillRect(-1000,-1000,2000,2000);
this.font = 'bold 2rem sans-serif';
this.fillStyle = 'black';
this.fillText(`${row+1}`, imageData.width / 2 - 5, imageData.height / 2 + 5);
/**
this.fillStyle = 'black';
this.fillText(`${row + 1} - `.repeat(10), 0, 25);
this.fillText(`${row + 1} - `.repeat(10), 0, 50);
this.fillText(`${row + 1} - `.repeat(10), 0, 75);
this.fillStyle = 'white';
this.fillText(`${row + 1} - `.repeat(10), 0, 38);
this.fillText(`${row + 1} - `.repeat(10), 0, 63);
this.fillText(`${row + 1} - `.repeat(10), 0, 88);
**/
} else if ((col + 2) === targetCol) {
// Previous column: lightly color, don't number
this.fillStyle = jColors[row % jColors.length];
this.fillRect(-1000,-1000,2000,2000);
this.fillStyle = '#ffffffbb';
this.fillRect(-1000,-1000,2000,2000);
this.font = 'bold 2rem sans-serif';
this.fillStyle = 'black';
this.fillText(`${row+1}`, imageData.width / 2 - 5, imageData.height / 2 + 5);
} else {
// Other columns: white-out
this.fillStyle = '#ffffffdd';
this.fillRect(-1000,-1000,2000,2000);
}
jC++;
}
}
}
@Dan-Q
Copy link

Dan-Q commented Jan 29, 2024

Amazing; love it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment