drill down with basic tooltip
Tim Bunce [Tue, 18 Sep 2012 21:29:20 +0000 (22:29 +0100)]
static/MemView.pl
static/public/.tm.js.swp
static/public/sprintf.js [new file with mode: 0644]
static/public/tm.js

index bd4889f..b4bb73e 100755 (executable)
@@ -27,21 +27,35 @@ get '/jit_tree/:id/:depth' => sub {
     my $depth = $self->stash('depth');
     warn "jit_tree $id $depth";
     my $jit_tree = _fetch_node($id, $depth, sub {
-        my $node=shift; $node->{data}{'$area'} = $node->{self_size}+$node->{kids_size}
+        my ($node, $children) = @_;
+        $node->{'$area'} = $node->{self_size}+$node->{kids_size};
+        $node->{child_count} = @$children if $children;
+        my $jit_node = {
+            id   => $node->{id},
+            name => $node->{name},
+            data => $node,
+        };
+        $jit_node->{children} = $children if $children;
+        return $jit_node;
     });
-    use Devel::Dwarn; Dwarn($jit_tree);
+if(1){
+    use Devel::Dwarn;
+    use Data::Dump qw(pp);
+    local $jit_tree->{children};
+    pp($jit_tree);
+}
     $self->render_json($jit_tree);
 };
 
 sub _fetch_node {
     my ($id, $depth, $transform) = @_;
     my $node = MemView->selectrow_hashref("select * from node where id = ?", undef, $id);
+    my $children;
     if ($depth && $node->{child_seqns}) {
         my @child_seqns = split /,/, $node->{child_seqns};
-        my @children = map { _fetch_node($_, $depth-1, $transform) } @child_seqns;
-        $node->{children} = \@children;
+        $children = [ map { _fetch_node($_, $depth-1, $transform) } @child_seqns ];
     }
-    $transform->($node) if $transform;
+    $node = $transform->($node, $children) if $transform;
     return $node;
 }
 
@@ -57,7 +71,7 @@ Welcome to the Mojolicious real-time web framework!
 <!DOCTYPE html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-<title>Treemap - TreeMap with on-demand nodes</title>
+<title>Perl Memory Treemap</title>
 
 <!-- CSS Files -->
 <link type="text/css" href="css/base.css" rel="stylesheet" />
@@ -70,7 +84,7 @@ Welcome to the Mojolicious real-time web framework!
 <script language="javascript" type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
 
 <!-- Example File -->
-<script language="javascript" type="text/javascript" src="tmdata.js"></script>
+<script language="javascript" type="text/javascript" src="sprintf.js"></script>
 <script language="javascript" type="text/javascript" src="tm.js"></script>
 </head>
 
@@ -79,52 +93,14 @@ Welcome to the Mojolicious real-time web framework!
 
 <div id="left-container">
 
-
-
 <div class="text">
 <h4>
-TreeMap with on-demand nodes    
+Perl Memory TreeMap
 </h4> 
-
-            This example shows how you can use the <b>request</b> controller method to create a TreeMap with on demand nodes<br /><br />
-            This example makes use of native Canvas text and shadows, but can be easily adapted to use HTML like the other examples.<br /><br />
-            There should be only one level shown at a time.<br /><br /> 
-            Clicking on a band should show a new TreeMap with its most listened albums.<br /><br />            
-            
-</div>
-
-<div id="id-list">
-<table>
-    <tr>
-        <td>
-            <label for="r-sq">Squarified </label>
-        </td>
-        <td>
-            <input type="radio" id="r-sq" name="layout" checked="checked" value="left" />
-        </td>
-    </tr>
-    <tr>
-         <td>
-            <label for="r-st">Strip </label>
-         </td>
-         <td>
-            <input type="radio" id="r-st" name="layout" value="top" />
-         </td>
-    <tr>
-         <td>
-            <label for="r-sd">SliceAndDice </label>
-          </td>
-          <td>
-            <input type="radio" id="r-sd" name="layout" value="bottom" />
-          </td>
-    </tr>
-</table>
+    Clicking on a node will show a new TreeMap with the contents of that node.<br /><br />            
 </div>
 
 <a id="back" href="#" class="theme button white">Go to Parent</a>
-
-
-<div style="text-align:center;"><a href="example2.js">See the Example Code</a></div>            
 </div>
 
 <div id="center-container">
