Merge remote-tracking branch 'origin/rafl' into tim-20120930-sizeme
[p5sagit/Devel-Size.git] / static / public / treemap.js
1 // based closely on http://thejit.org/static/v20/Jit/Examples/Treemap/example2.html
2 var labelType, useGradients, nativeTextSupport, animate;
3
4 (function() {
5   var ua = navigator.userAgent,
6       iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i),
7       typeOfCanvas = typeof HTMLCanvasElement,
8       nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'),
9       textSupport = nativeCanvasSupport 
10         && (typeof document.createElement('canvas').getContext('2d').fillText == 'function');
11   //I'm setting this based on the fact that ExCanvas provides text support for IE
12   //and that as of today iPhone/iPad current text support is lame
13   labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML';
14   nativeTextSupport = labelType == 'Native';
15   useGradients = nativeCanvasSupport;
16   animate = !(iStuff || !nativeCanvasSupport);
17   console.log({ "labelType":labelType, "useGradients":useGradients, "nativeTextSupport":nativeTextSupport });
18 })();
19
20 var Log = {
21   elem: false,
22   write: function(text){
23     if (!this.elem) 
24       this.elem = document.getElementById('log');
25     this.elem.innerHTML = text;
26     this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px';
27   }
28 };
29
30 /**
31  * Convert number of bytes into human readable format
32  *
33  * @param integer bytes     Number of bytes to convert
34  * @param integer precision Number of digits after the decimal separator
35  * @return string
36  * via http://codeaid.net/javascript/convert-size-in-bytes-to-human-readable-format-(javascript)
37  */
38 function bytesToSize(bytes, precision) {
39     var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
40     var posttxt = 0;
41     while( bytes >= 1024 ) {
42         posttxt++;
43         bytes = bytes / 1024;
44     }
45     var num = (posttxt) ? Number(bytes).toFixed(precision) : bytes;
46     return num + " " + sizes[posttxt];
47 }
48
49
50 // http://stackoverflow.com/questions/5199901/how-to-sort-an-associative-array-by-its-values-in-javascript
51 function bySortedValue(obj, comparitor, callback, context) {
52     var tuples = [];
53     for (var key in obj) {
54         if (obj.hasOwnProperty(key)) {
55             tuples.push([key, obj[key]]);
56         }
57     }
58
59     tuples.sort(comparitor);
60
61     if (callback) {
62         var length = tuples.length;
63         while (length--) callback.call(context, tuples[length][0], tuples[length][1]);
64     }
65     return tuples;
66 }
67
68
69
70 function request_jit_tree(nodeId, level, depth, onComplete){
71     var params = { };//logarea: 0 };
72     jQuery.getJSON('jit_tree/'+nodeId+'/'+depth, params, onComplete);
73 }
74
75
76 function init(){
77   var levelsToShow = 2;
78   //init TreeMap
79   var tm = new $jit.TM.Squarified({
80     //where to inject the visualization
81     injectInto: 'infovis',
82     //show only one tree level
83     levelsToShow: levelsToShow,
84     //parent box title heights
85     titleHeight: 14,
86     //enable animations
87     animate: animate,
88     //box offsets
89     offset: 1,
90     //use canvas text
91     // XXX disabled to allow the onMouseEnter/onMouseLeave Events to fire to set the blue border
92     XXX_Label: {
93       type: labelType,
94       size: 10,
95       family: 'Tahoma, Verdana, Arial'
96     },
97     //enable specific canvas styles
98     //when rendering nodes
99     Node: {
100       CanvasStyles: {
101         shadowBlur: 0,
102         shadowColor: '#000'
103       }
104     },
105     //Attach left and right click events
106     Events: {
107       enable: true,
108       onClick: function(node) {
109         if(node) tm.enter(node);
110       },
111       onRightClick: function() {
112         tm.out();
113       },
114       //change node styles and canvas styles
115       //when hovering a node
116       onMouseEnter: function(node, eventInfo) {
117         if(node) {
118           //add node selected styles and replot node
119           node.setCanvasStyle('shadowBlur', 7);
120           node.setData('color', '#888');
121           tm.fx.plotNode(node, tm.canvas);
122           tm.labels.plotLabel(tm.canvas, node);
123         }
124       },
125       onMouseLeave: function(node) {
126         if(node) {
127           node.removeData('color');
128           node.removeCanvasStyle('shadowBlur');
129           tm.plot();
130         }
131       }
132     },
133     //duration of the animations
134     duration: 300,
135     //Enable tips
136     Tips: {
137       enable: true,
138       type: 'Native',
139       //add positioning offsets
140       offsetX: 20,
141       offsetY: 20,
142       //implement the onShow method to
143       //add content to the tooltip when a node
144       //is hovered
145       onShow: function(tip, node, isLeaf, domElement) {
146
147           // XXX all this needs html escaping
148         var data = node.data;
149         var html = "<div class=\"tip-title\">"
150           + (data.title ? "\""+data.title+"\"" : "")
151           + " " + data.name
152           + "</div><div class=\"tip-text\">";
153
154         html += "<br />";
155         html += sprintf("Memory use: %s<br />", bytesToSize(data.self_size+data.kids_size,2));
156         if (data.kids_size) {
157             html += sprintf("Child use:  %s<br />", bytesToSize(data.kids_size,2));
158         }
159         if (data.self_size) {
160             html += sprintf("Own use:    %s<br />", bytesToSize(data.self_size,2));
161             html += sprintf("<div style=\"color:grey\">");
162             bySortedValue(data.leaves,
163                 function(a, b) { return a[1] - b[1] },
164                 function(k, v) { html += sprintf(" %9s: %s<br />", k, bytesToSize(v,2));
165             });
166             html += sprintf("</div>");
167         }
168         html += "<br />";
169
170
171     if (0) {
172         html += sprintf("Attributes:<br />");
173         bySortedValue(data.attr,
174             function(a, b) { return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0 },
175             function(k, v) { html += sprintf(" %10s: %5d<br />", k, v);
176         });
177         html += "<br />";
178     }
179
180         html += sprintf("<div style=\"color:grey\">");
181         if (data.child_count) {
182             //html += sprintf("Children: %d of %d<br />", data.child_count, data.kids_node_count);
183             html += sprintf("Children: %d<br />", data.kids_node_count);
184         }
185         html += sprintf("Id: %s%s<br />", node.id, data._ids_merged ? data._ids_merged : "");
186         html += sprintf("Depth: %d<br />", data.depth);
187         html += sprintf("Parent: %d<br />", data.parent_id);
188
189         //html += JSON.stringify(data.attr, undefined, 4);
190         //html += JSON.stringify(data, undefined, 4);
191         html += sprintf("</div>");
192
193         tip.innerHTML =  html; 
194       }  
195     },
196     //Implement this method for retrieving a requested  
197     //subtree that has as root a node with id = nodeId,  
198     //and level as depth. This method could also make a server-side  
199     //call for the requested subtree. When completed, the onComplete   
200     //callback method should be called.  
201     request: function(nodeId, level, onComplete){  
202             request_jit_tree(nodeId, level, levelsToShow, function(data) {
203                 console.log("Fetched node "+nodeId);
204                 console.log(data);
205                 onComplete.onComplete(nodeId, data);  
206             });
207             // XXX workaround jit bug where old tooltip is still shown till the
208             // mouse moves
209             jQuery("#_tooltip").fadeOut("fast");
210     },
211     //Add the name of the node in the corresponding label
212     //This method is called once, on label creation and only for DOM labels.
213     onCreateLabel: function(domElement, node){
214         domElement.innerHTML = node.name;
215
216         // this doesn't work with Label:{} above
217         var style = domElement.style;  
218         style.display = '';  
219         style.border = '1px solid transparent';  
220         domElement.onmouseover = function() {  
221             style.border = '1px solid #9FD4FF';  
222         };  
223         domElement.onmouseout = function() {  
224             style.border = '1px solid transparent';  
225         };  
226
227     },
228     onPlaceLabel: function(domElement, node){ },
229   });
230
231   request_jit_tree(1, 0, levelsToShow, function(data) {
232         console.log(data);
233         tm.loadJSON(data);
234         tm.refresh();
235     });
236
237     //add event to buttons
238     $jit.util.addEvent($jit.id('back'), 'click', function() { tm.out() });
239     $jit.util.addEvent($jit.id('logarea'), 'onchange', function() { tm.refresh() });
240
241 }
242
243