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 |