Ultima attività 1740947436

Revisione 197594f338ad6c84e63dc8634299e9a20db937c7

affirmations_v2.html Raw Playground
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>
116
contrast-checker.html Raw Playground
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>
8body {
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>
142
hex.js Raw Playground
1const 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 if (["INPUT", "TEXTAREA"].indexOf(node.tagName) > -1) {
13 return (val) => node.value = val;
14 } else {
15 return (val) => node.innerText = val;
16 }
17 }
18
19 let templs = new Map();
20 function from(tempSel) {
21 let toClone;
22 if (templs.has(tempSel)) {
23 toClone = templs.get(tempSel);
24 } else {
25 let n = q1(tempSel);
26 if (n) {
27 templs.set(tempSel, n);
28 toClone = n;
29 } else {
30 throw tempSel + " failed";
31 }
32 }
33 return toClone.content.cloneNode(true);
34 }
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
43 function spell(template, config) {
44
45 return function(into, start) {
46 const basis = from(template)
47 const state = { vars: {}, dom: {}, setters: {} };
48 const interface = Object.create(spell);
49 const recursionGuard = new Set();
50
51 for (let [k, v] of Object.entries(config)) {
52 const makeProp = (el, initialValue) => {
53 state.setters[k] = setterFor(el);
54
55 Object.defineProperty(interface, k, {
56 get() { return state.vars[k] },
57 set(v) {
58 state.vars[k] = v;
59 state.setters[k]((v || "") + "");
60 }
61 });
62
63 interface[k] = initialValue;
64 }
65 if (typeof v === "string" && v) {
66 const el = q1(basis, v);
67 state.dom[k] = el;
68 makeProp(el, start[k]);
69 } else if (typeof v === "function") {
70 interface[k] = v.bind(interface);
71 } else if (Array.isArray(v)) {
72 if (v.length <= 0) {
73 throw new Error("Empty array not valid!");
74 }
75 let el;
76 if (typeof v[0] === "string") {
77 el = q1(basis, v[0]);
78 state.dom[k] = el;
79 } else {
80 throw new Error(`Selector for ${k} must be a string!`);
81 }
82
83 if (v.length == 1 && (typeof v[0]) == "string") {
84 state.vars[k] = start[k];
85 makeProp(el, start[k]);
86 } else if (v.length == 2 && (typeof v[0]) === "string" && isSimple(v[1])) {
87 makeProp(el, start[k] || v[1]);
88 } else if (v.length == 2 && (typeof v[0]) === "string" && isObj(v[1])) {
89 makeProp(el, start[k]);
90 const obj = v[1];
91 if (obj.t) {
92 interface[k] = start[k] || obj.t;
93 }
94 if (obj.on) {
95 for (const [evt, fn] of Object.entries(obj.on)) {
96 let inner = fn.bind(interface);
97 let handler = function() {
98 if (recursionGuard.has(handler)) { return; }
99 try {
100 recursionGuard.add(handler);
101 inner(...arguments);
102 } finally {
103 recursionGuard.delete(handler);
104 }
105 };
106
107 el.addEventListener(evt, handler);
108 }
109 }
110 if (obj.init && typeof obj.init === "function") {
111 obj.init.call(interface, start);
112 }
113
114 } else if (v.length === 3 &&
115 (typeof v[0]) === "string" &&
116 (typeof v[1]) === "function" &&
117 (typeof v[2]) === "function"
118 && Array.isArray(start[k])
119 ) {
120 // TODO:
121 state.vars[k] = start[k].map((data) => v[1](el, v[2](data)));
122 interface[k] = function(new_data) {
123 el.replaceChildren();
124 state.vars[k] = new_data.map((data) => v[1](el, v[2](data)));
125 }
126 } else {
127 throw new Error(`Invalid config: ${k}`);
128 }
129 }
130 }
131
132 interface._ = state;
133 into.appendChild(basis);
134 if (typeof interface.init === "function") {
135 interface.init(start);
136 }
137 return interface;
138 }
139 }
140
141 return {q, q1, id, from, arise, scrawl, recall, become, spell};
142})();
143
144window.hex = hex;
145