things generally work for treemapped bucketed browsing.
authorAndrew Sutherland <asutherland@asutherland.org>
Thu, 15 Oct 2009 17:38:09 -0700
changeset 4 abe81cebc257b739d63556b9369bfba1b643187d
parent 3 a1fcf5764e5903e733f14948fb2cf973865047fc
child 5 4678231abf9544d1386ded3b378dc9feba2f74de
push id2
push userbugmail@asutherland.org
push dateMon, 26 Oct 2009 10:02:58 +0000
things generally work for treemapped bucketed browsing.
chrome/content/formatters.js
chrome/content/logaggr.js
chrome/content/logman.js
chrome/content/logui.js
chrome/content/logviewer.xhtml
chrome/content/logvis.js
new file mode 100644
--- /dev/null
+++ b/chrome/content/formatters.js
@@ -0,0 +1,77 @@
+let FormatHelp = {
+
+};
+
+let LogFormatters = {
+  test: { // and subtest
+    stringify: function format_test_stringify(obj) {
+      return obj.type + ": " + obj.name + " " + obj.parameter;
+    }
+  },
+
+  action: {
+    stringify: function format_action_stringify(obj) {
+      return obj.who + " " + obj.what + stringifyTypedObj(obj.arg, ": ");
+    }
+  },
+
+  check: {
+    stringify: function format_check_stringify(obj) {
+      return (obj.success ? "PASS " : "FAIL ") +
+        stringifyThing(obj.left) + " ?= " +
+        stringifyThing(obj.right) +
+        stringifyTypedObj(obj.stack, " in ");
+    }
+  },
+
+  failure: {
+    stringify: function format_failure_stringify(obj) {
+      return "EXPLOSION: " + obj.text +
+        stringifyTypedObj(obj.stack, " in ");
+    }
+  },
+
+  /* ************ _normalize_for_json stuff ************* */
+
+  folder: {
+    stringify: function format_folder_stringify(obj) {
+      return "Folder: " + obj.name;
+    }
+  },
+
+  msgHdr: {
+    stringify: function format_msgHdr_stringify(obj) {
+      return "MsgHdr: " + obj.name;
+    }
+  },
+
+  // a single frame, not a whole stack
+  stackFrame: {
+    stringify: function format_stackFrame_stringify(obj) {
+      return obj.name + " @ " + obj.filename + ":" + obj.lineNumber;
+    }
+  },
+};
+LogFormatters.subtest = LogFormatters.test;
+
+function stringifyThing(obj, conditionalStr) {
+  if (conditionalStr == null)
+    conditionalStr = "";
+
+  if (typeof(obj) == "object") {
+    if ("type" in obj)
+      return stringifyTypedObj(obj, conditionalStr);
+  }
+
+  return conditionalStr + obj;
+}
+
+function stringifyTypedObj(obj, conditionalStr) {
+  if (conditionalStr == null)
+    conditionalStr = "";
+
+  if (!(obj.type in LogFormatters))
+    return "";
+
+  return conditionalStr + LogFormatters[obj.type].stringify(obj);
+}
--- a/chrome/content/logaggr.js
+++ b/chrome/content/logaggr.js
@@ -18,30 +18,37 @@ let LogAggr = {
   },
 
   bucketAggrs: [],
   reset: function() {
     this.bucketAggrs = [];
   },
 
   chew: function() {
-    if (this.curBucket && this.curBucket.length != this.curBucketCount)
+    let didSomething = false;
+
+    if (this.curBucket && this.curBucket.length != this.curBucketCount) {
+      didSomething = true;
       this._chewBucket(this.curBucket, this.curBucketAggr);
+    }
 
     let newBuckets = LogManager.getAndClearNewBuckets();
     for each (let [iBucket, [bucketName, bucket]] in Iterator(newBuckets)) {
+      didSomething = true;
       let bucketAggr = {
         name: bucketName,
         loggerCounts: {}
       };
 
       this.bucketAggrs.push(bucketAggr);
 
       this._chewBucket(bucket, bucketAggr);
 
       if (iBucket == newBuckets.length - 1) {
         this.curBucket = bucket;
         this.curBucketAggr = bucketAggr;
         this.curBucketCount = bucket.length;
       }
     }
+
+    return didSomething;
   }
 };
