on 0050bfa help sign in
elk/runtime/Render/Element.js
289 lines 8.2 kB history raw
2ElmRuntime.Render.Element = function() {
3'use strict';
5var fromList = Elm.JavaScript({}).fromList;
6var eq = Elm.Native.Utils({}).eq;
8function newElement(elementType) {
9 var e = document.createElement(elementType);
10 e.style.padding = "0";
11 e.style.margin = "0";
12 return e;
15function addTo(container, elem) {
16 container.appendChild(elem);
19function extract(c) {
20 if (c._3 === 1) { return 'rgb(' + c._0 + ',' + c._1 + ',' + c._2 + ')'; }
21 return 'rgba(' + c._0 + ',' + c._1 + ',' + c._2 + ',' + c._3 + ')';
24function setProps(props, e) {
25 e.style.width = (props.width |0) + 'px';
26 e.style.height = (props.height|0) + 'px';
27 if (props.opacity !== 1) { e.style.opacity = props.opacity; }
28 if (props.color.ctor === 'Just') {
29 e.style.backgroundColor = extract(props.color._0);
30 }
31 if (props.tag !== '') { e.id = props.tag; }
32 if (props.href !== '') {
33 var a = newElement('a');
34 a.href = props.href;
35 addTo(a,e);
36 return a;
37 }
38 return e;
41function image(props, img) {
42 switch (img._0.ctor) {
43 case 'Plain': return plainImage(img._3);
44 case 'Fitted': return fittedImage(props.width, props.height, img._3);
45 case 'Cropped': return croppedImage(img,props.width,props.height,img._3);
46 }
49function plainImage(src) {
50 var img = newElement('img');
51 img.src = src;
52 img.name = src;
53 img.style.display = "block";
54 return img;
57function fittedImage(w, h, src) {
58 var e = newElement('div');
59 e.style.position = "relative";
60 e.style.overflow = "hidden";
62 var img = newElement('img');
63 img.onload = function() {
64 img.style.position = 'absolute';
65 img.style.margin = 'auto';
67 var sw = w, sh = h;
68 if (w / h > this.width / this.height) {
69 sh = Math.round(this.height * w / this.width);
70 } else {
71 sw = Math.round(this.width * h / this.height);
72 }
73 img.style.width = sw + 'px';
74 img.style.height = sh + 'px';
75 img.style.left = ((w - sw) / 2) + 'px';
76 img.style.top = ((h - sh) / 2) + 'px';
77 };
78 img.src = src;
79 img.name = src;
80 addTo(e,img);
81 return e;
84function croppedImage(elem, w, h, src) {
85 var pos = elem._0._0;
86 var e = newElement('div');
87 e.style.position = "relative";
88 e.style.overflow = "hidden";
90 var img = newElement('img');
91 img.onload = function() {
92 img.style.position = 'absolute';
93 img.style.margin = 'auto';
94 var sw = w / elem._1, sh = h / elem._2;
95 img.style.width = ((this.width * sw)|0) + 'px';
96 img.style.height = ((this.height * sh)|0) + 'px';
97 img.style.left = ((- pos._0 * sw)|0) + 'px';
98 img.style.top = ((- pos._1 * sh)|0) + 'px';
99 };
100 img.src = src;
101 img.name = src;
102 addTo(e,img);
103 return e;
106function goIn(e) { e.style.position = 'absolute'; return e; }
107function goDown(e) { return e }
108function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e; }
109function flowWith(f, array) {
110 var container = newElement('div');
111 for (var i = array.length; i--; ) {
112 addTo(container, f(render(array[i])));
113 }
114 return container;
117function flow(dir,elist) {
118 var array = fromList(elist);
119 switch(dir.ctor) {
120 case "DDown": array.reverse();
121 case "DUp": return flowWith(goDown,array);
122 case "DRight": array.reverse();
123 case "DLeft": return flowWith(goRight,array);
124 case "DOut": array.reverse();
125 case "DIn": return flowWith(goIn,array);
126 }
129function toPos(pos) {
130 switch(pos[0]) {
131 case "Absolute": return pos[1] + "px";
132 case "Relative": return (pos[1] * 100) + "%";
133 }
136function addTransform(style, trans) {
137 style.transform = trans;
138 style.msTransform = trans;
139 style.MozTransform = trans;
140 style.webkitTransform = trans;
141 style.OTransform = trans;
144function removeTransform(style) {
145 style.transform = 'none';
146 style.msTransform = 'none';
147 style.MozTransform = 'none';
148 style.webkitTransform = 'none';
149 style.OTransform = 'none';
152function setPos(pos,w,h,e) {
153 e.style.position = 'absolute';
154 e.style.margin = 'auto';
155 var transform = '';
156 switch(pos.horizontal) {
157 case 'P': e.style.right = toPos(pos.x); break;
158 case 'Z': transform = 'translateX(' + ((-w/2)|0) + 'px) ';
159 case 'N': e.style.left = toPos(pos.x); break;
160 }
161 switch(pos.vertical) {
162 case 'N': e.style.bottom = toPos(pos.x); break;
163 case 'Z': transform += 'translateY(' + ((-h/2)|0) + 'px)';
164 case 'P': e.style.top = toPos(pos.x); break;
165 }
166 if (transform !== '') addTransform(e.style, transform);
167 return e;
170function container(pos,elem) {
171 var e = render(elem);
172 setPos(pos, elem.props.width, elem.props.height, e);
173 var div = newElement('div');
174 div.style.position = 'relative';
175 div.style.overflow = 'hidden';
176 addTo(div,e);
177 return div;
180function rawHtml(html) {
181 var e = newElement('div');
182 e.innerHTML = html;
183 return e;
186function render(elem) { return setProps(elem.props, makeElement(elem)); }
187function makeElement(e) {
188 switch(e.element.ctor) {
189 case 'Image': return image(e.props, e.element);
190 case 'Flow': return flow(e.element._0, e.element._1);
191 case 'Container': return container(e.element._0, e.element._1);
192 case 'Spacer': return newElement('div');
193 case 'RawHtml': return rawHtml(e.element._0);
194 case 'DomNode': return e.element._0;
195 }
198function update(node, curr, next) {
199 if (node.tagName === 'A') { node = node.firstChild; }
200 if (curr.props.id === next.props.id) return false;
201 if (curr.element.ctor !== next.element.ctor) {
202 node.parentNode.replaceChild(render(next),node);
203 return true;
204 }
205 var nextE = next.element, currE = curr.element;
206 switch(nextE.ctor) {
207 case "Spacer": break;
208 case "RawHtml": if (nextE._0 !== currE._0) node.innerHTML = nextE._0; break;
209 case "Image":
210 if (nextE._0.ctor === 'Plain') {
211 if (nextE._3 !== currE._3) node.src = nextE._3;
212 } else if (!eq(nextE,currE) ||
213 next.props.width !== curr.props.width ||
214 next.props.height !== curr.props.height) {
215 node.parentNode.replaceChild(render(next),node);
216 return true;
217 }
218 break;
219 case "Flow":
220 if (nextE._0.ctor !== currE._0.ctor) {
221 node.parentNode.replaceChild(render(next),node);
222 return true;
223 }
224 var nexts = nextE._1;
225 var kids = node.childNodes;
226 if (nexts.length !== kids.length) {
227 node.parentNode.replaceChild(render(next),node);
228 return true;
229 }
230 var currs = currE._1;
231 var goDir = function(x) { return x; };
232 switch(nextE[1][0]) {
233 case "DDown": case "DUp": goDir = goDown; break;
234 case "DRight": case "DLeft": goDir = goRight; break;
235 case "DOut": case "DIn": goDir = goIn; break;
236 }
237 for (var i = kids.length; i-- ;) {
238 update(kids[i],currs[i],nexts[i]);
239 goDir(kids[i]);
240 }
241 break;
242 case "Container":
243 var inner = node.firstChild;
244 if (!update(inner, currE._1, nextE._1)) {
245 if (nextE._0.horizontal.ctor !== currE._0.horizontal.ctor) {
246 inner.style.left = inner.style.right = 'none';
247 removeTransform(inner.style);
248 }
249 if (nextE._0.vertical.ctor !== currE._0.vertical.ctor) {
250 inner.style.top = inner.style.bottom = 'none';
251 removeTransform(inner.style);
252 }
253 }
254 setPos(nextE._0, next.props.width, next.props.height, node.firstChild);
255 break;
256 case "DomNode":
257 if (next._0 !== curr._0) node.parentNode.replaceChild(render(next),node);
258 break;
259 }
260 var props = next.props, currP = curr.props, e = node;
261 if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
262 if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
263 if (props.opacity !== 1 && props.opacity !== currP.opacity) {
264 e.style.opacity = props.opacity;
265 }
266 var nextColor = (props.color.ctor === 'Just' ?
267 extract(props.color._0) : 'transparent');
268 if (e.style.backgroundColor !== nextColor) e.style.backgroundColor = nextColor;
269 if (props.tag !== currP.tag) { e.id = props.tag; }
270 if (props.href !== currP.href) {
271 if (currP.href === '') {
272 var a = newElement('a');
273 a.href = props.href;
274 addTo(a,e);
275 e.parentNode.replaceChild(a,e);
276 } else {
277 node.parentNode.href = props.href;
278 }
279 }
282return {
283 render:render,
284 update:update,
285 addTo:addTo,
286 newElement:newElement
287};
289}();