|
| 1 | +const { forEach } = require('callbag-basics') |
| 2 | +const morphdom = require('morphdom') |
| 3 | + |
| 4 | +const isCallbag = x => typeof x === 'function' |
| 5 | + , isNode = x => x instanceof Node |
| 6 | + |
| 7 | +const isAttributes = x => !(x instanceof Array) && x instanceof Object |
| 8 | + , isChildren = x => x instanceof Array && x.every(item => isNode(item) || isCallbag(item)) |
| 9 | + , isText = x => typeof x === 'string' |
| 10 | + , isNone = x => x === undefined |
| 11 | + |
| 12 | +const getArgs = (arg1, arg2) => |
| 13 | + isNone(arg1) && isNone(arg2) ? |
| 14 | + { attributes: {}, children: [] } : |
| 15 | + isChildren(arg1) && isNone(arg2) ? |
| 16 | + { attributes: {}, children: arg1 } : |
| 17 | + isAttributes(arg1) && isNone(arg2) ? |
| 18 | + { attributes: arg1, children: [] } : |
| 19 | + isText(arg1) && isNone(arg2) ? |
| 20 | + { attributes: {}, children: [document.createTextNode(arg1)] } : |
| 21 | + isAttributes(arg1) && isText(arg2) ? |
| 22 | + { attributes: arg1, children: [document.createTextNode(arg2)] } : |
| 23 | + isAttributes(arg1) && isChildren(arg2) ? |
| 24 | + { attributes: arg1, children: arg2 } : |
| 25 | + isCallbag(arg1) && isNone(arg2) ? |
| 26 | + { attributes: arg1, children: [] } : |
| 27 | + isNone(arg1) && isCallbag(arg2) ? |
| 28 | + { attributes: {}, children: arg2 } : |
| 29 | + isCallbag(arg1) && isCallbag(arg2) ? |
| 30 | + { attributes: arg1, children: arg2 } : null |
| 31 | + |
| 32 | +const setAttribute = (element, key) => value => { |
| 33 | + switch (key) { |
| 34 | + case 'id': element.id = value ;break |
| 35 | + case 'className': element.className = value ;break |
| 36 | + case 'classSet': |
| 37 | + element.className += ' ' + Object.entries(value) |
| 38 | + .reduce(([className, isIncluded]) => isIncluded ? className : '') |
| 39 | + .join(' ') ;break |
| 40 | + case 'style': |
| 41 | + Object.entries(value).forEach(([property, value]) => { |
| 42 | + element.style[property] = value |
| 43 | + }) ;break |
| 44 | + default: |
| 45 | + element[key] = value |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +const setAttributes = element => attributes => { |
| 50 | + for (const [key, value] of Object.entries(attributes)) { |
| 51 | + setAttribute(element, key)(value) |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +const removeChildren = element => { |
| 56 | + while (element.firstChild) element.removeChild(element.firstChild) |
| 57 | +} |
| 58 | + |
| 59 | +const setChildren = element => children => { |
| 60 | + removeChildren(element) |
| 61 | + |
| 62 | + children.forEach((_child, index) => { |
| 63 | + var { child, child$ } = { |
| 64 | + child: isNode(_child) ? _child : |
| 65 | + isText(_child) ? document.createTextNode(_child) : null, |
| 66 | + child$: isCallbag(_child) ? _child : null |
| 67 | + } |
| 68 | + |
| 69 | + if (child) |
| 70 | + element.appendChild(child) |
| 71 | + |
| 72 | + if (child$) |
| 73 | + forEach(newChild => { |
| 74 | + if (!child) { |
| 75 | + child = newChild |
| 76 | + element.insertBefore(child, element.children[index] || null) |
| 77 | + } |
| 78 | + morphdom(child, newChild) |
| 79 | + })(child$) |
| 80 | + }) |
| 81 | +} |
| 82 | + |
| 83 | +const setElement = (element, { attributes, children }) => { |
| 84 | + setAttributes(element)(attributes) |
| 85 | + |
| 86 | + if (isCallbag(children)) |
| 87 | + forEach(setChildren(element))(children) |
| 88 | + else |
| 89 | + setChildren(element)(children) |
| 90 | + |
| 91 | + return element |
| 92 | +} |
| 93 | + |
| 94 | +const _element = tagName => (arg1, arg2) => { |
| 95 | + const args = getArgs(arg1, arg2) |
| 96 | + if (args === null) throw new Error('Invalid arguments passed to ddom') |
| 97 | + return setElement(document.createElement(tagName), args) |
| 98 | +} |
| 99 | + |
| 100 | +module.exports = { |
| 101 | + a: _element('a'), |
| 102 | + abbr: _element('abbr'), |
| 103 | + acronym: _element('acronym'), |
| 104 | + address: _element('address'), |
| 105 | + applet: _element('applet'), |
| 106 | + area: _element('area'), |
| 107 | + article: _element('article'), |
| 108 | + aside: _element('aside'), |
| 109 | + audio: _element('audio'), |
| 110 | + b: _element('b'), |
| 111 | + base: _element('base'), |
| 112 | + basefont: _element('basefont'), |
| 113 | + bdo: _element('bdo'), |
| 114 | + bgsound: _element('bgsound'), |
| 115 | + big: _element('big'), |
| 116 | + blink: _element('blink'), |
| 117 | + blockquote: _element('blockquote'), |
| 118 | + body: _element('body'), |
| 119 | + br: _element('br'), |
| 120 | + button: _element('button'), |
| 121 | + canvas: _element('canvas'), |
| 122 | + caption: _element('caption'), |
| 123 | + center: _element('center'), |
| 124 | + cite: _element('cite'), |
| 125 | + code: _element('code'), |
| 126 | + col: _element('col'), |
| 127 | + colgroup: _element('colgroup'), |
| 128 | + command: _element('command'), |
| 129 | + datalist: _element('datalist'), |
| 130 | + dd: _element('dd'), |
| 131 | + del: _element('del'), |
| 132 | + details: _element('details'), |
| 133 | + dfn: _element('dfn'), |
| 134 | + div: _element('div'), |
| 135 | + dl: _element('dl'), |
| 136 | + dt: _element('dt'), |
| 137 | + em: _element('em'), |
| 138 | + embed: _element('embed'), |
| 139 | + fieldset: _element('fieldset'), |
| 140 | + figcaption: _element('figcaption'), |
| 141 | + figure: _element('figure'), |
| 142 | + font: _element('font'), |
| 143 | + footer: _element('footer'), |
| 144 | + form: _element('form'), |
| 145 | + frame: _element('frame'), |
| 146 | + frameset: _element('frameset'), |
| 147 | + h1: _element('h1'), |
| 148 | + h2: _element('h2'), |
| 149 | + h3: _element('h3'), |
| 150 | + h4: _element('h4'), |
| 151 | + h5: _element('h5'), |
| 152 | + h6: _element('h6'), |
| 153 | + head: _element('head'), |
| 154 | + header: _element('header'), |
| 155 | + hgroup: _element('hgroup'), |
| 156 | + hr: _element('hr'), |
| 157 | + html: _element('html'), |
| 158 | + i: _element('i'), |
| 159 | + iframe: _element('iframe'), |
| 160 | + img: _element('img'), |
| 161 | + input: _element('input'), |
| 162 | + ins: _element('ins'), |
| 163 | + isindex: _element('isindex'), |
| 164 | + kbd: _element('kbd'), |
| 165 | + keygen: _element('keygen'), |
| 166 | + label: _element('label'), |
| 167 | + legend: _element('legend'), |
| 168 | + li: _element('li'), |
| 169 | + link: _element('link'), |
| 170 | + listing: _element('listing'), |
| 171 | + map: _element('map'), |
| 172 | + mark: _element('mark'), |
| 173 | + marquee: _element('marquee'), |
| 174 | + math: _element('math'), |
| 175 | + menu: _element('menu'), |
| 176 | + meta: _element('meta'), |
| 177 | + meter: _element('meter'), |
| 178 | + nav: _element('nav'), |
| 179 | + nextid: _element('nextid'), |
| 180 | + nobr: _element('nobr'), |
| 181 | + noembed: _element('noembed'), |
| 182 | + noframes: _element('noframes'), |
| 183 | + noscript: _element('noscript'), |
| 184 | + object: _element('object'), |
| 185 | + ol: _element('ol'), |
| 186 | + optgroup: _element('optgroup'), |
| 187 | + option: _element('option'), |
| 188 | + output: _element('output'), |
| 189 | + p: _element('p'), |
| 190 | + param: _element('param'), |
| 191 | + plaintext: _element('plaintext'), |
| 192 | + pre: _element('pre'), |
| 193 | + progress: _element('progress'), |
| 194 | + q: _element('q'), |
| 195 | + rp: _element('rp'), |
| 196 | + rt: _element('rt'), |
| 197 | + ruby: _element('ruby'), |
| 198 | + s: _element('s'), |
| 199 | + samp: _element('samp'), |
| 200 | + script: _element('script'), |
| 201 | + section: _element('section'), |
| 202 | + select: _element('select'), |
| 203 | + small: _element('small'), |
| 204 | + source: _element('source'), |
| 205 | + spacer: _element('spacer'), |
| 206 | + span: _element('span'), |
| 207 | + strike: _element('strike'), |
| 208 | + strong: _element('strong'), |
| 209 | + style: _element('style'), |
| 210 | + sub: _element('sub'), |
| 211 | + sup: _element('sup'), |
| 212 | + summary: _element('summary'), |
| 213 | + svg: _element('svg'), |
| 214 | + table: _element('table'), |
| 215 | + tbody: _element('tbody'), |
| 216 | + td: _element('td'), |
| 217 | + textarea: _element('textarea'), |
| 218 | + tfoot: _element('tfoot'), |
| 219 | + th: _element('th'), |
| 220 | + thead: _element('thead'), |
| 221 | + time: _element('time'), |
| 222 | + title: _element('title'), |
| 223 | + tr: _element('tr'), |
| 224 | + track: _element('track'), |
| 225 | + tt: _element('tt'), |
| 226 | + u: _element('u'), |
| 227 | + ul: _element('ul'), |
| 228 | + var: _element('var'), |
| 229 | + video: _element('video'), |
| 230 | + wbr: _element('wbr'), |
| 231 | + xmp: _element('xmp') |
| 232 | +} |
0 commit comments