--- a/chrome/content/logman.js
+++ b/chrome/content/logman.js
@@ -41,16 +41,22 @@ let LogManager = {
 
   getAndClearNewBuckets: function LogManager_getAndClearNewBuckets() {
     let newBuckets = this._newBuckets;
     this._newBuckets = [];
 
     return newBuckets;
   },
 
+  getBucket: function LogManager_getBucket(bucketName) {
+    if (bucketName in this._dateBuckets)
+      return this._dateBuckets[bucketName];
+    return null;
+  },
+
   /**
    * Reset all state
    */
   reset: function LogManager_reset() {
     this._knownLoggers = {};
 
     this._dateBuckets = {};
     this._curBucket = null;
new file mode 100644
--- /dev/null
+++ b/chrome/content/logui.js
@@ -0,0 +1,82 @@
+/*
+ * UI servicing of things...
+ */
+
+/**
+ * In charge of the various concepts of selection and such.
+ */
+let LogUI = {
+  _init: function LogUI__init() {
+
+  },
+
+  selectBucket: function LogUI_showBucket(bucketAggr) {
+    this._notifyListeners("onBucketSelected", [bucketAggr]);
+  },
+
+  /**
+   * Maps a listeningFor identifier to a list of listeners.
+   */
+  _listenersByListeningFor: {},
+
+  _notifyListeners: function LogManager__notifyListeners(listeningFor, args) {
+    if (!(listeningFor in this._listenersByListeningFor))
+      return;
+
+    for each (let [, [listener, listenerThis]] in
+              Iterator(this._listenersByListeningFor[listeningFor])) {
+      listener.apply(listenerThis, args);
+    }
+  },
+
+  registerListener: function LogManager_registerListener(listenFor, listener,
+                                                         listenerThis) {
+    if (!(listenFor in this._listenersByListeningFor))
+      this._listenersByListeningFor[listenFor] = [];
+
+    this._listenersByListeningFor[listenFor].push([listener, listenerThis]);
+  }
+};
+LogUI._init();
+
+/**
+ * In charge of the log listing UI which is slaved to the currently selected
+ *  bucket.
+ */
+let LogList = {
+  _init: function LogList__init() {
+    LogUI.registerListener("onBucketSelected", this.onBucketSelected, this);
+  },
+
+  onBucketSelected: function LogList_onBucketSelected(bucketAggr) {
+    let bucket = LogManager.getBucket(bucketAggr.name);
+
+    let bucketNode = document.getElementById("bucket-contents");
+    while (bucketNode.lastChild)
+      bucketNode.removeChild(bucketNode.lastChild);
+
+    let listRoot = document.createElement("ul");
+
+    for each (let [, msg] in Iterator(bucket)) {
+      let text = "";
+      for each (let [, msgObj] in Iterator(msg.messageObjects)) {
+        // ignore contexts for this purpose
+        if (typeof(msgObj) != "object")
+          text += (text ? " " : "") + msgObj;
+        else if ("_isContext" in msgObj)
+          continue;
+        else if ("type" in msgObj)
+          text += stringifyTypedObj(msgObj, " ");
+
+        let listNode = document.createElement("li");
+        listNode.textContent = text;
+        listRoot.appendChild(listNode);
+      }
+    }
+
+    bucketNode.appendChild(listRoot);
+  },
+
+
+};
+LogList._init();
--- a/chrome/content/logviewer.xhtml
+++ b/chrome/content/logviewer.xhtml
@@ -11,16 +11,22 @@
       const Cu = Components.utils;
 
       Cu.import("resource://logsploder/modules/gobbler.js");
     </script>
     <script src="logman.js" type="application/javascript;version=1.8"/>
     <script src="logaggr.js" type="application/javascript;version=1.8"/>
     <script src="protovis-d3.1.js" type="application/javascript;version=1.8"/>
     <script src="logvis.js" type="application/javascript;version=1.8"/>
+    <script src="formatters.js" type="application/javascript;version=1.8"/>
+    <script src="logui.js" type="application/javascript;version=1.8"/>
     <link rel="stylesheet"
       href="chrome://logsploder/content/logviewer.css"
       type="text/css" />
   </head>
   <body bgcolor="#ffffff">
-    <div id="logviewer"/>
+    <div id="visualizations" style="display: inline-block">
+      <div id="logger-hierarchy-vis" style="float: left;"/>
+      <div id="date-bucket-vis" style="float: left;"/>
+    </div>
+    <div id="bucket-contents"/>
   </body>
 </html>
--- a/chrome/content/logvis.js
+++ b/chrome/content/logvis.js
@@ -10,47 +10,54 @@ function makeTimeVis(aWidth, aHeight) {
     .width(aWidth)
     .height(aHeight);
 
 
 }
 
 let LoggerHierarchyVisier = {
   WIDTH: 360,
-  HEIGHT: 240,
+  HEIGHT: 360,
   _init: function LoggerHierarchyVisier_init() {
     LogManager.registerListener("onNewLogger", this.onNewLogger, this);
   },
 
   _vis: null,
   _makeVis: function LoggerHierarchyVisier__makeVis() {
     let vis = this._vis = new pv.Panel()
+      .canvas("logger-hierarchy-vis")
       .width(this.WIDTH)
       .height(this.HEIGHT);
 
-    let colorize = pv.Colors.category19().by(function(n) n.keys.slice(0, -1));
+    //let colorize = pv.Colors.category20().by(function(n) n.keys);
+    let colorize = function(s) {
+      return new pv.Color.Hsl(360 * s.index / s.scene.length,
+                              1, 0.8, 1);
+    };
 
     this._treemap = pv.Layout.treemap(this.loggerTree)
-      .round(true).inset(17, 1, 1, 1).root("");
+      //.inset(17, 1, 1, 1)
+      .round(true);
 
     this._mapvis = vis.add(pv.Bar)
         .extend(this._treemap)
         //.width(function(n) n.width - 1)
         //.height(function(n) n.height - 1)
         .title(function(n) n.keys.join("."))
-        .fillStyle(function(n) colorize(n).alpha(0.5));
+        .fillStyle(function(n) colorize(this));
     this._mapvis.anchor("top").add(pv.Label)
         .text(function(n) n.keys[n.keys.length - 1]);
   },
   _updateVis: function LoggerHierarchyVisier__updateVis() {
     if (this._vis == null)
       this._makeVis();
     else {
       this._treemap = pv.Layout.treemap(this.loggerTree)
-        .round(true).inset(17, 1, 1, 1).root("");
+        //.inset(17, 1, 1, 1)
+        .round(true);
       this._mapvis.extend(this._treemap);
     }
 
     this._vis.render();
   },
 
   onNewLogger: function LoggerHierarchyVisier_onNewLogger(loggers,
                                                           newLoggerName) {
@@ -65,66 +72,66 @@ let LoggerHierarchyVisier = {
     this._updateVis();
   },
 };
 LoggerHierarchyVisier._init();
 
 
 
 let DateBucketVis = {
-  WIDTH: 600,
-  HEIGHT: 600,
-  CELL_WIDTH: 60,
-  CELL_HEIGHT: 60,
+  WIDTH: 610,
+  HEIGHT: 366,
+  CELL_WIDTH: 61,
+  CELL_HEIGHT: 61,
   _vis: null,
   _makeVis: function DateBucketVis__makeVis() {
     let vis = this._vis = new pv.Panel()
+      .canvas("date-bucket-vis")
       .width(this.WIDTH)
-      .height(this.HEIGHT)
-      .fillStyle("blue");
+      .height(this.HEIGHT);
 
     this._treemap = pv.Layout.treemap(LoggerHierarchyVisier.loggerTree)
       .round(true);
 
     let WIDTH = this.WIDTH, HEIGHT = this.HEIGHT;
     let CELL_WIDTH = this.CELL_WIDTH, CELL_HEIGHT = this.CELL_HEIGHT;
     let xCount = Math.floor(WIDTH / CELL_WIDTH);
     let yCount = Math.floor(HEIGHT / CELL_HEIGHT);
 
-    let raw_colorize =
-      pv.Colors.category19().by(function(n) n.keys.slice(0, -1));
+    //let raw_colorize =
+    //  pv.Colors.category20().by(function(n) n.keys);
 
     function colorize(s, n) {
       let loggerName = n.keys.join(".");
-      //dump("s.parent: " + uneval(s.parent) + "\n");
       let p = s.parent;
-      //dump("index: " + p.index + "\n");
       let bucketAggr = p.scene[p.index].data;
-      //dump("s: " + uneval(s) + "\n");
-      //dump("n: " + uneval(n) + "\n");
       let count = 0;
       if (loggerName in bucketAggr.loggerCounts)
         count = bucketAggr.loggerCounts[loggerName];
-      return raw_colorize(n).alpha(Math.min(1.0, count / 2 + 0.4));
+      return new pv.Color.Hsl(360 * s.index / s.scene.length,
+                              Math.min(1.0, count / 10),
+                              0.8, 1);
     };
 
     let cell = vis.add(pv.Panel)
       .data(this.buckets)
       .top(function() Math.floor(this.index / xCount) * CELL_HEIGHT)
       .left(function() (this.index % xCount) * CELL_WIDTH)
-      .height(CELL_HEIGHT)
-      .width(CELL_WIDTH);
+      .height(CELL_HEIGHT - 1)
+      .width(CELL_WIDTH - 1)
+      .event("click", function(d) LogUI.selectBucket(d));
     this._mapvis = cell.add(pv.Bar)
       .extend(this._treemap)
-      //.fillStyle(function(n) colorize(this, n));
-      .fillStyle(function(n) raw_colorize(n));
+      .fillStyle(function(n) colorize(this, n));
 
   },
   updateVis: function DateBucketVis__updateVis() {
-    LogAggr.chew();
+    // don't do anything if nothing changed.
+    if(!LogAggr.chew())
+      return;
     this.buckets = LogAggr.bucketAggrs;
 
     if (this.buckets.length == 0)
       return;
 
     dump(this.buckets.length + " buckets\n");
 
     if (this._vis == null)