<!doctype html>
<html>
<head>
  <title>PacVis</title>
  <script type="text/javascript" src="static/vis-network.min.js"></script>
  <link href="static/vis.min.css" rel="stylesheet" type="text/css" />
  <link rel="stylesheet" href="static/material.deep_purple-amber.min.css" />
  <script defer src="static/material.min.js"></script>
  <link defer rel="stylesheet" href="static/animate.css">
  <script src="static/pacvis.js"></script>
  <link rel="stylesheet" href="static/pacvis.css">
  <!-- favicons -->
  <link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
</head>
<body>
<div id="target" class="target"></div>
<button id="leftpanel_show" title="Show Panel"
        class="leftpanel-show mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect">
  <i class="material-icons">menu</i>
</button>
<div id="leftpanel" class="leftpanel">
  <div id="lefttoppanel" class="lefttoppanel">
    <div class="mdl-card mdl-shadow--4dp" id="card-header">
      <div class="mdl-card__title mdl-card--expand"
          style="background-color: rgb(103,58,183); color: #fff; padding: 16px 16px 0 16px;">
        <h3 style="color:#fff">PacVis</h3>
        <div class="mdl-layout-spacer"></div>

        <a title="GitHub repo" href="https://github.com/farseerfc/pacvis"
          class="mdl-button mdl-js-button mdl-button--icon">
          <i class="material-icons">launch</i>
        </a>
      </div>
      <div id="loading_progress" class="mdl-progress mdl-js-progress" style="width:100%"></div>
      <div class="mdl-card__title mdl-card--expand"
           style="color: rgb(103,58,183); padding: 16px 16px 8px 16px;">
        <div class="mdl-textfield mdl-js-textfield mdl-textfield--expandable
                    mdl-textfield--floating-label"
             id="searchwrapper">
          <label class="mdl-button mdl-js-button mdl-button--icon" for="search">
            <i class="material-icons">search</i>
          </label>
          <div class="mdl-textfield__expandable-holder">
            <input class="mdl-textfield__input" type="text" id="search" placeholder="search pkgname" />
          </div>
        </div>
        <div class="mdl-layout-spacer"></div>
        <button id="legend-btn" title="Show Legend"
          class="mdl-button mdl-js-button mdl-button--icon">
          <i class="material-icons">help_outline</i>
        </button>
        <button id="advanced_menu" title="Show Advanced options"
          class="mdl-button mdl-js-button mdl-button--icon">
          <i class="material-icons">settings</i>
        </button>
      </div>
      <ul class="mdl-list" id="search-list" style="padding: 0; background-color: #f5f5f5; margin: 0; display: none;">
      </ul>
      <div class="mdl-card__title mdl-card--expand" id="legend_panel"
           style="padding: 0 16px 16px 16px;">
           <div>
             Legend below: <a href="https://farseerfc.me/en/pacvis.html">and for more details</a>
           </div>
           <div id="legend_target" style="width:100%; height:300px">
           </div>
      </div>

      <div id="advanced_form">
        <form method="get" action="/">
          <div class="mdl-card__title mdl-card--expand"
               style="padding: 0 16px 0px 16px;">
            <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
              <input class="mdl-textfield__input" type="number" id="maxlevel"
                     title="Limiting max topology level to increase performance"
                     name="maxlevel" value="{{options.maxlevel}}" />
              <label class="mdl-textfield__label" for="maxlevel">Max Level: </label>
            </div>
            <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
              <input class="mdl-textfield__input" type="number" id="maxdeps"
                      title="Limiting max depends lines to show a clearer image"
                      name="maxdeps" value="{{options.maxdeps}}" />
              <label class="mdl-textfield__label" for="maxdeps">Max Depends: </label>
            </div>
            <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
              <input class="mdl-textfield__input" type="number" id="maxreqs"
                      title="Limiting max required-by lines to show a clearer image"
                      name="maxreqs" value="{{options.maxreqs}}" />
              <label class="mdl-textfield__label" for="maxreqs">Max Required-By: </label>
            </div>
          </div>
          <div class="mdl-card__supporting-text mdl-card--expand">
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-isize">
              <input type="radio" id="option-isize" class="mdl-radio__button" name="drawsize" value="isize"
                {% if options.drawsize == "isize" %} checked {% end %}>
              <span class="mdl-radio__label">Draw Installed Size (-R)</span>
            </label>
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-csize">
              <input type="radio" id="option-csize" class="mdl-radio__button" name="drawsize" value="csize"
                {% if options.drawsize == "csize" %} checked {% end %}>
              <span class="mdl-radio__label">Draw Cascade Size (-Rc)</span>
            </label>
            <label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="option-cssize">
              <input type="radio" id="option-cssize" class="mdl-radio__button" name="drawsize" value="cssize"
                {% if options.drawsize == "cssize" %} checked {% end %}>
              <span class="mdl-radio__label">Draw Recursive Size (-Rcs)</span>
            </label>
          </div>
          <div class="mdl-card__supporting-text mdl-card--expand">
            <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="usemagic">
              <input type="checkbox" id="usemagic" name="usemagic"
                class="mdl-switch__input"
                value="True"
                {% if options.usemagic %} checked {% end %} >
              <span class="mdl-switch__label">Randomize topology level</span>
            </label>
            <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="byrepos">
              <input type="checkbox" id="byrepos" name="byrepos"
                class="mdl-switch__input"
                value="True"
                {% if options.byrepos %} checked {% end %} >
              <span class="mdl-switch__label">Layered by repositories</span>
            </label>
            <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="enablephysics">
              <input type="checkbox" id="enablephysics" name="enablephysics"
                class="mdl-switch__input"
                value="True"
                {% if options.enablephysics %} checked {% end %} >
              <span class="mdl-switch__label">Enable physics after stabilized</span>
            </label>
            <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="aligntop">
              <input type="checkbox" id="aligntop" name="aligntop"
                class="mdl-switch__input"
                value="True"
                {% if options.aligntop %} checked {% end %} >
              <span class="mdl-switch__label">Align packages to top</span>
            </label>
            <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="showallvdeps">
              <input type="checkbox" id="showallvdeps" name="showallvdeps"
                class="mdl-switch__input"
                value="True"
                {% if options.showallvdeps %} checked {% end %} >
              <span class="mdl-switch__label">Show all virtual dependencies</span>
            </label>
            <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="straightline">
              <input type="checkbox" id="straightline" name="straightline"
                class="mdl-switch__input"
                value="True"
                {% if options.straightline %} checked {% end %} >
              <span class="mdl-switch__label">Use straight lines as edges</span>
            </label>
          </div>
          <div class="mdl-card__title mdl-card--expand">
            <div class="mdl-layout-spacer"></div>
            <button type="submit" value="Reload" title="Reload the graph" id="reloadbtn"
                    class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">
              <i class="material-icons">refresh</i>
            </button>
          </div>
        </form>
      </div>
      <div class="mdl-card__menu" style="color:#fff">
        <a id="close_button" title="Hide Panel"
           class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
          <i class="material-icons">close</i>
        </a>
      </div>
    </div>

    <div class="mdl-card mdl-shadow--4dp" id="fsinfo" style="display:none">
      <div class="mdl-card__title mdl-card--expand">
          <h4 id="pkgname" class="mdl-card__title-text"></h4>
      </div>
      <div class="mdl-card__actions mdl-card--expand">
        <div id="pkgdesc" class="mdl-card__subtitle-text"></div>
        <div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
          <div class="mdl-tabs__tab-bar">
              <a href="#info-panel" title="Informations" class="mdl-tabs__tab is-active">Info</a>
              <a href="#dep-panel" title="Dependencies" class="mdl-tabs__tab">
                <span class="mdl-badge" data-badge="4" id="badgedep">Dep</span>
              </a>
              <a href="#req-panel" title="Packages that depends on this" class="mdl-tabs__tab">
                <span class="mdl-badge" data-badge="4" id="badgereq">Req-By</span>
              </a>
              <a href="#opt-panel" title="Optional dependencies" class="mdl-tabs__tab">
                <span class="mdl-badge" data-badge="4" id="badgeoptdep">Opt-Dep</span>
              </a>
          </div>

          <div class="mdl-tabs__panel is-active" id="info-panel">
            <table class="mdl-data-table mdl-js-data-table">
              <tr>
                <th class="mdl-data-table__cell--non-numeric">Topology Level</th>
                <td id="pkglevel" class="mdl-data-table__cell--non-numeric"></td>
              </tr>
              <tr>
                <th class="mdl-data-table__cell--non-numeric">Repo</th>
                <td id="pkgrepo" class="mdl-data-table__cell--non-numeric"></td>
              </tr>
              <tr>
                <th class="mdl-data-table__cell--non-numeric">Groups</th>
                <td id="pkggroups" class="mdl-data-table__cell--non-numeric"></td>
              </tr>
              <tr>
                <th class="mdl-data-table__cell--non-numeric">Provides</th>
                <td id="pkgprovides" class="mdl-data-table__cell--non-numeric"></td>
              </tr>
              <tr>
                <th class="mdl-data-table__cell--non-numeric" id="pkgsizedesc">Size</th>
                <td id="pkgsize" class="mdl-data-table__cell--non-numeric"></td>
              </tr>
              <tr>
                <th class="mdl-data-table__cell--non-numeric">Version</th>
                <td id="pkgversion" class="mdl-data-table__cell--non-numeric"></td>
              </tr>
              <tr>
                <th class="mdl-data-table__cell--non-numeric">Install reason</th>
                <td id="pkgreason" class="mdl-data-table__cell--non-numeric"></td>
              </tr>
            </table>
          </div>
          <div class="mdl-tabs__panel" id="dep-panel">
            <div id="pkgdeps"></div>
          </div>
          <div class="mdl-tabs__panel" id="req-panel">
            <div id="pkgreqs"></div>
          </div>
          <div class="mdl-tabs__panel" id="opt-panel">
            <div id="pkgoptdeps"></div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <div class="leftpanel-space"></div>
  <div id="zoompanel" class="zoompanel">
    <button id="zoomin" title="Zoom In"
            class="zoombtn mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect">
      <i class="material-icons">add</i>
    </button>
    <button id="zoomout" title="Zoom Out"
            class="zoombtn mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect">
      <i class="material-icons">remove</i>
    </button>
    <button id="zoomfit" title="Zoom to fit all"
            class="zoombtn mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect">
      <i class="material-icons">zoom_out_map</i>
    </button>
  </div>
