on 007ea71 help sign in
elk/runtime/Render/Element.js
324 lines 11 kB history raw
2ElmRuntime.Render.Element = function() {
3'use strict';
5var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
6var newElement = Utils.newElement, extract = Utils.extract,
7 addTransform = Utils.addTransform, removeTransform = Utils.removeTransform,
8 fromList = Utils.fromList, eq = Utils.eq;
10function setProps(props, e) {
11 e.style.width = (props.width |0) + 'px';
12 e.style.height = (props.height|0) + 'px';
13 if (props.opacity !== 1) { e.style.opacity = props.opacity; }
14 if (props.color.ctor === 'Just') {
15 e.style.backgroundColor = extract(props.color._0);
16 }
17 if (props.tag !== '') { e.id = props.tag; }
18 if (props.href !== '') {
19 var a = newElement('a');
20 a.href = props.href;
21 a.style.width = '100%';
22 a.style.height = '100%';
23 a.style.top = 0;
24 a.style.left = 0;
25 a.style.display = 'block';
26 a.style.position = 'absolute';
27 e.style.position = 'relative';
28 e.appendChild(a);
29 }
30 if (props.hover.ctor !== '_Tuple0') {
31 var overCount = 0;
32 e.addEventListener('mouseover', function() {
33 if (overCount++ > 0) return;
34 props.hover(true);
35 });
36 e.addEventListener('mouseout', function(evt) {
37 if (e.contains(evt.toElement || evt.relatedTarget)) return;
38 overCount = 0;
39 props.hover(false);
40 });
41 }
42 return e;
45function image(props, img) {
46 switch (img._0.ctor) {
47 case 'Plain': return plainImage(img._3);
48 case 'Fitted': return fittedImage(props.width, props.height, img._3);
49 case 'Cropped': return croppedImage(img,props.width,props.height,img._3);
50 case 'Tiled': return tiledImage(img._3);
51 }
54function plainImage(src) {
55 var img = newElement('img');
56 img.src = src;
57 img.name = src;
58 img.style.display = "block";
59 return img;
62function tiledImage(src) {
63 var div = newElement('div');
64 div.style.backgroundImage = 'url(' + src + ')';
65 return div;
68function fittedImage(w, h, src) {
69 var div = newElement('div');
70 div.style.background = 'url(' + src + ') no-repeat center';
71 div.style.webkitBackgroundSize = 'cover';
72 div.style.MozBackgroundSize = 'cover';
73 div.style.OBackgroundSize = 'cover';
74 div.style.backgroundSize = 'cover';
75 return div;
78function croppedImage(elem, w, h, src) {
79 var pos = elem._0._0;
80 var e = newElement('div');
81 e.style.overflow = "hidden";
83 var img = newElement('img');
84 img.onload = function() {
85 var sw = w / elem._1, sh = h / elem._2;
86 img.style.width = ((this.width * sw)|0) + 'px';
87 img.style.height = ((this.height * sh)|0) + 'px';
88 img.style.marginLeft = ((- pos._0 * sw)|0) + 'px';
89 img.style.marginTop = ((- pos._1 * sh)|0) + 'px';
90 };
91 img.src = src;
92 img.name = src;
93 e.appendChild(img);
94 return e;
97function goIn(e) { e.style.position = 'absolute'; return e; }
98function goDown(e) { return e }
99function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e; }
100function flowWith(f, array) {
101 var container = newElement('div');
102 for (var i = array.length; i--; ) {
103 container.appendChild(f(render(array[i])));
104 }
105 return container;
108function flow(dir,elist) {
109 var array = fromList(elist);
110 switch(dir.ctor) {
111 case "DDown": array.reverse();
112 case "DUp": return flowWith(goDown,array);
113 case "DRight": array.reverse();
114 case "DLeft": return flowWith(goRight,array);
115 case "DOut": array.reverse();
116 case "DIn": return flowWith(goIn,array);
117 }
120function toPos(pos) {
121 switch(pos.ctor) {
122 case "Absolute": return pos._0 + "px";
123 case "Relative": return (pos._0 * 100) + "%";
124 }
127// must clear right, left, top, bottom, and transform
128// before calling this function
129function setPos(pos,w,h,e) {
130 e.style.position = 'absolute';
131 e.style.margin = 'auto';
132 var transform = '';
133 switch(pos.horizontal.ctor) {
134 case 'P': e.style.right = toPos(pos.x); e.style.removeProperty('left'); break;
135 case 'Z': transform = 'translateX(' + ((-w/2)|0) + 'px) ';
136 case 'N': e.style.left = toPos(pos.x); e.style.removeProperty('right'); break;
137 }
138 switch(pos.vertical.ctor) {
139 case 'N': e.style.bottom = toPos(pos.y); e.style.removeProperty('top'); break;
140 case 'Z': transform += 'translateY(' + ((-h/2)|0) + 'px)';
141 case 'P': e.style.top = toPos(pos.y); e.style.removeProperty('bottom'); break;
142 }
143 if (transform !== '') addTransform(e.style, transform);
144 return e;
147function container(pos,elem) {
148 var e = render(elem);
149 setPos(pos, elem.props.width, elem.props.height, e);
150 var div = newElement('div');
151 div.style.position = 'relative';
152 div.style.overflow = 'hidden';
153 div.appendChild(e);
154 return div;
157function rawHtml(elem) {
158 var html = elem.html;
159 var args = elem.args;
160 var guid = elem.guid;
161 var align = elem.align;
163 var div = newElement('div');
164 div.innerHTML = html;
165 div.style.visibility = "hidden";
166 if (align) div.style.textAlign = align;
167 document.body.appendChild(div);
169 for (var i = args.length; i--; ) {
170 var arg = args[i];
171 var span = document.getElementById('md-' + guid + '-' + i);
172 if (arg.isText) {
173 span.innerHTML = arg;
174 } else {
175 span.style.display = 'block';
176 span.style.width = arg.props.width + 'px';
177 span.style.height = arg.props.height + 'px';
178 span.appendChild(render(arg));
179 }
180 }
181 document.body.removeChild(div);
182 div.style.visibility = 'visible';
183 return div;
186function render(elem) { return setProps(elem.props, makeElement(elem)); }
187function makeElement(e) {
188 var elem = e.element;
189 switch(elem.ctor) {
190 case 'Image': return image(e.props, elem);
191 case 'Flow': return flow(elem._0, elem._1);
192 case 'Container': return container(elem._0, elem._1);
193 case 'Spacer': return newElement('div');
194 case 'RawHtml': return rawHtml(elem);
195 case 'Custom': return elem.render(elem.model);
196 }
199function update(node, curr, next) {
200 if (node.tagName === 'A') { node = node.firstChild; }
201 if (curr.props.id === next.props.id) return updateProps(node, curr, next);
202 if (curr.element.ctor !== next.element.ctor) {
203 node.parentNode.replaceChild(render(next),node);
204 return true;
205 }
206 var nextE = next.element, currE = curr.element;
207 switch(nextE.ctor) {
208 case "Spacer": break;
209 case "RawHtml":
210 // only markdown blocks have guids, so this must be a text block
211 if (nextE.guid === null) {
212 node.innerHTML = nextE.html;
213 break;
214 }
215 if (nextE.guid !== currE.guid) {
216 node.parentNode.replaceChild(render(next),node);
217 return true;
218 }
219 var nargs = nextE.args;
220 var cargs = currE.args;
221 for (var i = nargs.length; i--; ) {
222 var narg = nargs[i];
223 var carg = cargs[i]
224 if (narg == carg) continue;
225 var span = document.getElementById('md-' + currE.guid + '-' + i);
226 if (narg.isElement) {
227 if (carg.isElement) {
228 update(span, carg, narg);
229 } else {
230 span.style.display = 'block';
231 var e = render(narg);
232 span.innerHTML = '';
233 span.appendChild(e);
234 }
235 } else {
236 span.style.display = 'inline';
237 span.innerHTML = narg;
238 }
239 }
240 break;
241 case "Image":
242 if (nextE._0.ctor === 'Plain') {
243 if (nextE._3 !== currE._3) node.src = nextE._3;
244 } else if (!eq(nextE,currE) ||
245 next.props.width !== curr.props.width ||
246 next.props.height !== curr.props.height) {
247 node.parentNode.replaceChild(render(next),node);
248 return true;
249 }
250 break;
251 case "Flow":
252 var arr = fromList(nextE._1);
253 for (var i = arr.length; i--; ) { arr[i] = arr[i].element.ctor; }
254 if (nextE._0.ctor !== currE._0.ctor) {
255 node.parentNode.replaceChild(render(next),node);
256 return true;
257 }
258 var nexts = fromList(nextE._1);
259 var kids = node.childNodes;
260 if (nexts.length !== kids.length) {
261 node.parentNode.replaceChild(render(next),node);
262 return true;
263 }
264 var currs = fromList(currE._1);
265 var goDir = function(x) { return x; };
266 switch(nextE._0.ctor) {
267 case "DDown": case "DUp": goDir = goDown; break;
268 case "DRight": case "DLeft": goDir = goRight; break;
269 case "DOut": case "DIn": goDir = goIn; break;
270 }
271 for (var i = kids.length; i-- ;) {
272 update(kids[i],currs[i],nexts[i]);
273 goDir(kids[i]);
274 }
275 break;
276 case "Container":
277 update(node.firstChild, currE._1, nextE._1);
278 setPos(nextE._0, nextE._1.props.width, nextE._1.props.height, node.firstChild);
279 break;
280 case "Custom":
281 if (currE.type === nextE.type) {
282 var done = nextE.update(node, currE.model, nextE.model);
283 if (done) return;
284 } else {
285 return node.parentNode.replaceChild(render(next), node);
286 }
287 }
288 updateProps(node, curr, next);
291function updateProps(node, curr, next) {
292 var props = next.props, currP = curr.props, e = node;
293 if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
294 if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
295 if (props.opacity !== 1 && props.opacity !== currP.opacity) {
296 e.style.opacity = props.opacity;
297 }
298 var nextColor = (props.color.ctor === 'Just' ?
299 extract(props.color._0) : '');
300 if (e.style.backgroundColor !== nextColor) {
301 e.style.backgroundColor = (nextColor === '' ? 'transparent' : nextColor);
302 }
303 if (props.tag !== currP.tag) { e.id = props.tag; }
304 if (props.href !== currP.href) {
305 if (currP.href === '') {
306 var a = newElement('a');
307 a.href = props.href;
308 a.style.width = '100%';
309 a.style.height = '100%';
310 a.style.top = 0;
311 a.style.left = 0;
312 a.style.display = 'block';
313 a.style.position = 'absolute';
314 e.style.position = 'relative';
315 e.appendChild(a);
316 } else {
317 node.lastNode.href = props.href;
318 }
319 }
322return { render:render, update:update };
324};