Skip to content

Commit 958ed5c

Browse files
committed
Initial implementation
1 parent 05ee67a commit 958ed5c

File tree

7 files changed

+7324
-0
lines changed

7 files changed

+7324
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
node_modules
2+
.cache
3+
dist

LICENSE

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2018, Risto Stevcev
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
* Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

index.js

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
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

Comments
 (0)