aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoris2020-02-03 18:28:50 +0100
committerJoris2020-02-03 18:28:50 +0100
commitcbcc269be607cc964fbd69d179d8a0e8b8e4bffa (patch)
tree7a8135f7ede7e1ff02bc1cf1d697a8c39a56e962 /src
parentc7e20a6dd01a2ae049c451d18511708aaee60a19 (diff)
downloadcooking-cbcc269be607cc964fbd69d179d8a0e8b8e4bffa.tar.gz
cooking-cbcc269be607cc964fbd69d179d8a0e8b8e4bffa.tar.bz2
cooking-cbcc269be607cc964fbd69d179d8a0e8b8e4bffa.zip
Extract dom and number utilities to external files
Diffstat (limited to 'src')
-rw-r--r--src/dom.ts54
-rw-r--r--src/main.ts71
-rw-r--r--src/number.ts91
3 files changed, 216 insertions, 0 deletions
diff --git a/src/dom.ts b/src/dom.ts
new file mode 100644
index 0000000..6b1c803
--- /dev/null
+++ b/src/dom.ts
@@ -0,0 +1,54 @@
+type Attribute = string | boolean | ((e: Event) => void)
+
+type Child = Element | string
+
+export function h(tag: string, attrs: {[key: string]: Attribute}, children: Array<Child> = []): Element {
+ let element = document.createElement(tag)
+
+ for (let name in attrs) {
+ let value = attrs[name]
+ if (typeof value === 'boolean') {
+ if (value) {
+ element.setAttribute(name, name)
+ }
+ } else if (typeof value === 'function') {
+ (element as any)[name] = (e: Event) => {
+ (value as ((e: Event) => void))(e)
+ }
+ } else {
+ element.setAttribute(name, value)
+ }
+ }
+
+ children.forEach(child => {
+ if (typeof child === 'string') {
+ element.appendChild(document.createTextNode(child))
+ } else {
+ element.appendChild(child)
+ }
+ })
+
+ return element
+}
+
+export function toggleClassName(node: Element, className: string) {
+ if (node.className === className) {
+ node.className = ''
+ } else {
+ node.className = className
+ }
+}
+
+export function nodeListToArray(nodeList: NodeListOf<HTMLElement>): HTMLElement[] {
+ const xs: HTMLElement[] = [];
+ nodeList.forEach(node => xs.push(node))
+ return xs
+}
+
+export function replace(node: Node, replacement: Node) {
+ const parentNode = node.parentNode
+
+ if (parentNode) {
+ parentNode.replaceChild(replacement, node)
+ }
+}
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..ad83591
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,71 @@
+import * as number from './number'
+import * as dom from './dom'
+
+/* Set up inputs for the ingredients */
+
+const itemEntries =
+ dom.nodeListToArray(document.querySelectorAll('.g-Recipe__Content ul > li'))
+ .map(itemNode => ({ tag: 'li', node: itemNode }))
+
+const h1 = document.querySelector<HTMLElement>('.g-Recipe__Content h1')
+
+if (h1 !== null) {
+ itemEntries.push({ tag: 'h1', node: h1 })
+}
+
+const inputs = setupInputs(itemEntries)
+
+inputs.map(input => {
+ input.node.oninput = e => {
+ if (e.target !==null) {
+ const parsed = number.parse((e.target as HTMLInputElement).value)
+
+ if (parsed !== undefined && parsed.before === '' && parsed.after === '') {
+ const factor = parsed.number / input.number
+ inputs.map(input2 => {
+ if (input.node !== input2.node) {
+ input2.node.value = number.prettyPrint(input2.number * factor)
+ }
+ })
+ }
+ }
+ }
+})
+
+interface InputEntry {
+ tag: string;
+ node: HTMLElement;
+}
+
+interface InputResult {
+ number: number,
+ node: HTMLInputElement
+}
+
+function setupInputs(xs: InputEntry[]): InputResult[] {
+ const res: InputResult[] = []
+
+ xs.forEach(x => {
+ const parsed = number.parse(x.node.innerText)
+
+ if (parsed !== undefined) {
+ const numberNode = number.node(x.tag, parsed)
+ dom.replace(x.node, numberNode.node)
+ res.push({
+ number: parsed.number,
+ node: numberNode.number
+ })
+ }
+ })
+
+ return res
+}
+
+/* Set up done marks for steps */
+
+dom.nodeListToArray(document.querySelectorAll('.g-Recipe__Content ol > li')).forEach(todo => {
+ todo.onclick = e => {
+ dom.toggleClassName(todo, 'g-Recipe__Completed')
+ e.stopPropagation()
+ }
+})
diff --git a/src/number.ts b/src/number.ts
new file mode 100644
index 0000000..3021dc9
--- /dev/null
+++ b/src/number.ts
@@ -0,0 +1,91 @@
+import { h } from './dom'
+
+export interface Parsed {
+ before: string;
+ number: number;
+ after: string;
+}
+
+export function parse(str: string): Parsed | undefined {
+ let start;
+ for (start = 0; start < str.length; start++) {
+ if (isDigit(str.charAt(start))) {
+ break
+ }
+ }
+
+ if (start === str.length) {
+ return undefined
+ }
+
+ // Integer part
+ let integerPart = '';
+ let end = start;
+ for (; end < str.length; end++) {
+ const c = str.charAt(end)
+
+ if (!isDigit(c)) {
+ break
+ } else {
+ integerPart += c
+ }
+ }
+
+ // Decimal sign
+ if (end < str.length && (str.charAt(end) === '.' || str.charAt(end) === ',')) {
+ end++
+ }
+
+ // Decimal part
+ let decimalPart = '';
+ for (; end < str.length; end++) {
+ const c = str.charAt(end)
+
+ if (!isDigit(c)) {
+ break
+ } else {
+ decimalPart += c
+ }
+ }
+
+
+ return {
+ before: str.substring(0, start),
+ number: parseFloat(integerPart + (decimalPart !== '' ? '.' + decimalPart : '')),
+ after: str.substring(end, str.length)
+ }
+}
+
+function isDigit(c: string) {
+ return c >= '0' && c <= '9'
+}
+
+export interface Node {
+ node: Element;
+ number: HTMLInputElement;
+}
+
+export function node(tag: string, parsedNumber: Parsed): Node {
+ const numberElement = h(
+ 'input',
+ {
+ 'class': 'g-Number',
+ 'value': prettyPrint(parsedNumber.number)
+ }
+ ) as HTMLInputElement
+
+ return {
+ node: h(tag, {}, [parsedNumber.before, numberElement, parsedNumber.after]),
+ number: numberElement,
+ }
+}
+
+export function prettyPrint(n: number): string {
+ const xs = n.toString().split('.')
+
+ if (xs.length == 2) {
+ return xs[0] + ',' + xs[1].substring(0, 2)
+ } else {
+ return xs[0]
+ }
+}