index 24a0da4..1974839 100644 (file)
Binary files a/static/public/.tm.js.swp and b/static/public/.tm.js.swp differ
diff --git a/static/public/sprintf.js b/static/public/sprintf.js
new file mode 100644 (file)
index 0000000..78509f4
--- /dev/null
@@ -0,0 +1,183 @@
+// from https://raw.github.com/kvz/phpjs/master/functions/strings/sprintf.js\r
+function sprintf () {\r
+    // http://kevin.vanzonneveld.net\r
+    // +   original by: Ash Searle (http://hexmen.com/blog/)\r
+    // + namespaced by: Michael White (http://getsprink.com)\r
+    // +    tweaked by: Jack\r
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)\r
+    // +      input by: Paulo Freitas\r
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)\r
+    // +      input by: Brett Zamir (http://brett-zamir.me)\r
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)\r
+    // +   improved by: Dj\r
+    // *     example 1: sprintf("%01.2f", 123.1);\r
+    // *     returns 1: 123.10\r
+    // *     example 2: sprintf("[%10s]", 'monkey');\r
+    // *     returns 2: '[    monkey]'\r
+    // *     example 3: sprintf("[%'#10s]", 'monkey');\r
+    // *     returns 3: '[####monkey]'\r
+    var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g;\r
+    var a = arguments,\r
+        i = 0,\r
+        format = a[i++];\r
+\r
+    // pad()\r
+    var pad = function (str, len, chr, leftJustify) {\r
+        if (!chr) {\r
+            chr = ' ';\r
+        }\r
+        var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);\r
+        return leftJustify ? str + padding : padding + str;\r
+    };\r
+\r
+    // justify()\r
+    var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) {\r
+        var diff = minWidth - value.length;\r
+        if (diff > 0) {\r
+            if (leftJustify || !zeroPad) {\r
+                value = pad(value, minWidth, customPadChar, leftJustify);\r
+            } else {\r
+                value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);\r
+            }\r
+        }\r
+        return value;\r
+    };\r
+\r
+    // formatBaseX()\r
+    var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) {\r
+        // Note: casts negative numbers to positive ones\r
+        var number = value >>> 0;\r
+        prefix = prefix && number && {\r
+            '2': '0b',\r
+            '8': '0',\r
+            '16': '0x'\r
+        }[base] || '';\r
+        value = prefix + pad(number.toString(base), precision || 0, '0', false);\r
+        return justify(value, prefix, leftJustify, minWidth, zeroPad);\r
+    };\r
+\r
+    // formatString()\r
+    var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) {\r
+        if (precision != null) {\r
+            value = value.slice(0, precision);\r
+        }\r
+        return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);\r
+    };\r
+\r
+    // doFormat()\r
+    var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) {\r
+        var number;\r
+        var prefix;\r
+        var method;\r
+        var textTransform;\r
+        var value;\r
+\r
+        if (substring == '%%') {\r
+            return '%';\r
+        }\r
+\r
+        // parse flags\r
+        var leftJustify = false,\r
+            positivePrefix = '',\r
+            zeroPad = false,\r
+            prefixBaseX = false,\r
+            customPadChar = ' ';\r
+        var flagsl = flags.length;\r
+        for (var j = 0; flags && j < flagsl; j++) {\r
+            switch (flags.charAt(j)) {\r
+            case ' ':\r
+                positivePrefix = ' ';\r
+                break;\r
+            case '+':\r
+                positivePrefix = '+';\r
+                break;\r
+            case '-':\r
+                leftJustify = true;\r
+                break;\r
+            case "'":\r
+                customPadChar = flags.charAt(j + 1);\r
+                break;\r
+            case '0':\r
+                zeroPad = true;\r
+                break;\r
+            case '#':\r
+                prefixBaseX = true;\r
+                break;\r
+            }\r
+        }\r
+\r
+        // parameters may be null, undefined, empty-string or real valued\r
+        // we want to ignore null, undefined and empty-string values\r
+        if (!minWidth) {\r
+            minWidth = 0;\r
+        } else if (minWidth == '*') {\r
+            minWidth = +a[i++];\r
+        } else if (minWidth.charAt(0) == '*') {\r
+            minWidth = +a[minWidth.slice(1, -1)];\r
+        } else {\r
+            minWidth = +minWidth;\r
+        }\r
+\r
+        // Note: undocumented perl feature:\r
+        if (minWidth < 0) {\r
+            minWidth = -minWidth;\r
+            leftJustify = true;\r
+        }\r
+\r
+        if (!isFinite(minWidth)) {\r
+            throw new Error('sprintf: (minimum-)width must be finite');\r
+        }\r
+\r
+        if (!precision) {\r
+            precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined;\r
+        } else if (precision == '*') {\r
+            precision = +a[i++];\r
+        } else if (precision.charAt(0) == '*') {\r
+            precision = +a[precision.slice(1, -1)];\r
+        } else {\r
+            precision = +precision;\r
+        }\r
+\r
+        // grab value using valueIndex if required?\r
+        value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];\r
+\r
+        switch (type) {\r
+        case 's':\r
+            return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);\r
+        case 'c':\r
+            return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);\r
+        case 'b':\r
+            return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);\r
+        case 'o':\r
+            return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);\r
+        case 'x':\r
+            return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);\r
+        case 'X':\r
+            return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();\r
+        case 'u':\r
+            return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);\r
+        case 'i':\r
+        case 'd':\r
+            number = (+value) | 0;\r
+            prefix = number < 0 ? '-' : positivePrefix;\r
+            value = prefix + pad(String(Math.abs(number)), precision, '0', false);\r
+            return justify(value, prefix, leftJustify, minWidth, zeroPad);\r
+        case 'e':\r
+        case 'E':\r
+        case 'f': // Should handle locales (as per setlocale)\r
+        case 'F':\r
+        case 'g':\r
+        case 'G':\r
+            number = +value;\r
+            prefix = number < 0 ? '-' : positivePrefix;\r
+            method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];\r
+            textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];\r
+            value = prefix + Math.abs(number)[method](precision);\r
+            return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();\r
+        default:\r
+            return substring;\r
+        }\r
+    };\r
+\r
+    return format.replace(regex, doFormat);\r
+}\r
index 89b0fb6..5f5b36d 100644 (file)
@@ -99,15 +99,22 @@ function init(){
         var html = "<div class=\"tip-title\">" + node.name 
           + "</div><div class=\"tip-text\">";
         var data = node.data;
-        if(data.artist) {
-          html += "Artist: " + data.artist + "<br />";
-        }
-        if(data.playcount) {
-          html += "Play count: " + data.playcount;
-        }
-        if(data.image) {
-          html += "<img src=\""+ data.image +"\" class=\"album\" />";
+
+    //"child_seqns"     => 4,
+    //"depth"           => 2,
+    //"id"              => 3,
+    //"kids_node_count" => 4426,
+    //"kids_size"       => 560058,
+    //"name"            => "SV(PVHV)",
+    //"parent_seqn"     => 2,
+    //"self_size"       => 1080,
+
+        html += sprintf("Name: %s<br />\n", data.name);
+        html += sprintf("Size: %d (%d + %d)<br />", data.self_size+data.kids_size, data.self_size, data.kids_size);
+        if (data.child_count) {
+            html += sprintf("Children: %d of %d<br />", data.child_count, data.kids_node_count);
         }
+
         tip.innerHTML =  html; 
       }  
     },
@@ -152,27 +159,6 @@ else {
   tm.refresh();
 }
 
-  var sq = $jit.id('r-sq'),
-      st = $jit.id('r-st'),
-      sd = $jit.id('r-sd');
-  var util = $jit.util;
-  util.addEvent(sq, 'change', function() {
-    if(!sq.checked) return;
-    util.extend(tm, new $jit.Layouts.TM.Squarified);
-    tm.refresh();
-  });
-  util.addEvent(st, 'change', function() {
-    if(!st.checked) return;
-    util.extend(tm, new $jit.Layouts.TM.Strip);
-    tm.layout.orientation = "v";
-    tm.refresh();
-  });
-  util.addEvent(sd, 'change', function() {
-    if(!sd.checked) return;
-    util.extend(tm, new $jit.Layouts.TM.SliceAndDice);
-    tm.layout.orientation = "v";
-    tm.refresh();
-  });
   //add event to the back button
   var back = $jit.id('back');
   $jit.util.addEvent(back, 'click', function() {