</div>


<script type="text/javascript">

var currentsize="{{ options.drawsize }}";

var nodedata = {% raw nodes %};

function wrapDeps(list){
  var depsdom = "";
  if (list == "")
    return "<i>Nothing</i>";
  var count = 0;
  for (let dep of list.split(", ")) {
    depsdom += dep + ", ";
    count++;
    if(count%10 == 0){
      depsdom +="<br/>";
    }
  }
  return depsdom;
}

for(let nodeid in nodedata){
  let node = nodedata[nodeid];
  node.title = "<h4>" + node.label + "</h4>" +
    "<p>size: " + filesize(node[currentsize]) + "</p>" +
    "<p>groups: [" + wrapDeps(node.groups) + "]</p>" +
    "<p>depends: [" + wrapDeps(node.deps) + "]</p>" +
    "<p>required by:[" + wrapDeps(node.reqs) +"]</p>" +
    "<p>optdeps:[" + wrapDeps(node.optdeps) +"]</p>";
  node.value = size2value(node[currentsize]);
}

var nodes = new vis.DataSet(nodedata);

var edges = new vis.DataSet({% raw links %});

// create a network
var container = document.getElementById('target');
var data = {
  nodes: nodes,
  edges: edges
};
var physics = {
  maxVelocity: Math.floor(Math.sqrt(Math.sqrt(nodes.length))*200),
  hierarchicalRepulsion: {
    nodeDistance: Math.floor(Math.sqrt(Math.sqrt(nodes.length))*180),
    springLength: Math.floor(Math.sqrt(Math.sqrt(nodes.length))*100),
    springConstant: 1,
    damping: 0.1
  },
  timestep: 0.1,
  stabilization: {
    iterations: 30*Math.sqrt(nodes.length),
    updateInterval: 0.3*Math.sqrt(nodes.length),
  }
};
var options = {
  groups: {
    standalone: { shape: 'square', color: 'rgba(100,100,255,0.8)', size: 12 },
    normal: { shape: 'dot', color: 'rgba(255,171,64,0.8)'},
    group: { shape: 'triangle', size: 12, color: 'rgba(76,175,80,0.8)'},
    vdep: { shape: 'diamond', size: 12, color: 'rgba(205,220,57,0.8)'},
    explicit: { shape: 'dot', color: 'rgba(103,58,183,0.8)'},
    consolidated: { shape: 'star', color: 'rgba(255,0,0,0.8)', size: 12},
  },
  nodes: {
    scaling: {
      min: 5,
      max: 100,
      label: {
        min:5,
        max:100,
        maxVisible: 50,
        drawThreshold: 10
      }
    }
  },
  edges: {
    {% if options.straightline %}
    smooth: false,
    {% else %}
     smooth: {
         type: 'cubicBezier',
         forceDirection: "vertical",
         roundness: 0.5
     },
    {% end %}
     selectionWidth: 8,
     arrows: {
         to : true
     }
  },
  layout: {
     hierarchical: {
         direction: "UD",
         nodeSpacing: Math.floor(Math.sqrt(Math.sqrt(nodes.length))*100),
         treeSpacing: Math.floor(Math.sqrt(Math.sqrt(nodes.length))*180),
         levelSeparation: Math.floor(Math.sqrt(Math.sqrt(nodes.length))*50),
         blockShifting: false,
         edgeMinimization: false,
         parentCentralization: false
     },
    improvedLayout: true
  },
  interaction: {
    keyboard: {bindToWindow: false},
    hideEdgesOnDrag: true
  },
  {% if options.disableallphysics %}
  physics: false
  {% else %}
  physics: physics
  {% end %}
};

