on e4e8d89 help sign in
elk/runtime/Render/Element.js
272 lines 8.8 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 e.appendChild(a);
27 }
28 if (props.hover.ctor !== '_Tuple0') {
29 var overCount = 0;
30 e.addEventListener('mouseover', function() {
31 if (overCount++ > 0) return;
32 props.hover(true);
33 });
34 e.addEventListener('mouseout', function(evt) {
35 if (e.contains(evt.toElement || evt.relatedTarget)) return;
36 overCount = 0;
37 props.hover(false);
38 });
39 }
40 return e;
43function image(props, img) {
44 switch (img._0.ctor) {
45 case 'Plain': return plainImage(img._3);
46 case 'Fitted': return fittedImage(props.width, props.height, img._3);
47 case 'Cropped': return croppedImage(img,props.width,props.height,img._3);
48 case 'Tiled': return tiledImage(img._3);
49 }
52function plainImage(src) {
53 var img = newElement('img');
54 img.src = src;
55 img.name = src;
56 img.style.display = "block";
57 return img;
60function tiledImage(src) {
61 var div = newElement('div');
62 div.style.backgroundImage = 'url(' + src + ')';
63 return div;
66function fittedImage(w, h, src) {
67 var div = newElement('div');
68 div.style.background = 'url(' + src + ') no-repeat center';
69 div.style.webkitBackgroundSize = 'cover';
70 div.style.MozBackgroundSize = 'cover';
71 div.style.OBackgroundSize = 'cover';
72 div.style.backgroundSize = 'cover';
73 return div;
76function croppedImage(elem, w, h, src) {
77 var pos = elem._0._0;
78 var e = newElement('div');
79 e.style.overflow = "hidden";
81 var img = newElement('img');
82 img.onload = function() {
83 var sw = w / elem._1, sh = h / elem._2;
84 img.style.width = ((this.width * sw)|0) + 'px';
85 img.style.height = ((this.height * sh)|0) + 'px';
86 img.style.marginLeft = ((- pos._0 * sw)|0) + 'px';
87 img.style.marginTop = ((- pos._1 * sh)|0) + 'px';
88 };
89 img.src = src;
90 img.name = src;
91 e.appendChild(img);
92 return e;
95function goIn(e) { e.style.position = 'absolute'; return e; }
96function goDown(e) { return e }
97function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e; }
98function flowWith(f, array) {
99 var container = newElement('div');
100 for (var i = array.length; i--; ) {
101 container.appendChild(f(render(array[i])));
102 }
103 return container;
106function flow(dir,elist) {
107 var array = fromList(elist);
108 switch(dir.ctor) {
109 case "DDown": array.reverse();
110 case "DUp": return flowWith(goDown,array);
111 case "DRight": array.reverse();
112 case "DLeft": return flowWith(goRight,array);
113 case "DOut": array.reverse();
114 case "DIn": return flowWith(goIn,array);
115 }
118function toPos(pos) {
119 switch(pos.ctor) {
120 case "Absolute": return pos._0 + "px";
121 case "Relative": return (pos._0 * 100) + "%";
122 }
125function setPos(pos,w,h,e) {
126 e.style.position = 'absolute';
127 e.style.margin = 'auto';
128 var transform = '';
129 switch(pos.horizontal.ctor) {
130 case 'P': e.style.right = toPos(pos.x); break;
131 case 'Z': transform = 'translateX(' + ((-w/2)|0) + 'px) ';
132 case 'N': e.style.left = toPos(pos.x); break;
133 }
134 switch(pos.vertical.ctor) {
135 case 'N': e.style.bottom = toPos(pos.y); break;
136 case 'Z': transform += 'translateY(' + ((-h/2)|0) + 'px)';
137 case 'P': e.style.top = toPos(pos.y); break;
138 }
139 if (transform !== '') addTransform(e.style, transform);
140 return e;
143function container(pos,elem) {
144 var e = render(elem);
145 setPos(pos, elem.props.width, elem.props.height, e);
146 var div = newElement('div');
147 div.style.position = 'relative';
148 div.style.overflow = 'hidden';
149 div.appendChild(e);
150 return div;
153function rawHtml(html) {
154 var e = newElement('div');
155 e.innerHTML = html;
156 return e;
159function render(elem) { return setProps(elem.props, makeElement(elem)); }
160function makeElement(e) {
161 var elem = e.element;
162 switch(elem.ctor) {
163 case 'Image': return image(e.props, elem);
164 case 'Flow': return flow(elem._0, elem._1);
165 case 'Container': return container(elem._0, elem._1);
166 case 'Spacer': return newElement('div');
167 case 'RawHtml': return rawHtml(elem._0);
168 case 'Custom': return elem.render(elem.model);
169 }
172function update(node, curr, next) {
173 if (node.tagName === 'A') { node = node.firstChild; }
174 if (curr.props.id === next.props.id) return updateProps(node, curr, next);
175 if (curr.element.ctor !== next.element.ctor) {
176 node.parentNode.replaceChild(render(next),node);
177 return true;
178 }
179 var nextE = next.element, currE = curr.element;
180 switch(nextE.ctor) {
181 case "Spacer": break;
182 case "RawHtml":
183 if (nextE._0 !== currE._0) node.innerHTML = nextE._0;
184 break;
185 case "Image":
186 if (nextE._0.ctor === 'Plain') {
187 if (nextE._3 !== currE._3) node.src = nextE._3;
188 } else if (!eq(nextE,currE) ||
189 next.props.width !== curr.props.width ||
190 next.props.height !== curr.props.height) {
191 node.parentNode.replaceChild(render(next),node);
192 return true;
193 }
194 break;
195 case "Flow":
196 var arr = fromList(nextE._1);
197 for (var i = arr.length; i--; ) { arr[i] = arr[i].element.ctor; }
198 if (nextE._0.ctor !== currE._0.ctor) {
199 node.parentNode.replaceChild(render(next),node);
200 return true;
201 }
202 var nexts = fromList(nextE._1);
203 var kids = node.childNodes;
204 if (nexts.length !== kids.length) {
205 node.parentNode.replaceChild(render(next),node);
206 return true;
207 }
208 var currs = fromList(currE._1);
209 var goDir = function(x) { return x; };
210 switch(nextE._0.ctor) {
211 case "DDown": case "DUp": goDir = goDown; break;
212 case "DRight": case "DLeft": goDir = goRight; break;
213 case "DOut": case "DIn": goDir = goIn; break;
214 }
215 for (var i = kids.length; i-- ;) {
216 update(kids[i],currs[i],nexts[i]);
217 goDir(kids[i]);
218 }
219 break;
220 case "Container":
221 var inner = node.firstChild;
222 if (!update(inner, currE._1, nextE._1)) {
223 if (nextE._0.horizontal.ctor !== currE._0.horizontal.ctor) {
224 inner.style.left = inner.style.right = 'none';
225 removeTransform(inner.style);
226 }
227 if (nextE._0.vertical.ctor !== currE._0.vertical.ctor) {
228 inner.style.top = inner.style.bottom = 'none';
229 removeTransform(inner.style);
230 }
231 }
232 setPos(nextE._0, nextE._1.props.width, nextE._1.props.height, inner);
233 break;
234 case "Custom":
235 if (currE.type === nextE.type) {
236 var done = nextE.update(node, currE.model, nextE.model);
237 if (done) return;
238 } else {
239 return node.parentNode.replaceChild(render(next), node);
240 }
241 }
242 updateProps(node, curr, next);
245function updateProps(node, curr, next) {
246 var props = next.props, currP = curr.props, e = node;
247 if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
248 if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
249 if (props.opacity !== 1 && props.opacity !== currP.opacity) {
250 e.style.opacity = props.opacity;
251 }
252 var nextColor = (props.color.ctor === 'Just' ?
253 extract(props.color._0) : 'transparent');
254 if (e.style.backgroundColor !== nextColor) {
255 e.style.backgroundColor = nextColor;
256 }
257 if (props.tag !== currP.tag) { e.id = props.tag; }
258 if (props.href !== currP.href) {
259 if (currP.href === '') {
260 var a = newElement('a');
261 a.href = props.href;
262 a.appendChild(e);
263 e.parentNode.replaceChild(a,e);
264 } else {
265 node.parentNode.href = props.href;
266 }
267 }
270return { render:render, update:update };
272};