1window.initGridsnap = function({ 2 gridData = [], 3 maxX = null, 4 isFullScreenY = true, 5 pad = 10, 6 gridSizeY = 80, 7 isFillContainer, 8 sel = d3.select('.gridsnap'), 9 repositionFn = null, 10 serializedGrid = '', 11} = {}){ 12 var gridsnap = {gridData, serializeGrid, deserializeGrid} 13 14 gridData.forEach((d, i) => { 15 d.next = {...d.cur} 16 d.class = d.class === undefined ? i : d.class 17 }) 18 19 var maxX = maxX || d3.max(gridData, d => d.cur.x + d.cur.w) 20 function calcgridSizeX(){ 21 return (sel.node().offsetWidth)/maxX 22 } 23 var gridSizeX = calcgridSizeX() 24 25 // TODO: bubble events first 26 function calcGridSizeY() { 27 if (!isFillContainer) return gridSizeY 28 return sel.node().offsetHeight/(d3.max(gridData, d => d.cur.y + d.cur.h) || 1) 29 } 30 31 gridSizeY = calcGridSizeY() 32 33 var resizeKey = 'resize.gridsnap' + serializedGrid 34 d3.select(window).on(resizeKey, util.throttle(() => { 35 var newX = calcgridSizeX() 36 var newY = calcGridSizeY() 37 if (newX == gridSizeX && newY == gridSizeY) return 38 39 gridSizeX = newX 40 gridSizeY = newY 41 renderPositions() 42 43 44 gridItemSel.each(d => d.resizeFn?.()) 45 }, 500)) 46 47 var gridsnapSel = sel.html('').append('div.gridsnap-container') 48 49 var gridItemSel = gridsnapSel.appendMany('div.grid-item', gridData) 50 .each(function(d){ d.sel = d3.select(this).append('div.grid-contents').classed(d.class, 1) }) 51 52 gridItemSel.append('div.move-handle') 53 .text('â£') 54 .call(makeDragFn(false)) 55 gridItemSel.append('div.resize-handle') 56 .text('â') 57 .call(makeDragFn(true)) 58 59 var previewSel = gridsnapSel.append('div.preview.grid-item') 60 61 function makeDragFn(isResize) { 62 return d3.drag() 63 .subject((ev, d) => ({ 64 x: (d.cur.x + (isResize ? d.cur.w : 0))*gridSizeX, 65 y: (d.cur.y + (isResize ? d.cur.h : 0))*gridSizeY 66 })) 67 .container(function(){ return this.parentNode.parentNode }) 68 .on('start', function(ev, d){ 69 gridData.forEach(d => d.next = {...d.cur}) 70 d.dragStart = {...d.cur} 71 72 gridsnapSel.classed('dragging', 1) 73 d3.select(this.parentNode).classed('dragging', 1) 74 previewSel.st({'display': ''}) 75 }) 76 .on('end', (ev, d) => { 77 gridData.forEach(d => d.cur = {...d.next}) 78 79 gridsnapSel.classed('dragging', 0) 80 gridItemSel.classed('dragging', 0) 81 previewSel.st({'display': 'none'}) 82 renderPositions() 83 84 if (isResize) d.resizeFn?.() 85 }) 86 .on('drag', isResize ? resize : drag) 87 88 function drag(ev, d) { 89 d.cur.x = ev.x/gridSizeX 90 d.cur.y = ev.y/gridSizeY 91 92 pushGrid(d) 93 renderPositions(d) 94 } 95 96 function resize(ev, d) { 97 d.cur.x = d.dragStart.x 98 d.cur.y = d.dragStart.y 99 100 d.cur.w = ev.x/gridSizeX - d.dragStart.x 101 d.cur.h = ev.y/gridSizeY - d.dragStart.y 102 if (d.cur.w < 0) { 103 d.cur.x += d.cur.w 104 d.cur.w = d.dragStart.w - d.cur.w 105 } 106 if (d.cur.h < 0) { 107 d.cur.y += d.cur.h 108 d.cur.h = d.dragStart.h - d.cur.h 109 } 110 111 pushGrid(d) 112 renderPositions(d) 113 } 114 } 115 116 117 function pushGrid(active) { 118 if (active) active.next = snapToGrid(active.cur) 119 120 var sortedGridData = d3.sort(gridData, d => d != active) 121 sortedGridData = d3.sort(sortedGridData, d => d == active ? d.cur.y : d.next.y) 122 123 var topArray = d3.range(maxX).map(d => 0) 124 sortedGridData.forEach(d => { 125 var {x, y, w, h} = d.next 126 d.next.y = d3.max(d3.range(w), i => topArray[x + i]) 127 d3.range(w).forEach(i => topArray[x + i] = d.next.y + h) 128 }) 129 130 function snapToGrid({x, y, w, h}) { 131 var rv = {x: Math.max(0, Math.round(x)), y: Math.max(0, Math.round(y)), w: Math.max(1, Math.round(w)), h: Math.max(1, Math.round(h))} 132 if (rv.x + rv.w > maxX) rv.x = Math.max(0, maxX - rv.w) 133 return rv 134 } 135 } 136 137 function renderPositions(active){ 138 gridItemSel.call(renderGridItem, 'next') 139 140 if (active){ 141 gridItemSel.filter(d => d == active).call(renderGridItem, 'cur') 142 previewSel.datum(active).call(renderGridItem, 'next') 143 } else{ 144 if (!isFillContainer){ 145 var maxY = Math.max(maxY, d3.max(gridData, d => d.next.y + d.next.h)) 146 gridsnapSel.st({height: Math.max(isFullScreenY ? window.innerHeight : 0, maxY*gridSizeY + pad
146) + 'px'}) 147 } 148 } 149 150 repositionFn?.(gridsnap) 151 152 function renderGridItem(itemSel, key) { 153 itemSel 154 .translate(d => [d[key].x*gridSizeX + pad/2, d[key].y*gridSizeY + pad/2].map(Math.round)) 155 .st({ 156 width: d => Math.round(Math.max(0, d[key].w*gridSizeX - pad)), // negative sizes bug out 157 height: d => Math.round(Math.max(0, d[key].h*gridSizeY - pad)) 158 }) 159 } 160 } 161 162 function serializeGrid(){ 163 return gridData.map(d => { 164 var {x, y, w, h} = d.cur 165 return `${d.class}${[x, y, w, h].map(d3.format('02d')).join('')}` 166 }).join('_') 167 } 168 169 function deserializeGrid(serializedGrid){ 170 serializedGrid?.split('_').forEach(str => { 171 var match = str.match(/^([\w-]+)(\d{8})$/) 172 if (!match) return 173 var [_, className, coords] = match 174 175 var [x, y, w, h] = d3.range(4).map(i => +coords.substr(i*2, 2)) 176 var gridItem = gridData.find(d => d.class == className) 177 if (gridItem) gridItem.next = {x, y, w, h} 178 }) 179 180 pushGrid() 181 gridData.forEach(d => d.cur = {...d.next}) 182 renderPositions() 183 } 184 185 deserializeGrid(serializedGrid) 186 187 return gridsnap 188} 189 190window.init?.()
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.