var network = null;

function pacvis(){
  network = new vis.Network(container, data, options);

  var debug_performance = {% if options.debugperformance %} true {% else %} false {% end %};

  network.on("startStabilizing", function (params) {
    document.title = 'PacVis';
  });
  network.on("stabilizationProgress", function (params) {
    document.title = 'PacVis | ' +  Math.round(params.iterations/params.total*100) + "%";
    document.querySelector('#loading_progress').MaterialProgress.setProgress(Math.round(params.iterations/params.total*100));
  });
  network.on("stabilizationIterationsDone", function (params) {
    if(debug_performance){
      document.title = "PacVis | loaded in: " + window.performance.now()/1000 ;
    }else{
      document.title = "PacVis";
    }
    document.querySelector('#loading_progress').MaterialProgress.setProgress(100);
    if( ! document.querySelector('#enablephysics').checked){
      network.setOptions({physics: false});
    }
  });

  network.on("selectNode", function (params) {
    // this is to fix a timing issue with chrome
    // when switching node beyond 2 hoops
    setTimeout(function (){
      selectPkg(nodes.get(params.nodes[0]));
    },1);
  });
  network.on("deselectNode", function (params) {
    deselectPkg();
  });
  network.on("click", function(params){
    neighbourhoodHighlight(params);
  });

}


