/* Set up done marks for steps */ nodeListToArray(document.querySelectorAll('.g-Recipe__Content ol > li')).forEach(todo => { todo.onclick = e => { toggleClassName(todo, 'g-Recipe__Completed') e.stopPropagation() } }) function toggleClassName(node: Element, className: string) { if (node.className === className) { node.className = '' } else { node.className = className } } /* Set up inputs for the ingredients */ const itemEntries = nodeListToArray(document.querySelectorAll('.g-Recipe__Content ul > li')) .map(itemNode => ({ tag: 'li', node: itemNode })) const h1 = document.querySelector('.g-Recipe__Content h1') if (h1 !== null) { itemEntries.push( { tag: 'h1' , node: h1 } ) } const inputs = setInputs(itemEntries) inputs.map(input => { input.node.oninput = e => { if (e.target !==null) { const parsed: ParsedNumber | undefined = parseNumber((e.target as HTMLInputElement).value) if (parsed !== undefined && parsed.before === '' && parsed.after === '') { const factor = parsed.number / input.number inputs.map(input2 => input2.node.value = prettyPrintNumber(input2.number * factor)) } } } }) interface InputEntry { tag: string; node: HTMLElement; } interface InputResult { number: number, node: HTMLInputElement } function setInputs(xs: InputEntry[]): InputResult[] { const res: InputResult[] = [] xs.forEach(x => { const parsed = parseNumber(x.node.innerText) if (parsed !== undefined) { const numberElement = createNumberElement(x.tag, parsed) const parentNode = x.node.parentNode if (parentNode) { parentNode.replaceChild(numberElement.node, x.node) res.push({ number: parsed.number, node: numberElement.number }) } } }) return res } interface NumberElement { node: Element; number: HTMLInputElement; } function createNumberElement(tag: string, parsedNumber: ParsedNumber): NumberElement { const numberElement = h( 'input', { 'class': 'g-Number', 'value': prettyPrintNumber(parsedNumber.number) } ) as HTMLInputElement return { node: h(tag, {}, [parsedNumber.before, numberElement, parsedNumber.after]), number: numberElement, } } interface ParsedNumber { before: string; number: number; after: string; } function parseNumber(str: string): ParsedNumber | 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' } function nodeListToArray(nodeList: NodeListOf): HTMLElement[] { const xs: HTMLElement[] = []; nodeList.forEach(node => xs.push(node)) return xs } function prettyPrintNumber(n: number): string { const xs = n.toString().split('.') if (xs.length == 2) { return xs[0] + ',' + xs[1].substring(0, 2) } else { return xs[0] } } /* Element creation helper */ type Attribute = string | boolean | ((e: Event) => void) type Child = Element | string function h(tag: string, attrs: {[key: string]: Attribute}, children: Array = []): 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) => { if (e.target !== null) { console.log((e.target as HTMLInputElement).value) } } } else { element.setAttribute(name, value) } } children.forEach(child => { if (typeof child === 'string') { element.appendChild(document.createTextNode(child)) } else { element.appendChild(child) } }) return element }