# Citations

<script>
import ContextMenu from 'src/client/contextmenu.js';

(async () => {
   var vis = await (<d3-graphviz style="background:gray; width:100%; height: 100%"></d3-graphviz>)
    
    var baseURL = lively.query(this, "lively-container").getPath().replace(/[^/]*$/,"") + "../"
    vis.engine = "dot" 
    
    var menuItems = [
      ["graphviz engine", 
        ["dot", "neato", "fdp", "twopi", "circo"].map(ea => {
          return [ea,
            () => {
              vis.engine = ea  
              vis.setDotData(dataToDot(graph))
            }
          ]
        })
      ]
    ]
    
    vis.addEventListener("contextmenu",  evt => {
      ContextMenu.openIn(document.body, evt, this, undefined, menuItems);
      evt.stopPropagation();
      evt.preventDefault();
    });
    // vis.engine = "circo"
    
    var nodeMap = new Map()
    
//     var root = await loadNode("1509260") 
//     if (!root) {
//       return "I got not roots... " 
//     }
//     var graph =  [root]

    
    var mainCache = await caches.open("BibliograpyACMCache")
    
    async function loadJSON(id, type="main") {
     var request = new Request(baseURL  + "acm/" + type + "/" + id + ".json")
     try {
        
        var resp =  await mainCache.match(request)
        if (!resp ) {
          await mainCache.add(request)
          resp = await mainCache.match(request)
        }
        return  await resp.json()
      } catch(e) {
        console.warn(e)
        return
      }
    }

    async function loadNode(id) {
      var json = await loadJSON(id, "main")
      return ensureNode(id, json)
    }

    function ensureNode(id, json) {
      json = json || {}
      var node = nodeMap.get(id)
      if (!node) {
        node = {
          id: id,
          json: json,
          text: json.text,
          out: []
        }
        nodeMap.set(id, node)
      }
      return node
    }
    
//     subClasses.forEach(ea => {
//       var obj = ea
      
//       var node = ensureNode(obj)
      
//       node.expanded = true
//       if (obj.__proto__) {
//         var parentNode = ensureNode(obj.__proto__)
//         parentNode.out.push( {
//             // edge
//             label: "subclass",
//             target: node
//           })
//         parentNode.expanded = true
//       }
//     })

//     function collapseNode(graphNode) {
//       graphNode.out = []
//       graphNode.expanded = false
//     }
    
    async function expandNode(graphNode) {
      var json = (await loadJSON(graphNode.id, "citations")) || []
      graphNode.out = json.map(ea => {
        return {
          // edge
          label: "citation",
          target: ensureNode(ea.citation, ea)
        }
      })
      
      graphNode.expanded = true
    }

    var all = await fetch(baseURL  + "acm/main/", {
      method: "OPTIONS"
    }).then(r => r.json());
    
    var graph = []
    
    for(var eaId of all.contents.map(ea => ea.name.replace(/\.json/, ""))) {
      var n = await loadNode(eaId)
      graph.push(n)
      await expandNode(n)
    }
    window.LastGraph = graph    
    

    function stripLabel(str) {
      return str.replace(/([^A-Za-z0-9 _.,;:<>\/\[\]])/g," ").slice(0,50)
    }

    function dataToDot(graph) {
      var edges = []
      var nodes = []
      var visited = new Set()
      function visit(node) {
        if (visited.has(node)) return
        visited.add(node)
        var id = node.id
        var name = (node.json &&  node.json.title ) || node.text || node.name || node.id
        
        

        name = stripLabel(name)
        
        var inner = ["<b>" + name + "</b>"] 
        
        if (node.out) {
          node.out.forEach(eaOut => {
            edges.push(node.id + " -> " + eaOut.target.id + `[  ` // label="${eaOut.label}" 
                +`fontcolor="${ eaOut.target.expanded ? "black" : "gray"}" `
               +`color="${ eaOut.target.expanded ? "black" : "gray"}" `
              + `]`)
            visit(eaOut.target)            
          })          
        }
        // <U><TABLE><TR><TD>hello${inner.join("<br>")}</TD></TR></U>
        nodes.push(id + `[shape="plaintext" label=< ${inner}> ` 
          +`fontcolor="${ node.expanded ? "black" : "gray"}" `
          +`color="${ node.expanded ? "black" : "gray"}"]`)
      }
      if (graph.forEach) {
        graph.forEach(graphNode => {
          visit(graphNode) // support multiple roots
        })
      } else {
        visit(graph)
      }
      return `digraph {
        rankdir = LR;
        graph [  splines="spline"  overlap="false"  ];
        node [    fontname="Arial"  fontsize="14"  fontcolor="black" ];
        edge [ dir="back" arrowhead=none fontname="Arial"  fontsize="8" ];

        ${edges.join(";\n")}
        ${nodes.reverse().join(";\n")}
      }`
      
    }
    
    
    var dotData = dataToDot(graph)
    
    vis.config({
      async onclick(data, evt, element) {
        // lively.showElement(element)
        if(evt.ctrlKey) {
          lively.openInspector({
            data: data,
            node: nodeMap.get(data.key),
            element: element
          })
        } else {
          var node = nodeMap.get(data.key)
          if (node) {
            if (node.out.length == 0) {
              await expandNode(node)
            } else {
              collapseNode(node)
            }
          
          }
          lively.showElement(element, 300).innerHTML = ""
          vis.update(dataToDot(graph))    
        }
      }
    })    

    vis.setDotData(dotData)
    
    return vis
  })()
</script>