var pacvisopts = {% raw optionsjson %};

function ifNeedReload(){
  let need = false;
  for(key in pacvisopts){
    let v = pacvisopts[key];
    let dom = document.querySelector("#"+key);
    if(dom){
      let cv = Number.isInteger(v) ? dom.value : dom.checked;
      need = need || cv != v;
    }
    need = need || !document.querySelector("#option-"+currentsize).checked;
  }
  if(need){
    document.querySelector('#reloadbtn').className = "mdl-button mdl-js-button mdl-button--raised mdl-button--colored";
  }else{
    document.querySelector('#reloadbtn').className = "mdl-button mdl-js-button mdl-button--primary";
  }
}
for(key in pacvisopts){
  let dom = document.querySelector("#"+key);
  if(dom) {
    if(Number.isInteger(pacvisopts[key])){
      dom.addEventListener("input", ifNeedReload);
    }else{
      dom.addEventListener("change" , ifNeedReload);
    }
  }
}
document.querySelector("#option-isize").addEventListener("change" , ifNeedReload);
document.querySelector("#option-csize").addEventListener("change" , ifNeedReload);
document.querySelector("#option-cssize").addEventListener("change" , ifNeedReload);

document.querySelector('#search').addEventListener('input', trysearch);
document.querySelector('#close_button').addEventListener('click', close_panel);
document.querySelector('#leftpanel_show').addEventListener('click', show_panel);
document.querySelector('#advanced_menu').addEventListener('click', function(){
  if (document.querySelector('#advanced_form').style.display == "block"){
    document.querySelector('#advanced_form').style.display = "none";
  }else{
    document.querySelector('#advanced_form').style.display = "block";
    document.querySelector('#legend_panel').style.display = "none";
  }
});
document.querySelector('#legend-btn').addEventListener('click', function(){
  if (document.querySelector('#legend_panel').style.display == "block"){
    document.querySelector('#legend_panel').style.display = "none";
  }else{
    document.querySelector('#legend_panel').style.display = "block";
    document.querySelector('#advanced_form').style.display = "none";
    let legend_options = JSON.parse(JSON.stringify(options)); // deep copy
    legend_options.layout = {
       hierarchical: {
           direction: "UD",
           nodeSpacing: 50,
           treeSpacing: 50,
           levelSeparation: 100,
           blockShifting: false,
           edgeMinimization: false,
           parentCentralization: false
       },
      improvedLayout: true
    };
    legend_options.physics = false;
    let legend = new vis.Network(document.querySelector("#legend_target"), {
      nodes: [
        {
          id: "normal-legend",
          label: "packages installed as dependencies",
          level: 0,
          group: "normal",
          value: size2value(0),
        },
        {
          id: "explicit-legend",
          label: "packages installed explicitly",
          level: 2,
          group: "explicit",
          value: size2value(0),
        },
        {
          id: "group-legend",
          label: "package groups",
          level: 4,
          group: "group",
          value: size2value(0),
        },
        {
          id: "vdep-legend",
          label: "virtual dependencies (in provides)",
          level: 6,
          group: "vdep",
          value: size2value(0),
        },
      ],
      edges: [
        { from: "normal-legend", to: "explicit-legend" },
        { from: "explicit-legend", to: "group-legend" },
        { from: "group-legend", to: "vdep-legend" },
      ]
    }, legend_options);

    legend.fit();
    setTimeout(function(){
      legend.moveTo({scale: legend.getScale()*0.85, animation : {duration : 300}});
    }, 50);
  }
});
document.querySelector('#enablephysics').addEventListener('change', function(){
  if(this.checked){
    network.setOptions({physics: physics});
  }else{
    network.setOptions({physics: false});
  }
});
document.querySelector('#loading_progress').addEventListener('mdl-componentupgraded', pacvis);

document.querySelector('#zoomin').addEventListener('click', function(){
  network.moveTo({'scale': network.getScale()*2, animation : {duration : 300}});
});

document.querySelector('#zoomout').addEventListener('click', function(){
  network.moveTo({'scale': network.getScale()*0.5, animation : {duration : 300}});
});

document.querySelector('#zoomfit').addEventListener('click', function(){
  network.fit({animation : {duration : 300}});
});


</script>

</body>
</html>
