on 9df2eac help sign in
elk/runtime/Render/Element.js
357 lines 12 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 e.style.pointerEvents = 'auto';
32 e.elm_hover_handler = props.hover;
33 e.elm_hover_count = 0;
34 e.addEventListener('mouseover', function() {
35 if (e.elm_hover_count++ > 0) return;
36 var handler = e.elm_hover_handler;
37 if (handler !== null) {
38 handler(true);
39 }
40 });
41 e.addEventListener('mouseout', function(evt) {
42 if (e.contains(evt.toElement || evt.relatedTarget)) return;
43 e.elm_hover_count = 0;
44 var handler = e.elm_hover_handler;
45 if (handler !== null) {
46 handler(false);
47 }
48 });
49 }
50 if (props.click.ctor !== '_Tuple0') {
51 e.style.pointerEvents = 'auto';
52 e.elm_click_handler = props.click;
53 e.addEventListener('click', function() {
54 e.elm_click_handler(Tuple0);
55 });
56 }
57 return e;
60function image(props, img) {
61 switch (img._0.ctor) {
62 case 'Plain': return plainImage(img._3);
63 case 'Fitted': return fittedImage(props.width, props.height, img._3);
64 case 'Cropped': return croppedImage(img,props.width,props.height,img._3);
65 case 'Tiled': return tiledImage(img._3);
66 }
69function plainImage(src) {
70 var img = newElement('img');
71 img.src = src;
72 img.name = src;
73 img.style.display = "block";
74 return img;
77function tiledImage(src) {
78 var div = newElement('div');
79 div.style.backgroundImage = 'url(' + src + ')';
80 return div;
83function fittedImage(w, h, src) {
84 var div = newElement('div');
85 div.style.background = 'url(' + src + ') no-repeat center';
86 div.style.webkitBackgroundSize = 'cover';
87 div.style.MozBackgroundSize = 'cover';
88 div.style.OBackgroundSize = 'cover';
89 div.style.backgroundSize = 'cover';
90 return div;
93function croppedImage(elem, w, h, src) {
94 var pos = elem._0._0;
95 var e = newElement('div');
96 e.style.overflow = "hidden";
98 var img = newElement('img');
99 img.onload = function() {
100 var sw = w / elem._1, sh = h / elem._2;
101 img.style.width = ((this.width * sw)|0) + 'px';
102 img.style.height = ((this.height * sh)|0) + 'px';
103 img.style.marginLeft = ((- pos._0 * sw)|0) + 'px';
104 img.style.marginTop = ((- pos._1 * sh)|0) + 'px';
105 };
106 img.src = src;
107 img.name = src;
108 e.appendChild(img);
109 return e;
112function goIn(e) { e.style.position = 'absolute'; return e; }
113function goDown(e) { return e }
114function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e; }
115function flowWith(f, array) {
116 var container = newElement('div');
117 if (f == goIn) container.style.pointerEvents = 'none';
119 for (var i = array.length; i--; ) {
120 container.appendChild(f(render(array[i])));
121 }
122 return container;
125function flow(dir,elist) {
126 var array = fromList(elist);
127 switch(dir.ctor) {
128 case "DDown": array.reverse();
129 case "DUp": return flowWith(goDown,array);
130 case "DRight": array.reverse();
131 case "DLeft": return flowWith(goRight,array);
132 case "DOut": array.reverse();
133 case "DIn": return flowWith(goIn,array);
134 }
137function toPos(pos) {
138 switch(pos.ctor) {
139 case "Absolute": return pos._0 + "px";
140 case "Relative": return (pos._0 * 100) + "%";
141 }
144// must clear right, left, top, bottom, and transform
145// before calling this function
146function setPos(pos,w,h,e) {
147 e.style.position = 'absolute';
148 e.style.margin = 'auto';
149 var transform = '';
150 switch(pos.horizontal.ctor) {
151 case 'P': e.style.right = toPos(pos.x); e.style.removeProperty('left'); break;
152 case 'Z': transform = 'translateX(' + ((-w/2)|0) + 'px) ';
153 case 'N': e.style.left = toPos(pos.x); e.style.removeProperty('right'); break;
154 }
155 switch(pos.vertical.ctor) {
156 case 'N': e.style.bottom = toPos(pos.y); e.style.removeProperty('top'); break;
157 case 'Z': transform += 'translateY(' + ((-h/2)|0) + 'px)';
158 case 'P': e.style.top = toPos(pos.y); e.style.removeProperty('bottom'); break;
159 }
160 if (transform !== '') addTransform(e.style, transform);
161 return e;
164function container(pos,elem) {
165 var e = render(elem);
166 setPos(pos, elem.props.width, elem.props.height, e);
167 var div = newElement('div');
168 div.style.position = 'relative';
169 div.style.overflow = 'hidden';
170 div.appendChild(e);
171 return div;
174function rawHtml(elem) {
175 var html = elem.html;
176 var args = elem.args;
177 var guid = elem.guid;
178 var align = elem.align;
180 var div = newElement('div');
181 div.innerHTML = html;
182 div.style.visibility = "hidden";
183 if (align) div.style.textAlign = align;
184 document.body.appendChild(div);
186 for (var i = args.length; i--; ) {
187 var arg = args[i];
188 var span = document.getElementById('md-' + guid + '-' + i);
189 if (arg.isText) {
190 span.innerHTML = arg;
191 } else {
192 span.style.display = 'block';
193 span.style.width = arg.props.width + 'px';
194 span.style.height = arg.props.height + 'px';
195 span.appendChild(render(arg));
196 }
197 }
198 document.body.removeChild(div);
199 div.style.visibility = 'visible';
200 return div;
203function render(elem) { return setProps(elem.props, makeElement(elem)); }
204function makeElement(e) {
205 var elem = e.element;
206 switch(elem.ctor) {
207 case 'Image': return image(e.props, elem);
208 case 'Flow': return flow(elem._0, elem._1);
209 case 'Container': return container(elem._0, elem._1);
210 case 'Spacer': return newElement('div');
211 case 'RawHtml': return rawHtml(elem);
212 case 'Custom': return elem.render(elem.model);
213 }
216function update(node, curr, next) {
217 if (node.tagName === 'A') { node = node.firstChild; }
218 if (curr.props.id === next.props.id) return updateProps(node, curr, next);
219 if (curr.element.ctor !== next.element.ctor) {
220 node.parentNode.replaceChild(render(next),node);
221 return true;
222 }
223 var nextE = next.element, currE = curr.element;
224 switch(nextE.ctor) {
225 case "Spacer": break;
226 case "RawHtml":
227 // only markdown blocks have guids, so this must be a text block
228 if (nextE.guid === null) {
229 if(currE.html.valueOf() !== nextE.html.valueOf()) {
230 node.innerHTML = nextE.html;
231 }
232 break;
233 }
234 if (nextE.guid !== currE.guid) {
235 node.parentNode.replaceChild(render(next),node);
236 return true;
237 }
238 var nargs = nextE.args;
239 var cargs = currE.args;
240 for (var i = nargs.length; i--; ) {
241 var narg = nargs[i];
242 var carg = cargs[i]
243 if (narg == carg) continue;
244 var span = document.getElementById('md-' + currE.guid + '-' + i);
245 if (narg.isElement) {
246 if (carg.isElement) {
247 update(span, carg, narg);
248 } else {
249 span.style.display = 'block';
250 var e = render(narg);
251 span.innerHTML = '';
252 span.appendChild(e);
253 }
254 } else {
255 span.style.display = 'inline';
256 span.innerHTML = narg;
257 }
258 }
259 break;
260 case "Image":
261 if (nextE._0.ctor === 'Plain') {
262 if (nextE._3 !== currE._3) node.src = nextE._3;
263 } else if (!eq(nextE,currE) ||
264 next.props.width !== curr.props.width ||
265 next.props.height !== curr.props.height) {
266 node.parentNode.replaceChild(render(next),node);
267 return true;
268 }
269 break;
270 case "Flow":
271 var arr = fromList(nextE._1);
272 for (var i = arr.length; i--; ) { arr[i] = arr[i].element.ctor; }
273 if (nextE._0.ctor !== currE._0.ctor) {
274 node.parentNode.replaceChild(render(next),node);
275 return true;
276 }
277 var nexts = fromList(nextE._1);
278 var kids = node.childNodes;
279 if (nexts.length !== kids.length) {
280 node.parentNode.replaceChild(render(next),node);
281 return true;
282 }
283 var currs = fromList(currE._1);
284 var goDir = function(x) { return x; };
285 switch(nextE._0.ctor) {
286 case "DDown": case "DUp": goDir = goDown; break;
287 case "DRight": case "DLeft": goDir = goRight; break;
288 case "DOut": case "DIn": goDir = goIn; break;
289 }
290 for (var i = kids.length; i-- ;) {
291 update(kids[i],currs[i],nexts[i]);
292 goDir(kids[i]);
293 }
294 break;
295 case "Container":
296 update(node.firstChild, currE._1, nextE._1);
297 setPos(nextE._0, nextE._1.props.width, nextE._1.props.height, node.firstChild);
298 break;
299 case "Custom":
300 if (currE.type === nextE.type) {
301 var done = nextE.update(node, currE.model, nextE.model);
302 if (done) return;
303 } else {
304 return node.parentNode.replaceChild(render(next), node);
305 }
306 }
307 updateProps(node, curr, next);
310function updateProps(node, curr, next) {
311 var props = next.props, currP = curr.props, e = node;
312 if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
313 if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
314 if (props.opacity !== 1 && props.opacity !== currP.opacity) {
315 e.style.opacity = props.opacity;
316 }
317 var nextColor = (props.color.ctor === 'Just' ?
318 extract(props.color._0) : '');
319 if (e.style.backgroundColor !== nextColor) {
320 e.style.backgroundColor = (nextColor === '' ? 'transparent' : nextColor);
321 }
322 if (props.tag !== currP.tag) { e.id = props.tag; }
323 if (props.href !== currP.href) {
324 if (currP.href === '') {
325 var a = newElement('a');
326 a.href = props.href;
327 a.style.width = '100%';
328 a.style.height = '100%';
329 a.style.top = 0;
330 a.style.left = 0;
331 a.style.display = 'block';
332 a.style.position = 'absolute';
333 e.style.position = 'relative';
334 e.appendChild(a);
335 } else {
336 node.lastNode.href = props.href;
337 }
338 }
340 // update hover handlers
341 if (props.hover.ctor !== '_Tuple0') {
342 e.elm_hover_handler = props.hover;
343 } else if (e.elm_hover_handler) {
344 e.elm_hover_handler = null;
345 }
347 // update click handlers
348 if (props.click.ctor !== '_Tuple0') {
349 e.elm_click_handler = props.click;
350 } else if (e.elm_click_handler) {
351 e.elm_click_handler = null;
352 }
355return { render:render, update:update };
357};