Ultima attività 1740947436

yumaikas's Avatar yumaikas ha revisionato questo gist 1740947435. Vai alla revisione

1 file changed, 1 insertion

contrast-checker.html

@@ -51,6 +51,7 @@ body {
51 51 </div>
52 52 <button id="btn-share">Copy Link</button>
53 53 <a id="a-share" href="/">(link if button doesn't work)</a>
54 + </p>
54 55 </template>
55 56
56 57 <script src="hex.js"></script>

yumaikas's Avatar yumaikas ha revisionato questo gist 1740908451. Vai alla revisione

2 files changed, 169 insertions, 14 deletions

contrast-checker.html(file creato)

@@ -0,0 +1,141 @@
1 + <!DOCTYPE html>
2 + <html>
3 + <head>
4 + <meta name="viewport" content="width=device-width, initial-scale=1.0">
5 + <title>Discord Color Role Contrast Checker</title>
6 + </head>
7 + <style>
8 + body {
9 + margin-left: auto;
10 + margin-right: auto;
11 + max-width: 600px;
12 + text-align: center;
13 + color: white;
14 + background: #000;
15 + }
16 +
17 + a:visited {
18 + color: yellow;
19 + }
20 +
21 + .two-grid {
22 + display: grid;
23 + grid-template-columns: 1fr 1fr;
24 + width: 100%;
25 + color: var(--test-color, #476fbf);
26 + }
27 +
28 + .two-grid div {
29 + padding-top: 2rem;
30 + padding-bottom: 2rem;
31 + }
32 +
33 + .bg-white { font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; font-weight: bold; background-color: white; }
34 + .bg-discord { font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; font-weight: bold; background-color: #36393E; }
35 +
36 + </style>
37 + <body class="">
38 + <h2>Discord Role Color Contrast Checker</h2>
39 + <div id="app"></div>
40 +
41 + <template id="root">
42 + <input type="color" value="#476fbf" id="color-picker" />
43 + <p>Contrast of <span id="color-1"/>
44 + <div class="two-grid">
45 + <div class="bg-white">
46 + <p>vs. white: <span id="white-contrast"></span>:1</p>
47 + </div>
48 + <div class="bg-discord">
49 + <p>vs discord dark: <span id="discord-contrast"></span>:1</p>
50 + </div>
51 + </div>
52 + <button id="btn-share">Copy Link</button>
53 + <a id="a-share" href="/">(link if button doesn't work)</a>
54 + </template>
55 +
56 + <script src="hex.js"></script>
57 + <script>
58 + hex.arise(function() {
59 + function relativeLuminanceW3C(R8bit, G8bit, B8bit) {
60 +
61 + const RsRGB = R8bit/255;
62 + const GsRGB = G8bit/255;
63 + const BsRGB = B8bit/255;
64 +
65 + const R = (RsRGB <= 0.03928) ? RsRGB/12.92 : Math.pow((RsRGB+0.055)/1.055, 2.4);
66 + const G = (GsRGB <= 0.03928) ? GsRGB/12.92 : Math.pow((GsRGB+0.055)/1.055, 2.4);
67 + const B = (BsRGB <= 0.03928) ? BsRGB/12.92 : Math.pow((BsRGB+0.055)/1.055, 2.4);
68 +
69 + // For the sRGB colorspace, the relative luminance of a color is defined as:
70 + const L = 0.2126 * R + 0.7152 * G + 0.0722 * B;
71 + return L;
72 + }
73 +
74 + function hexToRGB8(color) {
75 + const r = Number.parseInt(color.slice(1, 3), 16);
76 + const g = Number.parseInt(color.slice(3, 5), 16);
77 + const b = Number.parseInt(color.slice(5, 8), 16)
78 +
79 + return {r,g,b}
80 + }
81 +
82 + function luminanceOfHex(color) {
83 + const {r,g,b} = hexToRGB8(color);
84 + return relativeLuminanceW3C(r,g,b);
85 + }
86 +
87 + const white_lum = luminanceOfHex("#FFFFFF");
88 + const discord_lum = luminanceOfHex("#36393E");
89 +
90 + const root = hex.spell("#root", {
91 + init(arg) {
92 + this.setColor(arg.color || "#4764bf");
93 + this.$doc = document.documentElement;
94 + },
95 + setColor(color) {
96 + const color_lum = luminanceOfHex(color);
97 + this.$doc.style.setProperty('--test-color', color);
98 +
99 + const white_contrast = (Math.max(white_lum, color_lum)+0.05) / (Math.min(white_lum, color_lum)+0.05);
100 + const discord_contrast = (Math.max(discord_lum, color_lum)+0.05) / (Math.min(discord_lum, color_lum)+0.05);
101 +
102 + this.colorName = color;
103 + this.whiteContrast = white_contrast.toFixed(1);
104 + this.discordContrast = discord_contrast.toFixed(1);
105 + const link = `https://contrast.junglecoder.com/${curr_color}`
106 + this.$toShare.href = link;
107 + }
108 + picker: ["#color-picker", { on: {
109 + change(e) {
110 + this.setColor(e.target.value);
111 + }
112 + }}],
113 + colorName: ["#color-1"],
114 + whiteContrast: ["#white-contrast"],
115 + darkContrast: ["#discord-contrast"],
116 + toShare: ["#a-share"],
117 + shareBtn: ["#btn-share", {
118 + t: 'Copy Link',
119 + on: {
120 + async click() {
121 + const link = `https://contrast.junglecoder.com/${this.colorName}`
122 + try {
123 + await navigator.clipboard.writeText(link)
124 + } finally {
125 + this.toShare = "Manually copy link from here";
126 + }
127 + }
128 + }
129 + }]
130 + });
131 + const urlParams = new URLSearchParams(window.location.search);
132 + let firstColor = window.loation.hash || urlParams.get('c') || "#4764bf";
133 + if (firstColor[0] !== "#") {
134 + firstColor = "#" + firstColor;
135 + }
136 + console.log('FIRST COLOR', firstColor);
137 + hex.become(root, hex.id('app'), { color: firstColor });
138 + });
139 + </script>
140 + </body>
141 + </html>

hex.js

@@ -9,11 +9,10 @@ const hex = (function() {
9 9 const recall = (k) => JSON.parse(localStorage.getItem(k) || "null");
10 10
11 11 function setterFor(node) {
12 - switch (node.tagName) {
13 - case "INPUT":
14 - return (val) => node.value = val;
15 - default:
16 - return (val) => { node.innerText = val; };
12 + if (["INPUT", "TEXTAREA"].indexOf(node.tagName) > -1) {
13 + return (val) => node.value = val;
14 + } else {
15 + return (val) => node.innerText = val;
17 16 }
18 17 }
19 18
@@ -34,6 +33,13 @@ const hex = (function() {
34 33 return toClone.content.cloneNode(true);
35 34 }
36 35
36 + function become(spell, el, config) {
37 + // TODO: Figure out a way for replaceChildren to work
38 + el.replaceChildren();
39 + spell(el, config);
40 + }
41 +
42 +
37 43 function spell(template, config) {
38 44
39 45 return function(into, start) {
@@ -60,6 +66,8 @@ const hex = (function() {
60 66 const el = q1(basis, v);
61 67 state.dom[k] = el;
62 68 makeProp(el, start[k]);
69 + } else if (typeof v === "function") {
70 + interface[k] = v.bind(interface);
63 71 } else if (Array.isArray(v)) {
64 72 if (v.length <= 0) {
65 73 throw new Error("Empty array not valid!");
@@ -80,6 +88,9 @@ const hex = (function() {
80 88 } else if (v.length == 2 && (typeof v[0]) === "string" && isObj(v[1])) {
81 89 makeProp(el, start[k]);
82 90 const obj = v[1];
91 + if (obj.t) {
92 + interface[k] = start[k] || obj.t;
93 + }
83 94 if (obj.on) {
84 95 for (const [evt, fn] of Object.entries(obj.on)) {
85 96 let inner = fn.bind(interface);
@@ -98,17 +109,19 @@ const hex = (function() {
98 109 }
99 110 if (obj.init && typeof obj.init === "function") {
100 111 obj.init.call(interface, start);
101 - }
112 + }
102 113
103 - } else if (v.length === 2 &&
114 + } else if (v.length === 3 &&
104 115 (typeof v[0]) === "string" &&
105 - (typeof v[1]) === "function"
116 + (typeof v[1]) === "function" &&
117 + (typeof v[2]) === "function"
106 118 && Array.isArray(start[k])
107 119 ) {
108 - state.vars[k] = start[k].map((data) => v[1](el, data));
120 + // TODO:
121 + state.vars[k] = start[k].map((data) => v[1](el, v[2](data)));
109 122 interface[k] = function(new_data) {
110 - el.innerHTML = "";
111 - state.vars[k] = new_data.map((data) => v[1](el, data));
123 + el.replaceChildren();
124 + state.vars[k] = new_data.map((data) => v[1](el, v[2](data)));
112 125 }
113 126 } else {
114 127 throw new Error(`Invalid config: ${k}`);
@@ -118,13 +131,14 @@ const hex = (function() {
118 131
119 132 interface._ = state;
120 133 into.appendChild(basis);
134 + if (typeof interface.init === "function") {
135 + interface.init(start);
136 + }
121 137 return interface;
122 138 }
123 139 }
124 140
125 -
126 -
127 - return {q, q1, id, from, arise, scrawl, recall, spell};
141 + return {q, q1, id, from, arise, scrawl, recall, become, spell};
128 142 })();
129 143
130 144 window.hex = hex;

yumaikas's Avatar yumaikas ha revisionato questo gist 1740902299. Vai alla revisione

1 file changed, 115 insertions

affirmations_v2.html(file creato)

@@ -0,0 +1,115 @@
1 + <!DOCTYPE html>
2 + <html>
3 +
4 + <div id="app"></div>
5 + <style>
6 +
7 + #app, body {
8 + height: 100%;
9 + }
10 + #edit-affirmations {
11 + display: grid;
12 + grid-template-rows: 80% 20%;
13 + height: 80vh;
14 + }
15 +
16 + .affirmation {
17 + width: 100%;
18 + }
19 + .half-half {
20 + display: grid;
21 + grid-template-columns: 50% 50%;
22 + width: 100%;
23 + }
24 + .right-corner {
25 + width: 25%;
26 + justify-self: end;
27 + }
28 + .w-full { width: 100%; }
29 + .grid { display: grid; }
30 +
31 + #edit-affirmations textarea {
32 + height: 80%;
33 + }
34 +
35 + </style>
36 +
37 + <template id="mode-edit-affirmations">
38 + <div id="edit-affirmations">
39 + <textarea class="wide 80-tall" id="affirmation-list"></textarea>
40 + <button id="save-affirmations">Save!</button>
41 + </div>
42 + </template>
43 + <template id="mode-view-affirmations">
44 + <div class="w-full grid">
45 + <button id="go-to-edit-mode" class="right-corner"></button>
46 + </div>
47 + <div id="affirmation-list"></div>
48 + <div class="half-half">
49 + <button id="add-affirmation">another</button>
50 + <button id="clear-affirmations">clear</button>
51 + </div>
52 + </template>
53 + <template id="affirmation"><h3 class="affirmation"></h3></template>
54 +
55 + <script src="hex.js"></script>
56 + <script>
57 + hex.arise(function() {
58 + let affirmation_idx = 0;
59 +
60 + let app = hex.id("app");
61 + const get_affirmations = () => hex.recall("affirmations") || ["You need to add affirmations!"];
62 + const next_affirmation = () => {
63 + const affirmations = get_affirmations().filter(x => !(/^\s+$/.test(x)));
64 + const ret = affirmations[affirmation_idx];
65 + affirmation_idx = (affirmation_idx + 1) % affirmations.length;
66 + return ret;
67 + }
68 +
69 + var affirmHeader = hex.spell("#affirmation", { value: ".affirmation" });
70 +
71 + var modeRecite = hex.spell("#mode-view-affirmations", {
72 + toEdit: ["#go-to-edit-mode", {t: "edit", on: {
73 + click() {
74 + hex.become(modeEdit, app, {affirmations: get_affirmations()});
75 + }
76 + } }],
77 + affirmations: ["#affirmation-list", (el, affirmation) => affirmHeader(el, { value: affirmation })],
78 + another: ["#add-affirmation",
79 + { t: "another",
80 + init() {
81 + this._affirmations = [];
82 + this._next = () => {
83 + this._affirmations.push(next_affirmation());
84 + this.affirmations(this._affirmations)
85 + }
86 + this._next();
87 + },
88 + on: { click() { this._next(); } }
89 + }
90 + ],
91 + clear: ["#clear-affirmations", {t: "clear", on: { click() { this._affirmations = []; this.affirmations([]); } } }]
92 + });
93 +
94 + var modeEdit = hex.spell("#mode-edit-affirmations", {
95 + affirmations: ["#affirmation-list", {
96 + init(arg) {
97 + this.affirmations = arg.affirmations.join("\n");
98 + this._affirmations = arg.affirmations || get_affirmations();
99 + },
100 + on: { input(e) { this._affirmations = e.target.value.split("\n"); } }
101 + }],
102 +
103 + save: ["#save-affirmations",
104 + { t:"save", on: { click() {
105 + console.log(this);
106 + hex.scrawl("affirmations", this._affirmations);
107 + hex.become(modeRecite, app, {affirmations: get_affirmations()});
108 + }}}
109 + ],
110 + });
111 +
112 + hex.become(modeRecite, app, {affirmations: get_affirmations()});
113 + });
114 + </script>
115 + </html>

yumaikas's Avatar yumaikas ha revisionato questo gist 1740897483. Vai alla revisione

1 file changed, 62 deletions

affirmations.html (file eliminato)

@@ -1,62 +0,0 @@
1 - <!DOCTYPE html>
2 - <html>
3 -
4 - <div id="app"></div>
5 -
6 - <script src="hex.js"></script>
7 -
8 - <template id="job-attr">
9 - <dt><button class="key"></button></dt>
10 - <dd><pre class="value"></pre></dd>
11 - </template>
12 -
13 - <template id="job-row">
14 - <dl class="job-content"></dl>
15 - <hr>
16 - </template>
17 -
18 -
19 - <script>
20 - let kvPair = hex.spell("#job-attr", {
21 - key: [".key", { init() { }, on: { click() {
22 - navigator.clipboard.writeText(this.value)
23 - } }}],
24 - value: ".value", // mutable
25 - });
26 -
27 - let jobRow = hex.spell("#job-row", {
28 - jobFields: [".job-content", (el, [key, value]) => kvPair(el, {key, value})],
29 - });
30 -
31 - hex.arise(function() {
32 - function job(employerName, city, state, position, startDate, endDate,
33 - contactName, email, phone) {
34 - return {
35 - employerName, city, state, position, startDate, endDate, contactName, email, phone
36 - }
37 - }
38 - let jobs = [
39 - job("Shift Paradigm", "Austin", "Texas", "Senior Software Developer", "06 - 2021", "04 - 2024 ",
40 - "Michelle Hytry", "michelle.hytry@shiftparadigm.com", "512-717-4097"),
41 - job("Linquest Corporation", "Colorado Springs", "Colorado", "Staff Software Engineer", "08 - 2019", "06 - 2021",
42 - "Susan Kang", "susan.kang@linquest.com", "(719) 884-8400" ),
43 - job("Qdoba Corporation", "Colorado Springs", "Colorado", "Line Cook", "06 - 2019", "08 - 2019",
44 - "N/a", "N/a", "719-592-9701"),
45 - job("Burrito Concepts", "Joplin", "Missouri", "Line Cook", "02 - 2019", "06 - 2019",
46 - "N/a", "N/a", "417-782-5300"),
47 - job("Greenshades Software", "Jacksonville", "Florida", "Software Developer", "11 - 2017", "12 - 2018",
48 - "N/a", "hr@greenshades.com", "904-807-0160"),
49 - job("Genesis Global Technologies", "Ft. Myers", "Florida", "Software Developer", "09 - 2014", "11 - 2017",
50 - "Mona Hilton", "monah@genesisgt.com", "239-337-2667"),
51 - ];
52 - let app_out = hex.q1("#app");
53 - app_out.innerHTML = "";
54 - window.J = [];
55 - for (let j of jobs) {
56 - window.J.push(jobRow(app_out, {
57 - jobFields: Object.entries(j),
58 - }));
59 - }
60 - });
61 - </script>
62 - </html>

yumaikas's Avatar yumaikas ha revisionato questo gist 1740897467. Vai alla revisione

1 file changed, 62 insertions

affirmations.html(file creato)

@@ -0,0 +1,62 @@
1 + <!DOCTYPE html>
2 + <html>
3 +
4 + <div id="app"></div>
5 +
6 + <script src="hex.js"></script>
7 +
8 + <template id="job-attr">
9 + <dt><button class="key"></button></dt>
10 + <dd><pre class="value"></pre></dd>
11 + </template>
12 +
13 + <template id="job-row">
14 + <dl class="job-content"></dl>
15 + <hr>
16 + </template>
17 +
18 +
19 + <script>
20 + let kvPair = hex.spell("#job-attr", {
21 + key: [".key", { init() { }, on: { click() {
22 + navigator.clipboard.writeText(this.value)
23 + } }}],
24 + value: ".value", // mutable
25 + });
26 +
27 + let jobRow = hex.spell("#job-row", {
28 + jobFields: [".job-content", (el, [key, value]) => kvPair(el, {key, value})],
29 + });
30 +
31 + hex.arise(function() {
32 + function job(employerName, city, state, position, startDate, endDate,
33 + contactName, email, phone) {
34 + return {
35 + employerName, city, state, position, startDate, endDate, contactName, email, phone
36 + }
37 + }
38 + let jobs = [
39 + job("Shift Paradigm", "Austin", "Texas", "Senior Software Developer", "06 - 2021", "04 - 2024 ",
40 + "Michelle Hytry", "michelle.hytry@shiftparadigm.com", "512-717-4097"),
41 + job("Linquest Corporation", "Colorado Springs", "Colorado", "Staff Software Engineer", "08 - 2019", "06 - 2021",
42 + "Susan Kang", "susan.kang@linquest.com", "(719) 884-8400" ),
43 + job("Qdoba Corporation", "Colorado Springs", "Colorado", "Line Cook", "06 - 2019", "08 - 2019",
44 + "N/a", "N/a", "719-592-9701"),
45 + job("Burrito Concepts", "Joplin", "Missouri", "Line Cook", "02 - 2019", "06 - 2019",
46 + "N/a", "N/a", "417-782-5300"),
47 + job("Greenshades Software", "Jacksonville", "Florida", "Software Developer", "11 - 2017", "12 - 2018",
48 + "N/a", "hr@greenshades.com", "904-807-0160"),
49 + job("Genesis Global Technologies", "Ft. Myers", "Florida", "Software Developer", "09 - 2014", "11 - 2017",
50 + "Mona Hilton", "monah@genesisgt.com", "239-337-2667"),
51 + ];
52 + let app_out = hex.q1("#app");
53 + app_out.innerHTML = "";
54 + window.J = [];
55 + for (let j of jobs) {
56 + window.J.push(jobRow(app_out, {
57 + jobFields: Object.entries(j),
58 + }));
59 + }
60 + });
61 + </script>
62 + </html>

yumaikas's Avatar yumaikas ha revisionato questo gist 1740897439. Vai alla revisione

1 file changed, 130 insertions

hex.js(file creato)

@@ -0,0 +1,130 @@
1 + const hex = (function() {
2 + const q = (el, sel) => !sel ? document.querySelectorAll(el) : el.querySelectorAll(sel);
3 + const id = (sel) => document.getElementById(sel);
4 + const q1 = (el, sel) => !sel ? document.querySelector(el) : el.querySelector(sel);
5 + const isObj = (obj) => { var type = typeof obj; return type === 'object' && !!obj; };
6 + const isSimple = (v) => {var type = typeof v; return type=== "number" || type === "string" || type === "boolean"; }
7 + const arise = (fn) => document.addEventListener('DOMContentLoaded', fn, false);
8 + const scrawl = (k, v) => localStorage.setItem(k, JSON.stringify(v));
9 + const recall = (k) => JSON.parse(localStorage.getItem(k) || "null");
10 +
11 + function setterFor(node) {
12 + switch (node.tagName) {
13 + case "INPUT":
14 + return (val) => node.value = val;
15 + default:
16 + return (val) => { node.innerText = val; };
17 + }
18 + }
19 +
20 + let templs = new Map();
21 + function from(tempSel) {
22 + let toClone;
23 + if (templs.has(tempSel)) {
24 + toClone = templs.get(tempSel);
25 + } else {
26 + let n = q1(tempSel);
27 + if (n) {
28 + templs.set(tempSel, n);
29 + toClone = n;
30 + } else {
31 + throw tempSel + " failed";
32 + }
33 + }
34 + return toClone.content.cloneNode(true);
35 + }
36 +
37 + function spell(template, config) {
38 +
39 + return function(into, start) {
40 + const basis = from(template)
41 + const state = { vars: {}, dom: {}, setters: {} };
42 + const interface = Object.create(spell);
43 + const recursionGuard = new Set();
44 +
45 + for (let [k, v] of Object.entries(config)) {
46 + const makeProp = (el, initialValue) => {
47 + state.setters[k] = setterFor(el);
48 +
49 + Object.defineProperty(interface, k, {
50 + get() { return state.vars[k] },
51 + set(v) {
52 + state.vars[k] = v;
53 + state.setters[k]((v || "") + "");
54 + }
55 + });
56 +
57 + interface[k] = initialValue;
58 + }
59 + if (typeof v === "string" && v) {
60 + const el = q1(basis, v);
61 + state.dom[k] = el;
62 + makeProp(el, start[k]);
63 + } else if (Array.isArray(v)) {
64 + if (v.length <= 0) {
65 + throw new Error("Empty array not valid!");
66 + }
67 + let el;
68 + if (typeof v[0] === "string") {
69 + el = q1(basis, v[0]);
70 + state.dom[k] = el;
71 + } else {
72 + throw new Error(`Selector for ${k} must be a string!`);
73 + }
74 +
75 + if (v.length == 1 && (typeof v[0]) == "string") {
76 + state.vars[k] = start[k];
77 + makeProp(el, start[k]);
78 + } else if (v.length == 2 && (typeof v[0]) === "string" && isSimple(v[1])) {
79 + makeProp(el, start[k] || v[1]);
80 + } else if (v.length == 2 && (typeof v[0]) === "string" && isObj(v[1])) {
81 + makeProp(el, start[k]);
82 + const obj = v[1];
83 + if (obj.on) {
84 + for (const [evt, fn] of Object.entries(obj.on)) {
85 + let inner = fn.bind(interface);
86 + let handler = function() {
87 + if (recursionGuard.has(handler)) { return; }
88 + try {
89 + recursionGuard.add(handler);
90 + inner(...arguments);
91 + } finally {
92 + recursionGuard.delete(handler);
93 + }
94 + };
95 +
96 + el.addEventListener(evt, handler);
97 + }
98 + }
99 + if (obj.init && typeof obj.init === "function") {
100 + obj.init.call(interface, start);
101 + }
102 +
103 + } else if (v.length === 2 &&
104 + (typeof v[0]) === "string" &&
105 + (typeof v[1]) === "function"
106 + && Array.isArray(start[k])
107 + ) {
108 + state.vars[k] = start[k].map((data) => v[1](el, data));
109 + interface[k] = function(new_data) {
110 + el.innerHTML = "";
111 + state.vars[k] = new_data.map((data) => v[1](el, data));
112 + }
113 + } else {
114 + throw new Error(`Invalid config: ${k}`);
115 + }
116 + }
117 + }
118 +
119 + interface._ = state;
120 + into.appendChild(basis);
121 + return interface;
122 + }
123 + }
124 +
125 +
126 +
127 + return {q, q1, id, from, arise, scrawl, recall, spell};
128 + })();
129 +
130 + window.hex = hex;
Più nuovi Più vecchi