1window.initCg = async function (sel, slug, {clickedId, clickedIdCb, isModal, isGridsnap, pruningThreshold, maxEmbedNodes} = {}){ 2 var data = await util.getFile(`./graph_data/${slug}.json`) 3 4 5 var visState = { 6 pinnedIds: [], 7 hiddenIds: [], 8 hoveredId: null, 9 hoveredNodeId: null, 10 hoveredCtxIdx: null, 11 clickedId: null, 12 clickedCtxIdx: null, 13 linkType: 'either', 14 isShowAllLinks: '', 15 isSyncEnabled: '', 16 subgraph: null, 17 isEditMode: 1, 18 isHideLayer: data.metadata.scan == util.scanSlugToName.h35 || data.metadata.scan == util.scanSlugToName.moc, 19 graphSchemaVersion: data.metadata?.schema_version || 0, 20 sg_pos: '', 21 isModal: true, 22 isGridsnap, 23 slug: slug, 24 // graph JSON qParams override visState defaults (only annotation-related params, not view settings) 25 ...(data.qParams ? Object.fromEntries(Object.entries(data.qParams).filter(([k, v]) => v !== null && ['pinnedIds', 'supernodes', 'clerps', 'clickedId', 'sg_pos', 'linkType'].includes(k))) : {}), 26 // Passed-in values from slider take precedence over saved qParams 27 ...(pruningThreshold !== undefined ? {pruningThreshold} : {}), 28 ...(maxEmbedNodes !== undefined ? {maxEmbedNodes} : {}), 29 } 30 31 // Get pinnedIds from URL parameters if available (prioritized over localStorage) 32 var urlPinnedIds = util.params.get('pinnedIds'); 33 var urlHiddenIds = util.params.get('hiddenIds'); 34 console.log(urlPinnedIds, urlHiddenIds) 35 console.log(visState.pinnedIds, visState.hiddenIds) 36 if (urlPinnedIds) visState.pinnedIds = urlPinnedIds 37 if (urlHiddenIds) visState.hiddenIds = urlHiddenIds 38 39 let urlSupernodes = []; 40 try { 41 const supernodesParam = util.params.get('supernodes'); 42 if (supernodesParam) { 43 urlSupernodes = JSON.parse(supernodesParam); 44 } 45 } catch (e) { 46 console.error('Error parsing supernodes from URL:', e); 47 } 48 if (urlSupernodes) visState.supernodes = urlSupernodes 49 50 if (visState.clickedId?.includes('supernode')) delete visState.clickedId 51 if (clickedId && clickedId != 'null' && !clickedId.includes('supernode-')) visState.clickedId = clickedId 52 if (!visState.clickedId || visState.clickedId == 'null' || visState.clickedId == 'undefined') visState.clickedId = data.nodes.find(d => d.isLogit)?.nodeId 53 if (visState.pinnedIds?.replace) visState.pinnedIds = visState.pinnedIds.split(',') 54 if (visState.hiddenIds?.replace) visState.hiddenIds = visState.hiddenIds.split(',') 55 56 57 // Load clerps from URL params 58 const clerpsParam = util.params.get('clerps') || data.qParams.clerps; 59 if (clerpsParam) { 60 const clerps = JSON.parse(clerpsParam); 61 visState.clerps = new Map(clerps); 62 } 63 data = await utilCg.formatData(data, visState) 64 65 var renderAll = util.initRenderAll(['hClerpUpdate', 'clickedId', 'hiddenIds', 'pinnedIds', 'linkType', 'isShowAllLinks', 'features', 'isSyncEnabled', 'shouldSortByWeight', 'hoveredId']) 66 67 function colorNodes() { 68 data.nodes.forEach(d => d.nodeColor = '#fff') 69 } 70 colorNodes() 71 72 // global link color â the color scale skips #fff so links are visible 73 // TODO: weight by input sum instead 74 function colorLinks() { 75 var absMax = d3.max(data.links, d => d.absWeight) 76 var _linearAbsScale = d3.scaleLinear().domain([-absMax, absMax]) 77 var _linearPctScale = d3.scaleLinear().domain([-.4, .4]) 78 var _linearTScale = d3.scaleLinear().domain([0, .5, .5, 1]).range([0, .5 - .001, .5 + .001, 1]) 79 80 var widthScale = d3.scaleSqrt().domain([0, 1]).range([.00001, 3]) 81 82 utilCg.pctInputColorFn = d => d3.interpolatePRGn(_linearTScale(_linearPctScale(d))) 83 84 data.links.forEach(d => { 85 // d.color = d3.interpolatePRGn(_linearTScale(_linearAbsScale(d.weight))) 86 d.strokeWidth = widthScale(Math.abs(d.pctInput)) 87 d.pctInputColor = utilCg.pctInputColorFn(d.pctInput) 88 d.color = d3.interpolatePRGn(_linearTScale(_linearPctScale(d.pctInput))) 89 }) 90 } 91 colorLinks() 92 93 renderAll.hClerpUpdate.fns.push(params => utilCg.hClerpUpdateFn(params, data)) 94 95 renderAll.hoveredId.fns.push(() => { 96 // use hovered node if possible, otherwise use last occurence of feature 97 var targetCtxIdx = visState.hoveredCtxIdx ?? 999 98 var hoveredNodes = data.nodes.filter(n => n.featureId == visState.hoveredId) 99 var node = d3.sort(hoveredNodes, d =>
99 Math.abs(d.ctx_idx - targetCtxIdx))[0] 100 visState.hoveredNodeId = node?.nodeId 101 }) 102 103 // set tmpClickedLink w/ strength of all the links connected the clickedNode 104 renderAll.clickedId.fns.push(() => { 105 clickedIdCb?.(visState.clickedId) 106 107 var node = data.nodes.idToNode[visState.clickedId] 108 if (!node){ 109 // for a clicked supernode, sum over memberNode links to make tmpClickedLink 110 if (visState.clickedId?.startsWith('supernode-')) { 111 node = { 112 nodeId: visState.clickedId, 113 memberNodes: visState.subgraph.supernodes[+visState.clickedId.split('-')[1]] 114 .slice(1) 115 .map(id => data.nodes.idToNode[id]) 116 } 117 node.memberSet = new Set(node.memberNodes.map(d => d.nodeId)) 118 119 function combineLinks(links, isSrc) { 120 return d3.nestBy(links, d => isSrc ? d.sourceNode.nodeId : d.targetNode.nodeId) 121 .map(links => ({ 122 source: isSrc ? links[0].sourceNode.nodeId : visState.clickedId, 123 target: isSrc ? visState.clickedId : links[0].targetNode.nodeId, 124 sourceNode: isSrc ? links[0].sourceNode : node, 125 targetNode: isSrc ? node : links[0].targetNode, 126 weight: d3.sum(links, d => d.weight), 127 absWeight: Math.abs(d3.sum(links, d => d.weight)) 128 })) 129 } 130 131 node.sourceLinks = combineLinks(node.memberNodes.flatMap(d => d.sourceLinks), true) 132 node.targetLinks = combineLinks(node.memberNodes.flatMap(d => d.targetLinks), false) 133 } else { 134 return data.nodes.forEach(d => { 135 d.tmpClickedLink = null 136 d.tmpClickedSourceLink = null 137 d.tmpClickedTargetLink = null 138 }) 139 } 140 } 141 142 var connectedLinks = [...node.sourceLinks, ...node.targetLinks] 143 var max = d3.max(connectedLinks, d => d.absWeight) 144 var colorScale = d3.scaleSequential(d3.interpolatePRGn).domain([-max*1.1, max*1.1]) 145 146 // allowing supernode links means each node can have a both tmpClickedSourceLink and tmpClickedTargetLink 147 // currently we render bidirectional links where possible, falling back to the target side links otherwises 148 var nodeIdToSourceLink = {} 149 var nodeIdToTargetLink = {} 150 var featureIdToLink = {} 151 connectedLinks.forEach(link => { 152 if (link.sourceNode === node) { 153 nodeIdToTargetLink[link.targetNode.nodeId] = link 154 featureIdToLink[link.targetNode.featureId] = link 155 link.tmpClickedCtxOffset = link.targetNode.ctx_idx - node.ctx_idx 156 } 157 if (link.targetNode === node) { 158 nodeIdToSourceLink[link.sourceNode.nodeId] = link 159 featureIdToLink[link.sourceNode.featureId] = link 160 link.tmpClickedCtxOffset = link.sourceNode.ctx_idx - node.ctx_idx 161 } 162 // link.tmpColor = colorScale(link.pctInput) 163 link.tmpColor = link.pctInputColor 164 }) 165 166 data.nodes.forEach(d => { 167 var link = nodeIdToSourceLink[d.nodeId] || nodeIdToTargetLink[d.nodeId] 168 d.tmpClickedLink = link 169 d.tmpClickedSourceLink = nodeIdToSourceLink[d.nodeId] 170 d.tmpClickedTargetLink = nodeIdToTargetLink[d.nodeId] 171 }) 172 173 data.features.forEach(d => { 174 var link = featureIdToLink[d.featureId] 175 d.tmpClickedLink = link 176 }) 177 }) 178 179 function initGridsnap() { 180 var gridData = [ 181 // {cur: {x: 0, y: 0, w: 6, h: .5}, class: 'button-container'}, 182 {cur: {x: 0, y: 8, w: 8, h: 8}, class: 'subgraph'}, 183 {cur: {x: 8, y: 1, w: 6, h: 6}, class: 'node-connections'}, 184 {cur: {x: 8, y: 6, w: 6, h: 10}, class: 'feature-detail'}, 185 {cur: {x: 0, y: 0, w: 8, h: 8}, class: 'link-graph', resizeFn: makeResizeFn(initCgLinkGraph)}, 186 // {cur: {x: 0, y: 18, w: 6, h: 7}, class: 'clerp-list'}, 187 // {cur: {x: 6, y: 30, w: 4, h: 7}, class: 'feature-scatter'}, 188 // {cur: {x: 0, y: 30, w: 3, h: 8}, class: 'metadata'}, 189 ].filter(d => d) 190 191 var initFns = [ 192 initCgButtonContainer, 193 initCgSubgraph, 194 initCgLinkGraph, 195 initCgNodeConnections, 196 initCgFeatureDetail, 197 // initCgClerpList, 198 // initCgFeatureScatter, 199 ].filter(d => d) 200 201 var gridsnapSel = sel.html('').append('div.gridsnap.cg') 202 .classed('is-edit-mode', visState.isGridsnap) 203 if (visState.isModal) gridsnapSel.st({width: '100%', height: '100%'}) 204 205 206 window.initGridsnap({ 207 gridData, 208 gridSizeY: 50, 209 pad: 10, 210 sel: gridsnapSel, 211 isFullScreenY: false,
212 isFillContainer: visState.isModal, 213 serializedGrid: data.qParams.gridsnap 214 }) 215 216 initFns.forEach(fn => fn({visState, renderAll, data, cgSel: sel})) 217 218 function makeResizeFn(fn){ 219 return () => { 220 fn({visState, renderAll, data, cgSel: sel.select('.gridsnap.cg')}) 221 Object.values(renderAll).forEach(d => d()) 222 } 223 } 224 } 225 226 initGridsnap() 227 renderAll.hClerpUpdate() 228 renderAll.isShowAllLinks() 229 renderAll.linkType() 230 renderAll.clickedId() 231 renderAll.pinnedIds() 232 renderAll.features() 233 renderAll.isSyncEnabled() 234 renderAll.hoveredId() 235} 236 237window.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.