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