on b930ac2 help sign in
elk/runtime/Render/Element.js
264 lines 7.9 kB history raw
2ElmRuntime.Render.Element = function() {
3'use strict';
5var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
6var newElement = Utils.newElement, addTo = Utils.addTo, extract = Utils.extract,
7 addTransform = Utils.addTransform, removeTransform = Utils.removeTransform,
8 fromList = Utils.fromList;
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 addTo(a,e);
22 return a;
23 }
24 return e;
27function image(props, img) {
28 switch (img._0.ctor) {
29 case 'Plain': return plainImage(img._3);
30 case 'Fitted': return fittedImage(props.width, props.height, img._3);
31 case 'Cropped': return croppedImage(img,props.width,props.height,img._3);
32 }
35function plainImage(src) {
36 var img = newElement('img');
37 img.src = src;
38 img.name = src;
39 img.style.display = "block";
40 return img;
43function fittedImage(w, h, src) {
44 var e = newElement('div');
45 e.style.position = "relative";
46 e.style.overflow = "hidden";
48 var img = newElement('img');
49 img.onload = function() {
50 img.style.position = 'absolute';
51 img.style.margin = 'auto';
53 var sw = w, sh = h;
54 if (w / h > this.width / this.height) {
55 sh = Math.round(this.height * w / this.width);
56 } else {
57 sw = Math.round(this.width * h / this.height);
58 }
59 img.style.width = sw + 'px';
60 img.style.height = sh + 'px';
61 img.style.left = ((w - sw) / 2) + 'px';
62 img.style.top = ((h - sh) / 2) + 'px';
63 };
64 img.src = src;
65 img.name = src;
66 addTo(e,img);
67 return e;
70function croppedImage(elem, w, h, src) {
71 var pos = elem._0._0;
72 var e = newElement('div');
73 e.style.position = "relative";
74 e.style.overflow = "hidden";
76 var img = newElement('img');
77 img.onload = function() {
78 img.style.position = 'absolute';
79 img.style.margin = 'auto';
80 var sw = w / elem._1, sh = h / elem._2;
81 img.style.width = ((this.width * sw)|0) + 'px';
82 img.style.height = ((this.height * sh)|0) + 'px';
83 img.style.left = ((- pos._0 * sw)|0) + 'px';
84 img.style.top = ((- pos._1 * sh)|0) + 'px';
85 };
86 img.src = src;
87 img.name = src;
88 addTo(e,img);
89 return e;
92function goIn(e) { e.style.position = 'absolute'; return e; }
93function goDown(e) { return e }
94function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e; }
95function flowWith(f, array) {
96 var container = newElement('div');
97 for (var i = array.length; i--; ) {
98 addTo(container, f(render(array[i])));
99 }
100 return container;
103function flow(dir,elist) {
104 var array = fromList(elist);
105 switch(dir.ctor) {
106 case "DDown": array.reverse();
107 case "DUp": return flowWith(goDown,array);
108 case "DRight": array.reverse();
109 case "DLeft": return flowWith(goRight,array);
110 case "DOut": array.reverse();
111 case "DIn": return flowWith(goIn,array);
112 }
115function toPos(pos) {
116 switch(pos[0]) {
117 case "Absolute": return pos[1] + "px";
118 case "Relative": return (pos[1] * 100) + "%";
119 }
122function setPos(pos,w,h,e) {
123 e.style.position = 'absolute';
124 e.style.margin = 'auto';
125 var transform = '';
126 switch(pos.horizontal) {
127 case 'P': e.style.right = toPos(pos.x); break;
128 case 'Z': transform = 'translateX(' + ((-w/2)|0) + 'px) ';
129 case 'N': e.style.left = toPos(pos.x); break;
130 }
131 switch(pos.vertical) {
132 case 'N': e.style.bottom = toPos(pos.x); break;
133 case 'Z': transform += 'translateY(' + ((-h/2)|0) + 'px)';
134 case 'P': e.style.top = toPos(pos.x); break;
135 }
136 if (transform !== '') addTransform(e.style, transform);
137 return e;
140function container(pos,elem) {
141 var e = render(elem);
142 setPos(pos, elem.props.width, elem.props.height, e);
143 var div = newElement('div');
144 div.style.position = 'relative';
145 div.style.overflow = 'hidden';
146 addTo(div,e);
147 return div;
150function rawHtml(html) {
151 var e = newElement('div');
152 e.innerHTML = html;
153 return e;
156function render(elem) { return setProps(elem.props, makeElement(elem)); }
157function makeElement(e) {
158 var elem = e.element;
159 switch(elem.ctor) {
160 case 'Image': return image(e.props, elem);
161 case 'Flow': return flow(elem._0, elem._1);
162 case 'Container': return container(elem._0, elem._1);
163 case 'Spacer': return newElement('div');
164 case 'RawHtml': return rawHtml(elem._0);
165 case 'Custom': return elem.render(elem.model);
166 }
169function update(node, curr, next) {
170 console.log('overall update', curr.element.ctor, next.element.ctor);
171 if (node.tagName === 'A') { node = node.firstChild; }
172 if (curr.props.id === next.props.id) return false;
173 if (curr.element.ctor !== next.element.ctor) {
174 node.parentNode.replaceChild(render(next),node);
175 return true;
176 }
177 var nextE = next.element, currE = curr.element;
178 switch(nextE.ctor) {
179 case "Spacer": break;
180 case "RawHtml": if (nextE._0 !== currE._0) node.innerHTML = nextE._0; break;
181 case "Image":
182 if (nextE._0.ctor === 'Plain') {
183 if (nextE._3 !== currE._3) node.src = nextE._3;
184 } else if (!eq(nextE,currE) ||
185 next.props.width !== curr.props.width ||
186 next.props.height !== curr.props.height) {
187 node.parentNode.replaceChild(render(next),node);
188 return true;
189 }
190 break;
191 case "Flow":
192 console.log('flow update', nextE._0.ctor, currE._0.ctor);
193 if (nextE._0.ctor !== currE._0.ctor) {
194 node.parentNode.replaceChild(render(next),node);
195 return true;
196 }
197 var nexts = fromList(nextE._1);
198 var kids = node.childNodes;
199 console.log('flow length', nexts.length, kids.length);
200 if (nexts.length !== kids.length) {
201 node.parentNode.replaceChild(render(next),node);
202 return true;
203 }
204 var currs = fromList(currE._1);
205 var goDir = function(x) { return x; };
206 console.log(nextE);
207 switch(nextE._0.ctor) {
208 case "DDown": case "DUp": goDir = goDown; break;
209 case "DRight": case "DLeft": goDir = goRight; break;
210 case "DOut": case "DIn": goDir = goIn; break;
211 }
212 console.log(currs, nexts);
213 for (var i = kids.length; i-- ;) {
214 update(kids[i],currs[i],nexts[i]);
215 goDir(kids[i]);
216 }
217 break;
218 case "Container":
219 var inner = node.firstChild;
220 if (!update(inner, currE._1, nextE._1)) {
221 if (nextE._0.horizontal.ctor !== currE._0.horizontal.ctor) {
222 inner.style.left = inner.style.right = 'none';
223 removeTransform(inner.style);
224 }
225 if (nextE._0.vertical.ctor !== currE._0.vertical.ctor) {
226 inner.style.top = inner.style.bottom = 'none';
227 removeTransform(inner.style);
228 }
229 }
230 setPos(nextE._0, next.props.width, next.props.height, node.firstChild);
231 break;
232 case "Custom":
233 console.log(currE.type, nextE.type);
234 if (currE.type === nextE.type) {
235 nextE.update(node, currE.model, nextE.model);
236 } else {
237 return node.parentNode.replaceChild(render(next), node);
238 }
239 }
240 var props = next.props, currP = curr.props, e = node;
241 if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
242 if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
243 if (props.opacity !== 1 && props.opacity !== currP.opacity) {
244 e.style.opacity = props.opacity;
245 }
246 var nextColor = (props.color.ctor === 'Just' ?
247 extract(props.color._0) : 'transparent');
248 if (e.style.backgroundColor !== nextColor) e.style.backgroundColor = nextColor;
249 if (props.tag !== currP.tag) { e.id = props.tag; }
250 if (props.href !== currP.href) {
251 if (currP.href === '') {
252 var a = newElement('a');
253 a.href = props.href;
254 addTo(a,e);
255 e.parentNode.replaceChild(a,e);
256 } else {
257 node.parentNode.href = props.href;
258 }
259 }
262return { render:render, update:update };
264};