Skip to content

Instantly share code, notes, and snippets.

@justinvdm
Created July 29, 2012 11:14
Show Gist options
  • Save justinvdm/3197645 to your computer and use it in GitHub Desktop.
Save justinvdm/3197645 to your computer and use it in GitHub Desktop.
Force directed graph with collapsible hierarchical clustering

Force directed graph with collapsible hierarchical clustering

fch.(coffee/js)

D3 powers the view class (ForceClusterHierarchy) found that extends from the Backbone View class. This module might be able to be used standalone for visualising pretty much anything, as long as a compatible Model Collection is plugged into it (see network-status.(coffee/js))

network-status.(coffee/js)

The StatusTree collection contains a flattened hierarchy of network node Models (StatusNode).

{"children": [{"children": [{"children": [{"children": [{"status": "bad", "size": 4}, {"status": "good", "size": 3}, {"status": "good", "size": 1}, {"status": "bad", "size": 1}]}, {"children": [{"status": "bad", "size": 1}, {"status": "good", "size": 1}, {"status": "bad", "size": 2}, {"status": "good", "size": 3}, {"status": "good", "size": 3}, {"status": "bad", "size": 2}, {"status": "ok", "size": 1}, {"status": "ok", "size": 3}, {"status": "good", "size": 3}]}, {"children": [{"status": "bad", "size": 2}]}, {"status": "ok", "size": 1}]}, {"status": "ok", "size": 1}, {"children": []}, {"children": [{"children": [{"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "good", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}]}, {"children": [{"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "bad", "size": 4}, {"status": "good", "size": 3}, {"status": "bad", "size": 4}]}, {"children": [{"status": "good", "size": 2}, {"status": "good", "size": 3}]}, {"children": [{"status": "ok", "size": 4}, {"status": "good", "size": 2}, {"status": "ok", "size": 4}, {"status": "ok", "size": 4}]}, {"status": "good", "size": 3}, {"children": []}, {"children": [{"status": "good", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}, {"status": "ok", "size": 1}]}, {"children": [{"status": "ok", "size": 1}, {"status": "good", "size": 2}, {"status": "ok", "size": 4}, {"status": "good", "size": 1}]}]}, {"status": "good", "size": 3}, {"children": []}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"children": [{"children": [{"status": "good", "size": 2}, {"status": "good", "size": 3}, {"status": "bad", "size": 3}, {"status": "ok", "size": 2}]}, {"children": [{"status": "bad", "size": 2}, {"status": "bad", "size": 1}, {"status": "good", "size": 4}, {"status": "good", "size": 1}, {"status": "good", "size": 1}, {"status": "ok", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 4}]}, {"children": [{"status": "ok", "size": 4}, {"status": "good", "size": 1}, {"status": "ok", "size": 4}, {"status": "bad", "size": 2}, {"status": "good", "size": 2}, {"status": "good", "size": 2}, {"status": "good", "size": 1}]}, {"children": [{"status": "bad", "size": 2}, {"status": "good", "size": 4}, {"status": "bad", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}]}, {"children": []}, {"children": [{"status": "good", "size": 2}, {"status": "bad", "size": 2}, {"status": "ok", "size": 1}, {"status": "bad", "size": 2}, {"status": "bad", "size": 2}]}, {"children": [{"status": "ok", "size": 3}, {"status": "ok", "size": 3}, {"status": "good", "size": 4}, {"status": "bad", "size": 1}, {"status": "good", "size": 3}, {"status": "ok", "size": 4}]}]}]}, {"children": [{"status": "bad", "size": 4}, {"children": []}, {"children": [{"children": [{"status": "ok", "size": 4}, {"status": "good", "size": 4}]}]}, {"children": [{"children": [{"status": "bad", "size": 4}, {"status": "good", "size": 2}, {"status": "ok", "size": 4}, {"status": "ok", "size": 4}, {"status": "bad", "size": 4}, {"status": "bad", "size": 4}, {"status": "bad", "size": 2}]}, {"children": [{"status": "good", "size": 4}, {"status": "ok", "size": 3}, {"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 1}]}, {"status": "good", "size": 3}, {"children": [{"status": "bad", "size": 2}, {"status": "good", "size": 4}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}]}, {"children": [{"status": "bad", "size": 4}]}, {"status": "good", "size": 3}, {"children": [{"status": "bad", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}]}, {"status": "good", "size": 2}, {"children": [{"status": "bad", "size": 2}, {"status": "bad", "size": 3}, {"status": "ok", "size": 4}, {"status": "good", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 3}, {"status": "good", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 4}]}]}, {"status": "good", "size": 2}, {"children": [{"children": [{"status": "good", "size": 4}, {"status": "good", "size": 4}, {"status": "ok", "size": 1}, {"status": "good", "size": 4}, {"status": "ok", "size": 3}, {"status": "bad", "size": 1}]}, {"children": [{"status": "ok", "size": 1}, {"status": "ok", "size": 2}, {"status": "bad", "size": 1}]}, {"children": []}, {"status": "bad", "size": 3}, {"children": [{"status": "good", "size": 3}]}, {"status": "ok", "size": 2}, {"children": [{"status": "good", "size": 3}, {"status": "good", "size": 1}, {"status": "good", "size": 2}, {"status": "bad", "size": 2}, {"status": "ok", "size": 2}, {"status": "good", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 2}, {"status": "bad", "size": 2}]}]}, {"status": "bad", "size": 3}]}, {"children": [{"children": []}, {"status": "good", "size": 3}, {"children": []}]}, {"children": [{"status": "bad", "size": 3}]}, {"children": [{"children": [{"children": []}, {"children": [{"status": "good", "size": 4}, {"status": "ok", "size": 2}, {"status": "ok", "size": 2}, {"status": "good", "size": 4}, {"status": "bad", "size": 4}, {"status": "bad", "size": 3}]}, {"children": [{"status": "bad", "size": 4}, {"status": "bad", "size": 3}, {"status": "ok", "size": 2}]}, {"children": [{"status": "ok", "size": 2}, {"status": "good", "size": 2}]}]}, {"children": [{"status": "good", "size": 2}, {"children": [{"status": "ok", "size": 1}, {"status": "good", "size": 3}, {"status": "ok", "size": 1}, {"status": "ok", "size": 2}, {"status": "bad", "size": 4}, {"status": "ok", "size": 4}, {"status": "good", "size": 4}]}, {"children": [{"status": "ok", "size": 3}, {"status": "good", "size": 3}, {"status": "bad", "size": 1}]}, {"children": [{"status": "bad", "size": 2}, {"status": "ok", "size": 2}]}]}, {"status": "good", "size": 1}]}, {"status": "bad", "size": 1}, {"status": "ok", "size": 1}, {"status": "ok", "size": 3}, {"status": "bad", "size": 2}, {"status": "good", "size": 3}, {"status": "good", "size": 4}, {"status": "ok", "size": 1}, {"status": "good", "size": 3}, {"status": "bad", "size": 4}, {"status": "bad", "size": 2}, {"status": "bad", "size": 4}, {"status": "bad", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 4}, {"status": "ok", "size": 2}, {"status": "bad", "size": 2}, {"status": "bad", "size": 1}, {"status": "bad", "size": 2}, {"status": "good", "size": 4}, {"status": "ok", "size": 3}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "ok", "size": 2}, {"status": "good", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "good", "size": 2}, {"status": "good", "size": 4}, {"status": "bad", "size": 1}, {"status": "bad", "size": 1}, {"status": "bad", "size": 2}, {"status": "ok", "size": 4}, {"status": "good", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 4}, {"status": "bad", "size": 1}, {"status": "ok", "size": 4}, {"status": "bad", "size": 1}, {"status": "bad", "size": 1}, {"status": "ok", "size": 3}, {"status": "bad", "size": 2}, {"status": "bad", "size": 4}, {"status": "good", "size": 4}, {"status": "ok", "size": 3}, {"status": "good", "size": 1}, {"status": "ok", "size": 4}, {"status": "good", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}, {"status": "ok", "size": 2}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 3}, {"status": "bad", "size": 3}, {"status": "ok", "size": 3}, {"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 2}, {"status": "bad", "size": 2}, {"status": "bad", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 4}, {"status": "good", "size": 1}, {"status": "bad", "size": 1}, {"status": "bad", "size": 1}, {"status": "good", "size": 2}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "good", "size": 3}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "ok", "size": 3}, {"status": "ok", "size": 2}, {"status": "ok", "size": 3}, {"status": "ok", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 2}, {"status": "bad", "size": 2}, {"status": "ok", "size": 1}, {"status": "bad", "size": 2}, {"status": "bad", "size": 2}, {"status": "good", "size": 1}, {"status": "ok", "size": 4}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}, {"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 1}, {"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "ok", "size": 4}, {"status": "ok", "size": 4}, {"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "bad", "size": 4}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "ok", "size": 1}, {"status": "bad", "size": 4}, {"status": "bad", "size": 1}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "good", "size": 1}, {"status": "ok", "size": 2}, {"status": "ok", "size": 3}, {"status": "ok", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 1}, {"status": "bad", "size": 1}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "bad", "size": 1}, {"status": "good", "size": 4}, {"status": "bad", "size": 2}, {"status": "good", "size": 1}, {"status": "bad", "size": 1}, {"status": "ok", "size": 4}, {"status": "good", "size": 3}, {"status": "ok", "size": 4}, {"status": "ok", "size": 1}, {"status": "good", "size": 1}, {"status": "good", "size": 2}, {"status": "bad", "size": 2}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"status": "good", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}, {"status": "ok", "size": 3}, {"status": "good", "size": 3}, {"status": "ok", "size": 2}, {"status": "good", "size": 2}, {"status": "ok", "size": 1}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 2}, {"status": "bad", "size": 4}, {"status": "ok", "size": 1}, {"status": "good", "size": 3}, {"status": "ok", "size": 4}, {"status": "ok", "size": 2}, {"status": "good", "size": 1}, {"status": "good", "size": 4}, {"status": "good", "size": 4}, {"status": "bad", "size": 2}, {"status": "bad", "size": 3}, {"status": "good", "size": 1}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}, {"status": "bad", "size": 2}, {"status": "good", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "good", "size": 1}, {"status": "ok", "size": 4}, {"status": "bad", "size": 1}, {"status": "good", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "ok", "size": 3}, {"status": "ok", "size": 4}, {"status": "good", "size": 2}, {"status": "good", "size": 3}, {"status": "bad", "size": 4}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}, {"status": "good", "size": 1}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 3}, {"status": "ok", "size": 4}, {"status": "good", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 1}, {"status": "good", "size": 4}, {"status": "bad", "size": 1}, {"status": "bad", "size": 1}, {"status": "bad", "size": 4}, {"status": "good", "size": 3}, {"status": "ok", "size": 1}, {"status": "bad", "size": 2}, {"status": "bad", "size": 4}, {"status": "good", "size": 1}, {"status": "bad", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "good", "size": 1}, {"status": "good", "size": 3}, {"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}]}
{"children": [{"children": [{"children": []}, {"children": [{"children": [{"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "bad", "size": 4}, {"status": "good", "size": 4}, {"status": "bad", "size": 2}, {"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 2}]}, {"children": [{"status": "good", "size": 4}, {"status": "ok", "size": 1}]}, {"children": []}, {"children": [{"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "bad", "size": 2}, {"status": "bad", "size": 1}, {"status": "good", "size": 2}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}]}, {"children": [{"status": "bad", "size": 1}, {"status": "bad", "size": 4}, {"status": "ok", "size": 1}, {"status": "good", "size": 3}, {"status": "good", "size": 1}, {"status": "good", "size": 1}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}, {"status": "good", "size": 1}]}, {"status": "bad", "size": 2}, {"children": [{"status": "good", "size": 3}, {"status": "good", "size": 1}, {"status": "bad", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 4}]}, {"status": "ok", "size": 3}]}, {"status": "ok", "size": 3}, {"children": [{"children": [{"status": "good", "size": 2}]}, {"children": [{"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "ok", "size": 4}, {"status": "ok", "size": 3}, {"status": "good", "size": 4}]}, {"children": [{"status": "good", "size": 4}, {"status": "good", "size": 2}, {"status": "good", "size": 2}]}, {"children": []}, {"children": [{"status": "good", "size": 2}]}, {"children": [{"status": "bad", "size": 2}, {"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 2}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "bad", "size": 1}]}, {"children": [{"status": "good", "size": 1}, {"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 1}]}, {"status": "ok", "size": 3}]}, {"children": [{"children": [{"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 1}, {"status": "good", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 4}, {"status": "good", "size": 3}, {"status": "good", "size": 3}, {"status": "ok", "size": 4}]}, {"children": []}, {"status": "good", "size": 3}, {"status": "good", "size": 1}, {"children": [{"status": "ok", "size": 2}, {"status": "ok", "size": 1}, {"status": "ok", "size": 2}, {"status": "good", "size": 2}]}, {"children": [{"status": "bad", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 3}, {"status": "ok", "size": 4}, {"status": "good", "size": 4}, {"status": "good", "size": 3}, {"status": "bad", "size": 1}]}, {"children": [{"status": "good", "size": 2}, {"status": "bad", "size": 1}, {"status": "ok", "size": 3}, {"status": "good", "size": 4}, {"status": "bad", "size": 2}]}, {"children": [{"status": "good", "size": 1}, {"status": "good", "size": 1}, {"status": "ok", "size": 1}]}]}, {"children": []}, {"children": [{"children": [{"status": "ok", "size": 3}, {"status": "ok", "size": 4}, {"status": "ok", "size": 1}, {"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 1}, {"status": "ok", "size": 2}]}, {"children": [{"status": "ok", "size": 1}, {"status": "good", "size": 4}, {"status": "bad", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 3}]}, {"children": []}, {"children": [{"status": "ok", "size": 4}, {"status": "ok", "size": 3}, {"status": "good", "size": 3}, {"status": "ok", "size": 2}]}, {"children": [{"status": "ok", "size": 4}, {"status": "ok", "size": 3}, {"status": "bad", "size": 4}, {"status": "bad", "size": 4}, {"status": "ok", "size": 4}]}, {"children": [{"status": "good", "size": 4}, {"status": "good", "size": 4}, {"status": "good", "size": 3}]}]}]}, {"status": "bad", "size": 1}, {"status": "ok", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 1}, {"status": "bad", "size": 2}, {"status": "good", "size": 4}, {"status": "ok", "size": 4}, {"status": "good", "size": 4}, {"status": "ok", "size": 1}, {"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "bad", "size": 4}, {"status": "ok", "size": 1}, {"status": "ok", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 2}, {"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}, {"status": "bad", "size": 1}, {"status": "good", "size": 3}, {"status": "ok", "size": 2}, {"status": "good", "size": 2}, {"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 1}, {"status": "bad", "size": 2}, {"status": "bad", "size": 3}, {"status": "bad", "size": 4}, {"status": "bad", "size": 1}, {"status": "good", "size": 3}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}, {"status": "ok", "size": 3}, {"status": "bad", "size": 1}, {"status": "ok", "size": 3}, {"status": "good", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 4}, {"status": "ok", "size": 1}, {"status": "good", "size": 4}, {"status": "ok", "size": 1}, {"status": "good", "size": 2}, {"status": "good", "size": 1}, {"status": "good", "size": 1}, {"status": "good", "size": 3}, {"status": "good", "size": 3}, {"status": "bad", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 1}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "good", "size": 1}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 1}, {"status": "bad", "size": 4}, {"status": "ok", "size": 3}, {"status": "good", "size": 1}, {"status": "bad", "size": 4}, {"status": "bad", "size": 4}, {"status": "good", "size": 4}, {"status": "bad", "size": 1}, {"status": "ok", "size": 3}, {"status": "good", "size": 3}, {"status": "ok", "size": 3}, {"status": "ok", "size": 4}, {"status": "good", "size": 4}, {"status": "ok", "size": 3}, {"status": "ok", "size": 3}, {"status": "good", "size": 2}, {"status": "bad", "size": 3}, {"status": "ok", "size": 4}, {"status": "bad", "size": 3}, {"status": "ok", "size": 2}, {"status": "ok", "size": 4}, {"status": "good", "size": 3}, {"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 3}, {"status": "bad", "size": 4}, {"status": "bad", "size": 4}, {"status": "ok", "size": 3}, {"status": "ok", "size": 4}, {"status": "good", "size": 3}, {"status": "good", "size": 4}, {"status": "ok", "size": 4}, {"status": "good", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 2}, {"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "bad", "size": 4}, {"status": "good", "size": 1}, {"status": "bad", "size": 4}, {"status": "ok", "size": 3}, {"status": "bad", "size": 4}, {"status": "good", "size": 4}, {"status": "bad", "size": 1}, {"status": "bad", "size": 1}, {"status": "ok", "size": 1}, {"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "ok", "size": 1}, {"status": "bad", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}, {"status": "bad", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "good", "size": 1}, {"status": "ok", "size": 2}, {"status": "bad", "size": 3}, {"status": "ok", "size": 2}, {"status": "bad", "size": 3}, {"status": "good", "size": 2}, {"status": "bad", "size": 2}, {"status": "bad", "size": 4}, {"status": "ok", "size": 3}, {"status": "ok", "size": 1}, {"status": "ok", "size": 4}, {"status": "ok", "size": 4}, {"status": "bad", "size": 2}, {"status": "bad", "size": 1}, {"status": "good", "size": 4}, {"status": "good", "size": 2}, {"status": "bad", "size": 3}, {"status": "ok", "size": 4}, {"status": "good", "size": 4}, {"status": "ok", "size": 2}, {"status": "ok", "size": 1}, {"status": "bad", "size": 3}, {"status": "bad", "size": 4}, {"status": "good", "size": 3}, {"status": "ok", "size": 1}, {"status": "good", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 1}, {"status": "good", "size": 1}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "good", "size": 4}, {"status": "bad", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 2}, {"status": "ok", "size": 2}, {"status": "good", "size": 3}, {"status": "ok", "size": 2}, {"status": "bad", "size": 4}, {"status": "good", "size": 1}, {"status": "good", "size": 4}, {"status": "ok", "size": 2}, {"status": "ok", "size": 3}, {"status": "bad", "size": 4}, {"status": "bad", "size": 4}, {"status": "good", "size": 3}, {"status": "bad", "size": 1}, {"status": "good", "size": 4}, {"status": "ok", "size": 4}, {"status": "good", "size": 1}, {"status": "ok", "size": 1}, {"status": "good", "size": 4}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}, {"status": "bad", "size": 3}, {"status": "bad", "size": 2}, {"status": "good", "size": 4}, {"status": "good", "size": 3}, {"status": "bad", "size": 1}, {"status": "good", "size": 4}, {"status": "ok", "size": 3}, {"status": "good", "size": 4}, {"status": "good", "size": 2}, {"status": "bad", "size": 2}, {"status": "bad", "size": 3}, {"status": "good", "size": 4}, {"status": "good", "size": 2}]}
###
# Module for a d3+backbone-powered force directed graph
# with collapsible hierarchical clustering
###
class ForceClusterHierarchy extends Backbone.View
radiusAttr: "radius"
colorAttr: "color"
initialize: ->
@collection.on("change:#{@radiusAttr}", => @setRadius())
@collection.on("change:#{@colorAttr}", => @setColor())
# d3 init stuff
@iticks = 100
@strokeWidth = 2
@innerNodeColor = "#000"
@padding = 2
@margin =
top: 0
right: 0
bottom: 0
left: 0
[w, h] = [@options.width, @options.height]
w ?= $(window).width() - 5
h ?= $(window).height() - 5
@width = w - @margin.left - @margin.right
@height = h - @margin.top - @margin.bottom
@center =
x: @width * 0.5
y: @height * 0.5
@vis = d3.select(@el).append("svg")
.attr("width", @width + @margin.left + @margin.right)
.attr("height", @height + @margin.top + @margin.bottom)
.append("g")
.attr("transform",
"translate(#{@margin.left}, #{@margin.top})")
d3.select("body").call(d3.behavior.zoom().scaleExtent([1, 60]).on("zoom", @zoom))
@force = d3.layout.force()
.gravity(0)
.charge(-0.1)
.friction(0.01)
.size([@width, @height])
.on("tick", @tick)
# backbone init stuff
@collection.on("change", => @render())
@collection.fetch
success: =>
@render()
# tick for a bit, then expand
# (to correctly place child nodes)
@force.tick() for i in [1..@iticks]
@expand(d) for d in @collection.models
render: ->
self = @
for model in @collection.models
# reset color and radius
@setColor(model)
@setRadius(model)
# spawn nodes from parent"s center
parent = @parentOf(model)
if (not model.x?) and (parent?) and (parent.x?)
model.x = parent.x
model.y = parent.y
@force
.nodes(@collection.models)
.start()
@nodes = @vis.selectAll("circle")
.data(@collection.models, (d) -> d.cid)
@nodes.enter().append("circle")
.attr("r", (d) -> d.radius)
.style("fill", (d) -> self.setFill(d))
.style("stroke", (d) -> d.color)
.style("stroke-width", @strokeWidth)
.style("fill-opacity", (d) -> self.setOpacity(d))
@nodes.exit().remove()
@nodes
.on "mouseover", (d) ->
transition = d3.select(@).transition().duration(250)
transition.style("fill", (d) -> self.setFill(d, true))
transition.style("fill-opacity", (d) -> self.setOpacity(d, true))
.on "mouseout", (d) ->
transition = d3.select(@).transition().duration(250)
transition.style("fill", (d) -> self.setFill(d))
transition.style("fill-opacity", (d) -> self.setOpacity(d))
.on "click", (d) ->
if not self.isLeaf(d)
self.texpand(d)
transition = d3.select(@).transition().duration(250)
transition.style("fill-opacity", (d) -> self.setOpacity(d))
zoom: =>
@vis.attr("transform",
"translate(#{d3.event.translate})
scale(#{d3.event.scale})")
setColor: (d) ->
v = d.get(@colorAttr)
return d.color = @colorFn(v)
setRadius: (d, v) ->
v ?= d.get(@radiusAttr)
d.radius = @radiusFn(v)
d.bradius = d.radius - @strokeWidth - @padding
d.cradius = d.radius + @padding + @strokeWidth
return d.radius
setFill: (d, mouseIsOver = false) ->
if mouseIsOver
d3.rgb(d.color).darker()
else if @isLeaf(d)
d.color
else
@innerNodeColor
setOpacity: (d, mouseIsOver = false) ->
if @isExpanded(d)
if mouseIsOver
"0.25"
else
"0"
else if @isLeaf(d)
"1"
else
"0.25"
radiusFn: (d) -> d
colorFn: (d) -> d
parentOf: (d) -> d?.parent
childrenOf: (d) -> d?.children
clusterOf: (d) -> d?.parent?.children
starOf: (d) -> d?.parent?.biggestChild
expand: (d) ->
if not @isLeaf(d)
d.expand()
# toggle expand
texpand: (d) ->
if @isExpanded(d)
d.collapse()
else
d.expand()
collapse: (d) -> d.collapse()
isExpanded: (d) -> d.get("expanded")
isLeaf: (d) -> not @childrenOf(d)?
isRoot: (d) -> not @parentOf(d)?
tick: (e) =>
@nodes
.each((d) =>
if @isExpanded(d)
@drawBoundary(d))
.each((d) =>
@contract(d, 10 * e.alpha * e.alpha))
.each((d) => @collide(d, 0.5))
@nodes
.attr("r", (d) -> d.radius)
.attr("cx", (d) -> d.x)
.attr("cy", (d) -> d.y)
drawBoundary: (d) ->
[x1, y1] = [88888, 88888]
[x2, y2] = [-88888, -88888]
for child in @childrenOf(d)
[cx1, cy1] = [child.x - child.cradius, child.y - child.cradius]
[cx2, cy2] = [child.x + child.cradius, child.y + child.cradius]
if cx1 < x1 then x1 = cx1
if cy1 < y1 then y1 = cy1
if cx2 > x2 then x2 = cx2
if cy2 > y2 then y2 = cy2
l = Math.max(x2 - x1, y2 - y1)
#r = Math.sqrt(l * l)
r = l / 2
@setRadius(d, r)
d.x = (x2 + x1) * 0.5
d.y = (y2 + y1) * 0.5
contract: (d, alpha) ->
star = @starOf(d)
if d is star
return
x = d.x - star.x
y = d.y - star.y
l = Math.sqrt(x * x + y * y)
r = d.cradius + star.cradius
if l isnt r
l = (l - r) / l * alpha
[x, y] = [x * l, y * l]
[d.x, d.y] = [d.x - x, d.y - y]
[star.x, star.y] = [star.x + x, star.y + y]
collide: (d, alpha) ->
# node - node cluster collisions
for neighbor in @clusterOf(d)
if neighbor is d
continue
x = d.x - neighbor.x
y = d.y - neighbor.y
l = Math.sqrt(x * x + y * y)
r = d.cradius + neighbor.cradius
diff = l - r
if diff < 0
l = diff / l * alpha
[x, y] = [x * l, y * l]
[d.x, d.y] = [d.x - x, d.y - y]
[neighbor.x, neighbor.y] = [neighbor.x + x, neighbor.y + y]
# node - boundary collisions
if @isRoot(@parentOf(d))
d.x = Math.max(d.cradius, Math.min(@width - d.cradius, d.x))
d.y = Math.max(d.cradius, Math.min(@height - d.cradius, d.y))
return
# bounding circle collisions
x = parent.x - d.x
y = parent.y - d.y
l = Math.sqrt(x * x + y * y) + d.cradius
diff = parent.bradius - l
if diff < 0
l = diff / l * alpha
[x, y] = [x * l, y * l]
[d.x, d.y] = [d.x - x, d.y - y]
[parent.x, parent.y] = [parent.x + x, parent.y + y]
# expose module to browser
window.ForceClusterHierarchy = ForceClusterHierarchy if window?
/*
# Module for a d3+backbone-powered force directed graph
# with collapsible hierarchical clustering
*/
(function() {
var ForceClusterHierarchy,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
ForceClusterHierarchy = (function(_super) {
__extends(ForceClusterHierarchy, _super);
function ForceClusterHierarchy() {
this.tick = __bind(this.tick, this);
this.zoom = __bind(this.zoom, this);
return ForceClusterHierarchy.__super__.constructor.apply(this, arguments);
}
ForceClusterHierarchy.prototype.radiusAttr = "radius";
ForceClusterHierarchy.prototype.colorAttr = "color";
ForceClusterHierarchy.prototype.initialize = function() {
var h, w, _ref,
_this = this;
this.collection.on("change:" + this.radiusAttr, function() {
return _this.setRadius();
});
this.collection.on("change:" + this.colorAttr, function() {
return _this.setColor();
});
this.iticks = 100;
this.strokeWidth = 2;
this.innerNodeColor = "#000";
this.padding = 2;
this.margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
_ref = [this.options.width, this.options.height], w = _ref[0], h = _ref[1];
if (w == null) {
w = $(window).width() - 5;
}
if (h == null) {
h = $(window).height() - 5;
}
this.width = w - this.margin.left - this.margin.right;
this.height = h - this.margin.top - this.margin.bottom;
this.center = {
x: this.width * 0.5,
y: this.height * 0.5
};
this.vis = d3.select(this.el).append("svg").attr("width", this.width + this.margin.left + this.margin.right).attr("height", this.height + this.margin.top + this.margin.bottom).append("g").attr("transform", "translate(" + this.margin.left + ", " + this.margin.top + ")");
d3.select("body").call(d3.behavior.zoom().scaleExtent([1, 60]).on("zoom", this.zoom));
this.force = d3.layout.force().gravity(0).charge(-0.1).friction(0.01).size([this.width, this.height]).on("tick", this.tick);
this.collection.on("change", function() {
return _this.render();
});
return this.collection.fetch({
success: function() {
var d, i, _i, _j, _len, _ref1, _ref2, _results;
_this.render();
for (i = _i = 1, _ref1 = _this.iticks; 1 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 1 <= _ref1 ? ++_i : --_i) {
_this.force.tick();
}
_ref2 = _this.collection.models;
_results = [];
for (_j = 0, _len = _ref2.length; _j < _len; _j++) {
d = _ref2[_j];
_results.push(_this.expand(d));
}
return _results;
}
});
};
ForceClusterHierarchy.prototype.render = function() {
var model, parent, self, _i, _len, _ref;
self = this;
_ref = this.collection.models;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
model = _ref[_i];
this.setColor(model);
this.setRadius(model);
parent = this.parentOf(model);
if ((!(model.x != null)) && (parent != null) && (parent.x != null)) {
model.x = parent.x;
model.y = parent.y;
}
}
this.force.nodes(this.collection.models).start();
this.nodes = this.vis.selectAll("circle").data(this.collection.models, function(d) {
return d.cid;
});
this.nodes.enter().append("circle").attr("r", function(d) {
return d.radius;
}).style("fill", function(d) {
return self.setFill(d);
}).style("stroke", function(d) {
return d.color;
}).style("stroke-width", this.strokeWidth).style("fill-opacity", function(d) {
return self.setOpacity(d);
});
this.nodes.exit().remove();
return this.nodes.on("mouseover", function(d) {
var transition;
transition = d3.select(this).transition().duration(250);
transition.style("fill", function(d) {
return self.setFill(d, true);
});
return transition.style("fill-opacity", function(d) {
return self.setOpacity(d, true);
});
}).on("mouseout", function(d) {
var transition;
transition = d3.select(this).transition().duration(250);
transition.style("fill", function(d) {
return self.setFill(d);
});
return transition.style("fill-opacity", function(d) {
return self.setOpacity(d);
});
}).on("click", function(d) {
var transition;
if (!self.isLeaf(d)) {
self.texpand(d);
transition = d3.select(this).transition().duration(250);
return transition.style("fill-opacity", function(d) {
return self.setOpacity(d);
});
}
});
};
ForceClusterHierarchy.prototype.zoom = function() {
return this.vis.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
};
ForceClusterHierarchy.prototype.setColor = function(d) {
var v;
v = d.get(this.colorAttr);
return d.color = this.colorFn(v);
};
ForceClusterHierarchy.prototype.setRadius = function(d, v) {
if (v == null) {
v = d.get(this.radiusAttr);
}
d.radius = this.radiusFn(v);
d.bradius = d.radius - this.strokeWidth - this.padding;
d.cradius = d.radius + this.padding + this.strokeWidth;
return d.radius;
};
ForceClusterHierarchy.prototype.setFill = function(d, mouseIsOver) {
if (mouseIsOver == null) {
mouseIsOver = false;
}
if (mouseIsOver) {
return d3.rgb(d.color).darker();
} else if (this.isLeaf(d)) {
return d.color;
} else {
return this.innerNodeColor;
}
};
ForceClusterHierarchy.prototype.setOpacity = function(d, mouseIsOver) {
if (mouseIsOver == null) {
mouseIsOver = false;
}
if (this.isExpanded(d)) {
if (mouseIsOver) {
return "0.25";
} else {
return "0";
}
} else if (this.isLeaf(d)) {
return "1";
} else {
return "0.25";
}
};
ForceClusterHierarchy.prototype.radiusFn = function(d) {
return d;
};
ForceClusterHierarchy.prototype.colorFn = function(d) {
return d;
};
ForceClusterHierarchy.prototype.parentOf = function(d) {
return d != null ? d.parent : void 0;
};
ForceClusterHierarchy.prototype.childrenOf = function(d) {
return d != null ? d.children : void 0;
};
ForceClusterHierarchy.prototype.clusterOf = function(d) {
var _ref;
return d != null ? (_ref = d.parent) != null ? _ref.children : void 0 : void 0;
};
ForceClusterHierarchy.prototype.starOf = function(d) {
var _ref;
return d != null ? (_ref = d.parent) != null ? _ref.biggestChild : void 0 : void 0;
};
ForceClusterHierarchy.prototype.expand = function(d) {
if (!this.isLeaf(d)) {
return d.expand();
}
};
ForceClusterHierarchy.prototype.texpand = function(d) {
if (this.isExpanded(d)) {
return d.collapse();
} else {
return d.expand();
}
};
ForceClusterHierarchy.prototype.collapse = function(d) {
return d.collapse();
};
ForceClusterHierarchy.prototype.isExpanded = function(d) {
return d.get("expanded");
};
ForceClusterHierarchy.prototype.isLeaf = function(d) {
return !(this.childrenOf(d) != null);
};
ForceClusterHierarchy.prototype.isRoot = function(d) {
return !(this.parentOf(d) != null);
};
ForceClusterHierarchy.prototype.tick = function(e) {
var _this = this;
this.nodes.each(function(d) {
if (_this.isExpanded(d)) {
return _this.drawBoundary(d);
}
}).each(function(d) {
return _this.contract(d, 10 * e.alpha * e.alpha);
}).each(function(d) {
return _this.collide(d, 0.5);
});
return this.nodes.attr("r", function(d) {
return d.radius;
}).attr("cx", function(d) {
return d.x;
}).attr("cy", function(d) {
return d.y;
});
};
ForceClusterHierarchy.prototype.drawBoundary = function(d) {
var child, cx1, cx2, cy1, cy2, l, r, x1, x2, y1, y2, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4;
_ref = [88888, 88888], x1 = _ref[0], y1 = _ref[1];
_ref1 = [-88888, -88888], x2 = _ref1[0], y2 = _ref1[1];
_ref2 = this.childrenOf(d);
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
child = _ref2[_i];
_ref3 = [child.x - child.cradius, child.y - child.cradius], cx1 = _ref3[0], cy1 = _ref3[1];
_ref4 = [child.x + child.cradius, child.y + child.cradius], cx2 = _ref4[0], cy2 = _ref4[1];
if (cx1 < x1) {
x1 = cx1;
}
if (cy1 < y1) {
y1 = cy1;
}
if (cx2 > x2) {
x2 = cx2;
}
if (cy2 > y2) {
y2 = cy2;
}
}
l = Math.max(x2 - x1, y2 - y1);
r = l / 2;
this.setRadius(d, r);
d.x = (x2 + x1) * 0.5;
return d.y = (y2 + y1) * 0.5;
};
ForceClusterHierarchy.prototype.contract = function(d, alpha) {
var l, r, star, x, y, _ref, _ref1, _ref2;
star = this.starOf(d);
if (d === star) {
return;
}
x = d.x - star.x;
y = d.y - star.y;
l = Math.sqrt(x * x + y * y);
r = d.cradius + star.cradius;
if (l !== r) {
l = (l - r) / l * alpha;
_ref = [x * l, y * l], x = _ref[0], y = _ref[1];
_ref1 = [d.x - x, d.y - y], d.x = _ref1[0], d.y = _ref1[1];
return _ref2 = [star.x + x, star.y + y], star.x = _ref2[0], star.y = _ref2[1], _ref2;
}
};
ForceClusterHierarchy.prototype.collide = function(d, alpha) {
var diff, l, neighbor, r, x, y, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
_ref = this.clusterOf(d);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
neighbor = _ref[_i];
if (neighbor === d) {
continue;
}
x = d.x - neighbor.x;
y = d.y - neighbor.y;
l = Math.sqrt(x * x + y * y);
r = d.cradius + neighbor.cradius;
diff = l - r;
if (diff < 0) {
l = diff / l * alpha;
_ref1 = [x * l, y * l], x = _ref1[0], y = _ref1[1];
_ref2 = [d.x - x, d.y - y], d.x = _ref2[0], d.y = _ref2[1];
_ref3 = [neighbor.x + x, neighbor.y + y], neighbor.x = _ref3[0], neighbor.y = _ref3[1];
}
}
if (this.isRoot(this.parentOf(d))) {
d.x = Math.max(d.cradius, Math.min(this.width - d.cradius, d.x));
d.y = Math.max(d.cradius, Math.min(this.height - d.cradius, d.y));
return;
}
x = parent.x - d.x;
y = parent.y - d.y;
l = Math.sqrt(x * x + y * y) + d.cradius;
diff = parent.bradius - l;
if (diff < 0) {
l = diff / l * alpha;
_ref4 = [x * l, y * l], x = _ref4[0], y = _ref4[1];
_ref5 = [d.x - x, d.y - y], d.x = _ref5[0], d.y = _ref5[1];
return _ref6 = [parent.x + x, parent.y + y], parent.x = _ref6[0], parent.y = _ref6[1], _ref6;
}
};
return ForceClusterHierarchy;
})(Backbone.View);
if (typeof window !== "undefined" && window !== null) {
window.ForceClusterHierarchy = ForceClusterHierarchy;
}
}).call(this);
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #222;
margin: 0;
}
</style>
<body>
<div class="vis">
</div>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.1.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore.js"></script>
<script type="text/javascript" src="http://backbonejs.org/backbone.js"></script>
<script type="text/javascript" src="http://d3js.org/d3.v2.js"></script>
<script src="js/network-status.js" type="text/javascript"></script>
<script src="js/fch.js" type="text/javascript"></script>
<script src="js/main.js" type="text/javascript"></script>
###
# Example usage of the network status collection
# and force cluster hierarchy together
###
STATUS_COLORS =
{
'good': '#33cc33'
'ok': '#cccc33'
'bad': '#cc3333'
}
collection = new StatusNodeCollection()
class NetworkStatusHierarchy extends ForceClusterHierarchy
radiusAttr: 'size'
colorAttr: 'status'
colorFn: (d) -> STATUS_COLORS[d]
nsh = new NetworkStatusHierarchy
el: '.vis'
collection: collection
/*
# Example usage of the network status collection
# and force cluster hierarchy together
*/
(function() {
var NetworkStatusHierarchy, STATUS_COLORS, collection, nsh,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
STATUS_COLORS = {
'good': '#33cc33',
'ok': '#cccc33',
'bad': '#cc3333'
};
collection = new StatusNodeCollection();
NetworkStatusHierarchy = (function(_super) {
__extends(NetworkStatusHierarchy, _super);
function NetworkStatusHierarchy() {
return NetworkStatusHierarchy.__super__.constructor.apply(this, arguments);
}
NetworkStatusHierarchy.prototype.radiusAttr = 'size';
NetworkStatusHierarchy.prototype.colorAttr = 'status';
NetworkStatusHierarchy.prototype.colorFn = function(d) {
return STATUS_COLORS[d];
};
return NetworkStatusHierarchy;
})(ForceClusterHierarchy);
nsh = new NetworkStatusHierarchy({
el: '.vis',
collection: collection
});
}).call(this);
###
# Module for network status models
###
# require backbone if the module is running through node
#if require?
# Backbone = require 'backbone'
STATUS_CODES =
'good': 0
'ok': 1
'bad': 2
class StatusNode extends Backbone.Model
initialize: ->
super()
if @has 'parent'
@parent = @get 'parent'
@unset 'parent', silent: true
if not @has 'size'
@set size: 0
if not @has 'status'
@set status: 'good'
getNodes: ->
# abstract
class StatusInnerNode extends StatusNode
initialize: ->
super()
if not @has 'expanded'
@set expanded: false
# add children nodes recursively
childConfigs = @get 'children'
@children = []
for childConfig in childConfigs
childConfig.parent = @
child =
if childConfig.children
new StatusInnerNode(childConfig)
else
new StatusLeafNode(childConfig)
sizeCb = =>
size = @get 'size'
previousChildSize = child.previous 'size'
childSize = child.get 'size'
@set size: size + (childSize - previousChildSize)
if child isnt @biggestChild and
child.get('size') > @biggestChild.get('size')
@biggestChild = child
child.on('change:size', sizeCb)
statusCb = =>
childStatus = (child.get 'status')
code = STATUS_CODES[@get 'status']
childCode = STATUS_CODES[childStatus]
@set status: childStatus if (childCode > code)
child.on('change:status', statusCb)
# init parent attrs from child attrs
@set size: (@get 'size') + (child.get 'size')
if (not @biggestChild?) or
(child.get 'size') > (@biggestChild?.get 'size')
@biggestChild = child
statusCb() # status init is the same as status callback
@children.push(child)
@unset 'children', silent: true
expand: ->
@set expanded: true
texpand: ->
@set expanded: not @get('expanded')
collapse: ->
@set expanded: false
expandAll: ->
@expand()
for child in @children
if child instanceof StatusInnerNode
child.expandAll()
collapse: ->
@set expanded: false
getNodes: ->
nodes = if @parent? then [@] else []
if @get('expanded')
for child in @children
nodes.push.apply(nodes, child.getNodes())
return nodes
class StatusLeafNode extends StatusNode
getNodes: ->
return [@]
class StatusNodeCollection extends Backbone.Collection
### Collection of currently expanded status nodes ###
#url: 'data/network-status.json'
url: 'data/centralised-network-1.json'
#url: 'data/branched-network-1.json'
model: StatusNode
initialize: ->
@.on 'change', =>
@reset(@root.getNodes())
parse: (response) ->
@root = new StatusInnerNode(response)
@root.expand()
nodes = @root.getNodes()
@add(nodes)
return nodes
expandAll: ->
@root.expandAll()
@reset(@root.getNodes())
# expose module appropriately (based on whether
# the module is running through node or the browser)
#if exports?
# exports.StatusNodeCollection = StatusNodeCollection
#else
window.StatusNodeCollection = StatusNodeCollection
/*
# Module for network status models
*/
(function() {
var STATUS_CODES, StatusInnerNode, StatusLeafNode, StatusNode, StatusNodeCollection,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
STATUS_CODES = {
'good': 0,
'ok': 1,
'bad': 2
};
StatusNode = (function(_super) {
__extends(StatusNode, _super);
function StatusNode() {
return StatusNode.__super__.constructor.apply(this, arguments);
}
StatusNode.prototype.initialize = function() {
StatusNode.__super__.initialize.call(this);
if (this.has('parent')) {
this.parent = this.get('parent');
this.unset('parent', {
silent: true
});
}
if (!this.has('size')) {
this.set({
size: 0
});
}
if (!this.has('status')) {
return this.set({
status: 'good'
});
}
};
StatusNode.prototype.getNodes = function() {};
return StatusNode;
})(Backbone.Model);
StatusInnerNode = (function(_super) {
__extends(StatusInnerNode, _super);
function StatusInnerNode() {
return StatusInnerNode.__super__.constructor.apply(this, arguments);
}
StatusInnerNode.prototype.initialize = function() {
var child, childConfig, childConfigs, sizeCb, statusCb, _i, _len, _ref,
_this = this;
StatusInnerNode.__super__.initialize.call(this);
if (!this.has('expanded')) {
this.set({
expanded: false
});
}
childConfigs = this.get('children');
this.children = [];
for (_i = 0, _len = childConfigs.length; _i < _len; _i++) {
childConfig = childConfigs[_i];
childConfig.parent = this;
child = childConfig.children ? new StatusInnerNode(childConfig) : new StatusLeafNode(childConfig);
sizeCb = function() {
var childSize, previousChildSize, size;
size = _this.get('size');
previousChildSize = child.previous('size');
childSize = child.get('size');
_this.set({
size: size + (childSize - previousChildSize)
});
if (child !== _this.biggestChild && child.get('size') > _this.biggestChild.get('size')) {
return _this.biggestChild = child;
}
};
child.on('change:size', sizeCb);
statusCb = function() {
var childCode, childStatus, code;
childStatus = child.get('status');
code = STATUS_CODES[_this.get('status')];
childCode = STATUS_CODES[childStatus];
if (childCode > code) {
return _this.set({
status: childStatus
});
}
};
child.on('change:status', statusCb);
this.set({
size: (this.get('size')) + (child.get('size'))
});
if ((!(this.biggestChild != null)) || (child.get('size')) > ((_ref = this.biggestChild) != null ? _ref.get('size') : void 0)) {
this.biggestChild = child;
}
statusCb();
this.children.push(child);
}
return this.unset('children', {
silent: true
});
};
StatusInnerNode.prototype.expand = function() {
return this.set({
expanded: true
});
};
StatusInnerNode.prototype.texpand = function() {
return this.set({
expanded: !this.get('expanded')
});
};
StatusInnerNode.prototype.collapse = function() {
return this.set({
expanded: false
});
};
StatusInnerNode.prototype.expandAll = function() {
var child, _i, _len, _ref, _results;
this.expand();
_ref = this.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (child instanceof StatusInnerNode) {
_results.push(child.expandAll());
} else {
_results.push(void 0);
}
}
return _results;
};
StatusInnerNode.prototype.collapse = function() {
return this.set({
expanded: false
});
};
StatusInnerNode.prototype.getNodes = function() {
var child, nodes, _i, _len, _ref;
nodes = this.parent != null ? [this] : [];
if (this.get('expanded')) {
_ref = this.children;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
nodes.push.apply(nodes, child.getNodes());
}
}
return nodes;
};
return StatusInnerNode;
})(StatusNode);
StatusLeafNode = (function(_super) {
__extends(StatusLeafNode, _super);
function StatusLeafNode() {
return StatusLeafNode.__super__.constructor.apply(this, arguments);
}
StatusLeafNode.prototype.getNodes = function() {
return [this];
};
return StatusLeafNode;
})(StatusNode);
StatusNodeCollection = (function(_super) {
__extends(StatusNodeCollection, _super);
function StatusNodeCollection() {
return StatusNodeCollection.__super__.constructor.apply(this, arguments);
}
/* Collection of currently expanded status nodes
*/
StatusNodeCollection.prototype.url = 'data/centralised-network-1.json';
StatusNodeCollection.prototype.model = StatusNode;
StatusNodeCollection.prototype.initialize = function() {
var _this = this;
return this.on('change', function() {
return _this.reset(_this.root.getNodes());
});
};
StatusNodeCollection.prototype.parse = function(response) {
var nodes;
this.root = new StatusInnerNode(response);
this.root.expand();
nodes = this.root.getNodes();
this.add(nodes);
return nodes;
};
StatusNodeCollection.prototype.expandAll = function() {
this.root.expandAll();
return this.reset(this.root.getNodes());
};
return StatusNodeCollection;
})(Backbone.Collection);
window.StatusNodeCollection = StatusNodeCollection;
}).call(this);
{
"children": [
{
"children": [
{
"status": "bad",
"size": 20
},
{
"status": "good",
"size": 18
},
{
"children": [
{
"status": "bad",
"size": 8
},
{
"status": "ok",
"size": 15
}
]
}
]
},
{
"children": [
{
"status": "bad",
"size": 12
},
{
"status": "good",
"size": 10
},
{
"status": "good",
"size": 25
},
{
"status": "ok",
"size": 8
}
]
},
{
"children": [
{
"status": "bad",
"size": 20
},
{
"status": "good",
"size": 18
},
{
"children": [
{
"status": "bad",
"size": 8
},
{
"status": "ok",
"size": 15
}
]
}
]
},
{
"children": [
{
"status": "bad",
"size": 12
},
{
"status": "good",
"size": 10
},
{
"status": "good",
"size": 25
},
{
"status": "ok",
"size": 8
}
]
},
{
"children": [
{
"status": "bad",
"size": 20
},
{
"status": "good",
"size": 18
},
{
"children": [
{
"status": "bad",
"size": 8
},
{
"status": "ok",
"size": 15
}
]
}
]
},
{
"children": [
{
"status": "bad",
"size": 12
},
{
"status": "good",
"size": 10
},
{
"status": "good",
"size": 25
},
{
"status": "ok",
"size": 8
}
]
},
{
"children": [
{
"status": "bad",
"size": 20
},
{
"status": "good",
"size": 18
},
{
"children": [
{
"status": "bad",
"size": 8
},
{
"status": "ok",
"size": 15
}
]
}
]
},
{
"children": [
{
"status": "bad",
"size": 12
},
{
"status": "good",
"size": 10
},
{
"status": "good",
"size": 25
},
{
"status": "ok",
"size": 8
}
]
},
{
"children": [
{
"status": "bad",
"size": 20
},
{
"status": "good",
"size": 18
},
{
"children": [
{
"status": "bad",
"size": 8
},
{
"status": "ok",
"size": 15
}
]
}
]
},
{
"children": [
{
"status": "bad",
"size": 12
},
{
"status": "good",
"size": 10
},
{
"status": "good",
"size": 25
},
{
"status": "ok",
"size": 8
}
]
},
{
"children": [
{
"status": "bad",
"size": 20
},
{
"status": "good",
"size": 18
},
{
"children": [
{
"status": "bad",
"size": 8
},
{
"status": "ok",
"size": 15
}
]
}
]
},
{
"children": [
{
"status": "bad",
"size": 12
},
{
"status": "good",
"size": 10
},
{
"status": "good",
"size": 25
},
{
"status": "ok",
"size": 8
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment