PageSourceSearch

https://transcoder-adapters.github.io/graphs/attribution_graph/init-cg.js

js transcoder-adapters.github.io collected 2026-10-03 10:06:04 UTC 9,320 bytes, 237 lines download raw bytes

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.