OLD | NEW |
| (Empty) |
1 <!-- | |
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | |
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | |
6 Code distributed by Google as part of the polymer project is also | |
7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | |
8 --> | |
9 | |
10 | |
11 <!-- | |
12 @group Polymer Core Elements | |
13 | |
14 @element core-layout-grid | |
15 @status beta | |
16 @homepage github.io | |
17 | |
18 TODO | |
19 --> | |
20 <link rel="import" href="../polymer/polymer.html"> | |
21 | |
22 <polymer-element name="core-layout-grid" attributes="nodes layout auto"> | |
23 <script> | |
24 (function() { | |
25 | |
26 Polymer('core-layout-grid', { | |
27 | |
28 nodes: null, | |
29 layout: null, | |
30 auto: false, | |
31 | |
32 created: function() { | |
33 this.layout = []; | |
34 }, | |
35 | |
36 nodesChanged: function() { | |
37 this.invalidate(); | |
38 }, | |
39 | |
40 layoutChanged: function() { | |
41 this.invalidate(); | |
42 }, | |
43 | |
44 autoNodes: function() { | |
45 this.nodes = this.parentNode.children.array().filter( | |
46 function(node) { | |
47 switch(node.localName) { | |
48 case 'core-layout-grid': | |
49 case 'style': | |
50 return false; | |
51 } | |
52 return true; | |
53 } | |
54 ); | |
55 }, | |
56 | |
57 invalidate: function() { | |
58 if (this.layout && this.layout.length) { | |
59 // job debounces layout, only letting it occur every N ms | |
60 this.layoutJob = this.job(this.layoutJob, this.relayout); | |
61 } | |
62 }, | |
63 | |
64 relayout: function() { | |
65 if (!this.nodes || this.auto) { | |
66 this.autoNodes(); | |
67 } | |
68 layout(this.layout, this.nodes); | |
69 this.asyncFire('core-layout'); | |
70 } | |
71 | |
72 }); | |
73 | |
74 // | |
75 | |
76 var lineParent; | |
77 | |
78 function line(axis, p, d) { | |
79 var l = document.createElement('line'); | |
80 var extent = (axis === 'left' ? 'width' : | |
81 (axis === 'top' ? 'height' : axis)); | |
82 l.setAttribute('extent', extent); | |
83 if (d < 0) { | |
84 axis = (axis === 'left' ? 'right' : | |
85 (axis === 'top' ? 'bottom' : axis)); | |
86 } | |
87 p = Math.abs(p); | |
88 l.style[axis] = p + 'px'; | |
89 l.style[extent] = '0px'; | |
90 lineParent.appendChild(l); | |
91 } | |
92 | |
93 var colCount, colOwners, rowCount, rowOwners; | |
94 | |
95 function matrixillate(matrix) { | |
96 // mesaure the matrix, must be rectangular | |
97 rowCount = matrix.length; | |
98 colCount = rowCount && matrix[0].length || 0; | |
99 // transpose matrix | |
100 var transpose = []; | |
101 for (var i=0; i<colCount; i++) { | |
102 var c = []; | |
103 for (var j=0; j<rowCount; j++) { | |
104 c.push(matrix[j][i]); | |
105 } | |
106 transpose.push(c); | |
107 } | |
108 // assign sizing control | |
109 colOwners = findOwners(matrix); | |
110 rowOwners = findOwners(transpose); | |
111 //console.log('colOwners', colOwners); | |
112 //console.log('rowOwners', rowOwners); | |
113 } | |
114 | |
115 function findOwners(matrix) { | |
116 var majCount = matrix.length; | |
117 var minCount = majCount && matrix[0].length || 0; | |
118 var owners = []; | |
119 // for each column (e.g.) | |
120 for (var i=0; i<minCount; i++) { | |
121 // array of contained areas | |
122 var contained = {}; | |
123 // look at each row to find a containing area | |
124 for (var j=0; j<majCount; j++) { | |
125 // get the row vector | |
126 var vector = matrix[j] | |
127 // node index at [i,j] | |
128 var nodei = vector[i]; | |
129 // if a node is there | |
130 if (nodei) { | |
131 // determine if it bounds this column | |
132 var owns = false; | |
133 if (i === 0) { | |
134 owns = (i === minCount-1) || (nodei !== vector[i+1]); | |
135 } else if (i === minCount - 1) { | |
136 owns = (i === 0) || (nodei !== vector[i-1]); | |
137 } else { | |
138 owns = nodei !== vector[i-1] && nodei !== vector[i+1]; | |
139 } | |
140 if (owns) { | |
141 contained[nodei] = 1; | |
142 } | |
143 } | |
144 // store the owners for this column | |
145 owners[i] = contained; | |
146 } | |
147 } | |
148 return owners; | |
149 } | |
150 | |
151 var nodes; | |
152 | |
153 function colWidth(i) { | |
154 for (var col in colOwners[i]) { | |
155 col = Number(col); | |
156 if (col === 0) { | |
157 return 96; | |
158 } | |
159 var node = nodes[col - 1]; | |
160 if (node.hasAttribute('h-flex') || node.hasAttribute('flex')) { | |
161 return -1; | |
162 } | |
163 var w = node.offsetWidth; | |
164 //console.log('colWidth(' + i + ') ==', w); | |
165 return w; | |
166 } | |
167 return -1; | |
168 } | |
169 | |
170 function rowHeight(i) { | |
171 for (var row in rowOwners[i]) { | |
172 row = Number(row); | |
173 if (row === 0) { | |
174 return 96; | |
175 } | |
176 var node = nodes[row - 1]; | |
177 if (node.hasAttribute('v-flex') || node.hasAttribute('flex')) { | |
178 return -1; | |
179 } | |
180 var h = node.offsetHeight; | |
181 //console.log('rowHeight(' + i + ') ==', h); | |
182 return h; | |
183 } | |
184 return -1; | |
185 } | |
186 | |
187 var m = 0; | |
188 | |
189 function railize(count, sizeFn) { | |
190 // | |
191 // create rails for `count` tracks using | |
192 // sizing function `sizeFn(trackNo)` | |
193 // | |
194 // for n tracks there are (n+1) rails | |
195 // | |
196 // |track|track|track| | |
197 // 0|->sz0|->sz1|<-sz2|0 | |
198 // | |
199 // |track|track|track| | |
200 // 0|->sz0| |<-sz2|0 | |
201 // | |
202 // there can be one elastic track per set | |
203 // | |
204 // |track|track|track|track| | |
205 // 0|-->s0|-->s1|<--s1|<--s2|0 | |
206 // | |
207 // sz1 spans multiple tracks which makes | |
208 // it elastic (it's underconstrained) | |
209 // | |
210 var rails = []; | |
211 var a = 0; | |
212 for (var i=0, x; i<count; i++) { | |
213 rails[i] = {p: a, s: 1}; | |
214 x = sizeFn(i) + m + m; | |
215 if (x == -1) { | |
216 break; | |
217 } | |
218 a += x; | |
219 } | |
220 if (i === count) { | |
221 rails[i] = {p: 0, s: -1}; | |
222 } | |
223 var b = 0; | |
224 for (var ii=count, x; ii>i; ii--) { | |
225 rails[ii] = {p: b, s: -1}; | |
226 x = sizeFn(ii - 1) + m + m; | |
227 if (x !== -1) { | |
228 b += x; | |
229 } | |
230 } | |
231 return rails; | |
232 } | |
233 | |
234 // TODO(sjmiles): this code tries to preserve actual position, | |
235 // so 'unposition' is really 'naturalize' or something | |
236 function unposition(box) { | |
237 var style = box.style; | |
238 //style.right = style.bottom = style.width = style.height = ''; | |
239 style.position = 'absolute'; | |
240 style.display = 'inline-block'; | |
241 style.boxSizing = style.mozBoxSizing = 'border-box'; | |
242 } | |
243 | |
244 function _position(style, maj, min, ext, a, b) { | |
245 style[maj] = style[min] = ''; | |
246 style[ext] = 'auto'; | |
247 if (a.s < 0 && b.s < 0) { | |
248 var siz = a.p - b.p - m - m; | |
249 style[ext] = siz + 'px'; | |
250 var c = 'calc(100% - ' + (b.p + siz + m) + 'px' + ')'; | |
251 style[maj] = '-webkit-' + c; | |
252 style[maj] = c; | |
253 } else if (b.s < 0) { | |
254 style[maj] = a.p + m + 'px'; | |
255 style[min] = b.p + m + 'px'; | |
256 } else { | |
257 style[maj] = a.p + m + 'px'; | |
258 style[ext] = b.p - a.p - m - m + 'px'; | |
259 } | |
260 } | |
261 | |
262 function position(elt, left, right, top, bottom) { | |
263 _position(elt.style, 'top', 'bottom', 'height', rows[top], | |
264 rows[bottom]); | |
265 _position(elt.style, 'left', 'right', 'width', columns[left], | |
266 columns[right]); | |
267 } | |
268 | |
269 function layout(matrix, anodes, alineParent) { | |
270 //console.group('layout'); | |
271 | |
272 lineParent = alineParent; | |
273 nodes = anodes; | |
274 matrixillate(matrix); | |
275 | |
276 nodes.forEach(unposition); | |
277 | |
278 columns = railize(colCount, colWidth); | |
279 rows = railize(rowCount, rowHeight); | |
280 | |
281 if (alineParent) { | |
282 //console.group('column rails'); | |
283 columns.forEach(function(c) { | |
284 //console.log(c.p, c.s); | |
285 line('left', c.p, c.s); | |
286 }); | |
287 //console.groupEnd(); | |
288 | |
289 //console.group('row rails'); | |
290 rows.forEach(function(r) { | |
291 //console.log(r.p, r.s); | |
292 line('top', r.p, r.s); | |
293 }); | |
294 //console.groupEnd(); | |
295 } | |
296 | |
297 //console.group('rail boundaries'); | |
298 nodes.forEach(function(node, i) { | |
299 // node indices are 1-based | |
300 var n = i + 1; | |
301 // boundary rails | |
302 var l, r, t = 1e10, b = -1e10; | |
303 matrix.forEach(function(vector, i) { | |
304 var f = vector.indexOf(n); | |
305 if (f > -1) { | |
306 l = f; | |
307 r = vector.lastIndexOf(n) + 1; | |
308 t = Math.min(t, i); | |
309 b = Math.max(b, i) + 1; | |
310 } | |
311 }); | |
312 if (l == undefined) { | |
313 //console.log('unused'); | |
314 node.style.position = 'absolute'; | |
315 var offscreen = node.getAttribute('offscreen'); | |
316 switch (offscreen) { | |
317 case 'basement': | |
318 node.style.zIndex = 0; | |
319 break; | |
320 case 'left': | |
321 case 'top': | |
322 node.style[offscreen] = node.offsetWidth * -2 + 'px'; | |
323 break; | |
324 case 'right': | |
325 node.style.left = node.offsetParent.offsetWidth | |
326 + node.offsetWidth + 'px'; | |
327 break; | |
328 case 'bottom': | |
329 node.style.top = node.parentNode.offsetHeight | |
330 + node.offsetHeight + 'px'; | |
331 break; | |
332 default: | |
333 node.style[Math.random() >= 0.5 ? 'left' : 'top'] = '-110%'; | |
334 } | |
335 //node.style.opacity = 0; | |
336 node.style.pointerEvents = 'none'; | |
337 } else { | |
338 node.style.pointerEvents = ''; | |
339 //node.style.opacity = ''; | |
340 //console.log(l, r, t, b); | |
341 position(node, l, r, t, b); | |
342 } | |
343 }); | |
344 //console.groupEnd(); | |
345 //console.groupEnd(); | |
346 } | |
347 | |
348 })(); | |
349 </script> | |
350 </polymer-element> | |
OLD | NEW |