yumaikas bu gisti düzenledi . Düzenlemeye git
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 bu gisti düzenledi . Düzenlemeye git
2 files changed, 169 insertions, 14 deletions
contrast-checker.html(dosya oluşturuldu)
@@ -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 bu gisti düzenledi . Düzenlemeye git
1 file changed, 115 insertions
affirmations_v2.html(dosya oluşturuldu)
@@ -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 bu gisti düzenledi . Düzenlemeye git
1 file changed, 62 deletions
affirmations.html (dosya silindi)
@@ -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 bu gisti düzenledi . Düzenlemeye git
1 file changed, 62 insertions
affirmations.html(dosya oluşturuldu)
@@ -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 bu gisti düzenledi . Düzenlemeye git
1 file changed, 130 insertions
hex.js(dosya oluşturuldu)
@@ -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; |