Add experimental palette.
authorRobert Konigsberg <konigsberg@google.com>
Fri, 6 Jan 2012 23:20:32 +0000 (18:20 -0500)
committerRobert Konigsberg <konigsberg@google.com>
Fri, 6 Jan 2012 23:20:32 +0000 (18:20 -0500)
experimental/palette/index.html [new file with mode: 0644]
experimental/palette/index.js [new file with mode: 0644]
experimental/palette/options.js [new file with mode: 0644]
experimental/palette/palette.css [new file with mode: 0644]
experimental/palette/palette.js [new file with mode: 0644]

diff --git a/experimental/palette/index.html b/experimental/palette/index.html
new file mode 100644 (file)
index 0000000..610d352
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9">
+    <title>Palette Demo</title>
+    <!--[if IE]>
+    <script type="text/javascript" src="../../excanvas.js"></script>
+    <![endif]-->
+    <script type="text/javascript" src="../../dygraph-dev.js"></script>
+    <script type="text/javascript" src="options.js"></script>
+    <script type="text/javascript" src="palette.js"></script>
+    <script type="text/javascript" src="index.js"></script>
+    <link rel="stylesheet" type="text/css" href="palette.css" />
+    <style>
+    </style>
+  </head>
+  <body>
+    <h2>Palette</h2>
+    <div style="font-size:smaller;"><p>To configure this chart, tweak the values on the right.
+    Enter applies changes, and the filter bar on the top reduces selections</p></div>
+    <table>
+      <tr>
+        <td valign="top">
+          <div id="graph"></div>
+          <div id="status" style="width:200px; font-size:0.8em; padding-top:5px;"></div>
+          Other messages:
+          <div id="messages"></div>
+        </td>
+        <td valign="top"><div class="palette" id="optionsPalette"></div></td>
+     </tr>
+    </table>
+  </body>
+  <script type="text/javascript">start();</script>
+</html>
diff --git a/experimental/palette/index.js b/experimental/palette/index.js
new file mode 100644 (file)
index 0000000..6598602
--- /dev/null
@@ -0,0 +1,104 @@
+// Copyright (c) 2012 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/** 
+ * @fileoverview Javascript to run index.html.
+ *
+ * @author konigsberg@google.com (Robert Konigsberg)
+ */
+
+"use strict";
+
+function draw(element, options) {
+  try {
+    element.innerHTML = "";
+    element.removeAttribute("style");
+    var g = new Dygraph(
+      element,
+        function() {
+          var zp = function(x) { if (x < 10) return "0"+x; else return x; };
+          var r = "date,parabola,line,another line,sine wave\n";
+          for (var i=1; i<=31; i++) {
+            r += "201110" + zp(i);
+            r += "," + 10*(i*(31-i));
+            r += "," + 10*(8*i);
+            r += "," + 10*(250 - 8*i);
+            r += "," + 10*(125 + 125 * Math.sin(0.3*i));
+            r += "\n";
+          }
+          return r;
+        }, options
+    );
+  
+    // These don't work yet.
+    g.updateOptions({
+      labelsDiv: 'status',
+    });
+  } catch(err) {
+   addMessage(err);
+   throw(err);
+  } finally {
+  }
+}
+
+function addMessage(text) {
+  var messages = document.getElementById("messages");
+  messages.innerText = messages.innerText + text + "\n";
+}
+
+function start() {
+  var options = {
+    colors: [
+      "rgb(51,204,204)",
+      "rgb(255,100,100)",
+      "#00DD55",
+      "rgba(50,50,200,0.4)"
+    ],
+    labelsSeparateLines: true,
+    labelsKMB: true,
+    legend: 'always',
+    width: 640,
+    height: 480,
+    title: 'Interesting Shapes',
+    xlabel: 'Date',
+    ylabel: 'Count',
+    axisLineColor: 'white',
+    drawXGrid: false,
+    pointClickCallback: function() { alert("p-click!"); },
+  };
+
+  var redraw = function() {
+    draw(document.getElementById("graph"), palette.read());
+  }
+
+  var palette = new Palette();
+  palette.create(document, document.getElementById("optionsPalette"));
+  palette.write(options);
+  palette.onchange = redraw;
+  palette.filterBar.focus();
+  redraw();
+
+  for (var opt in Dygraph.OPTIONS_REFERENCE) {
+    if (!(opt in opts)) {
+      var entry = Dygraph.OPTIONS_REFERENCE[opt];
+      console.warn("missing option: " + opt + " of type " + entry.type);
+    }
+  }
+}
diff --git a/experimental/palette/options.js b/experimental/palette/options.js
new file mode 100644 (file)
index 0000000..e590693
--- /dev/null
@@ -0,0 +1,116 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/** 
+ * @fileoverview List of options and their types, used for the palette.
+ *
+ * @author konigsberg@google.com (Robert Konigsberg)
+ */
+
+"use strict";
+
+var opts = {
+  animatedZooms: { type: "boolean" },
+  annotationClickHandler : {type: "function(annotation, point, dygraph, event)"},
+  annotationDblClickHandler : {type: "function(annotation, point, dygraph, event)"},
+  annotationMouseOutHandler : {type: "function(annotation, point, dygraph, event)"},
+  annotationMouseOverHandler : {type: "function(annotation, point, dygraph, event)"},
+  avoidMinZero: { type: "boolean" },
+  axisLabelColor: { type: "string" },
+  axisLabelFontSize: {type: "int" },
+  axisLabelWidth: {type: "int" },
+  axisLineColor: {type: "string" },
+  axisLineWidth: {type: "int" },
+  axisTickSize: {type: "int" },
+  clickCallback : {type: "function(e, x, points)"},
+  colorSaturation: { type: "float" },
+  colors: { type: "array<string>" },
+  colorValue: { type: "float" },
+  connectSeparatedPoints: { type: "boolean" },
+  customBars: { type: "boolean" },
+  dateWindow: { type: "array<Date>"},
+  delimiter: { type: "string" },
+  digitsAfterDecimal: { type: "int"},
+  displayAnnotations: { type: "boolean" },
+  drawCallback : {type: "function(dygraph, is_initial)"},
+  drawPoints: { type: "boolean" },
+  drawXAxis: {type: "boolean" },
+  drawXGrid: {type: "boolean" },
+  drawYAxis: {type: "boolean" },
+  drawYGrid: {type: "boolean" },
+  errorBars: { type: "boolean" },
+  fillAlpha: { type: "float" },
+  fillGraph: { type: "boolean" },
+  fractions: { type: "boolean" },
+  gridLineColor: { type: "string" },
+  gridLineWidth: { type: "int" },
+  height: {type: "int"},
+  hideOverlayOnMouseOut: { type: "boolean" },
+  highlightCallback : {type: "function(event, x, points,row)"},
+  highlightCircleSize: { type: "int" },
+  includeZero: { type: "boolean" },
+  isZoomedIgnoreProgrammaticZoom: {type: "boolean" },
+  labelsDivWidth: {type: "integer"},
+  labels: {type: "array<string>" },
+  labelsKMB: {type: "boolean" },
+  labelsKMG2: {type: "boolean"},
+  labelsSeparateLines: {type: "boolean"},
+  labelsShowZeroValues: {type: "boolean"},
+  legend: {type: "string"},
+  logscale: { type: "boolean" },
+  maxNumberWidth: {type: "int"},
+  panEdgeFraction: { type: "float" },
+  pixelsPerLabel: { type: "int" },
+  pixelsPerXLabel: { type: "int" },
+  pixelsPerYLabel: { type: "int" },
+  pointClickCallback : {type: "function(e, point)"},
+  pointSize: { type: "integer" },
+  rangeSelectorHeight: { type: "int" },
+  rangeSelectorPlotFillColor: { type: "int" },
+  rangeSelectorPlotStrokeColor: { type: "int" },
+  rightGap: {type: "boolean"},
+  rollPeriod: {type: "int"},
+  showLabelsOnHighlight: { type: "boolean" },
+  showRangeSelector: { type: "boolean" },
+  showRoller: {type: "boolean" },
+  sigFigs: {type: "int"},
+  sigma: { type: "float" },
+  stackedGraph: { type: "boolean" },
+  stepPlot: { type: "boolean" },
+  strokeWidth: { type: "integer" },
+  timingName: { type: "string" },
+  title: {type: "string"},
+  titleHeight: {type: "integer"},
+  underlayCallback : {type: "function(canvas, area, dygraph)"},
+  unhighlightCallback : {type: "function(event)"},
+  valueRange: {type: "array<float>"},
+  visibility: {type: "array<boolean>"},
+  width: {type: "int"},
+  wilsonInterval: { type: "boolean" },
+  xAxisHeight: {type: "int"},
+  xAxisLabelWidth: {type: "int"},
+  xLabelHeight: {type: "int"},
+  xlabel : {type: "string" },
+  xValueParser : {type: "function(str)"},
+  yAxisLabelWidth: {type: "int"},
+  yLabelWidth: {type: "int"},
+  ylabel : {type: "string" },
+  zoomCallback : {type: "function(minDate, maxDate, yRanges)"},
+};
diff --git a/experimental/palette/palette.css b/experimental/palette/palette.css
new file mode 100644 (file)
index 0000000..ee63c5d
--- /dev/null
@@ -0,0 +1,5 @@
+.palette {
+  border-style: solid;
+  border-width: 1px;
+  font-size:smaller;
+}
diff --git a/experimental/palette/palette.js b/experimental/palette/palette.js
new file mode 100644 (file)
index 0000000..f242545
--- /dev/null
@@ -0,0 +1,219 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/** 
+ * @fileoverview Dygraphs options palette.
+ *
+ * @author konigsberg@google.com (Robert Konigsberg)
+ */
+"use strict";
+
+function Palette() {
+  this.model = {};
+  this.onchange = function() {};
+  this.filterBar = null;
+}
+
+Palette.prototype.create = function(document, parentElement) {
+  var palette = this;
+  var createChild = function(type, parentElement) {
+    var element = document.createElement(type);
+    parentElement.appendChild(element);
+    return element;
+  };
+
+  var table = createChild("table", parentElement);
+  table.class = "palette";
+
+  var row = createChild("tr", table);
+  row.style.display = "block";
+
+  createChild("td", row).innerText = "Filter:";
+  this.filterBar = createChild("input", createChild("td", row));
+  this.filterBar.onkeyup = function() {
+    palette.filter(palette.filterBar.value)
+  };
+  var go = document.createElement("button");
+  createChild("td", row).appendChild(go);
+  go.innerText = "Redraw"
+  go.onclick = function() {
+    palette.onchange();
+  };
+
+  for (var opt in opts) {
+    try {
+      if (opts.hasOwnProperty(opt)) {
+        var type = opts[opt].type;
+        var isFunction = type.indexOf("function(") == 0;
+        var row = createChild("tr", table);
+        var div = createChild("div", createChild("td", row));
+        var a = createChild("a", div);
+        a.innerText = opt;
+        a.href = "http://dygraphs.com/options.html#" + opt;
+        a.title = Dygraph.OPTIONS_REFERENCE[opt].description;
+        a.target="_blank";
+
+        if (isFunction) {
+           var input = createChild("button", createChild("td", row));
+           input.onclick = function(opt, palette) {
+             return function(event) {
+               var entry = palette.model[opt];
+               var inputValue = entry.functionString;
+               if (inputValue == null || inputValue.length == 0) {
+                 inputValue = type + "{ }";
+               }
+               var value = prompt("enter function", inputValue);
+               if (value != null) {
+                 if (value.length == 0) {
+                   value = null;
+                 }
+                 if (value != inputValue) {
+                   entry.functionString = value;
+                   entry.input.innerText = value ? "defined" : "not defined";
+                   palette.onchange();
+                 }
+               }
+             }
+           }(opt, this);
+        } else {
+          var input = createChild("input", createChild("td", row));
+          input.onkeypress = function(event) {
+            var keycode = event.which;
+            if (keycode == 13 || keycode == 8) {
+              palette.onchange();
+            }
+          }
+
+          input.type="text";
+        }
+        this.model[opt] = { input: input, row: row };
+      }
+    } catch(err) {
+      throw "For option " + opt + ":" + err;
+    }
+  }
+  this.filter("");
+}
+
+// TODO: replace semicolon parsing with comma parsing, and supporting quotes.
+Palette.parseStringArray = function(value) {
+  if (value == null || value.length == 0) {
+    return null;
+  }
+  return value.split(";");
+}
+
+Palette.parseBooleanArray = function(value) {
+  if (value == null || value.length == 0) {
+    return null;
+  }
+  return value.split(',').map(function(x) { return x.trim() == "true"; });
+}
+
+Palette.parseFloatArray = function(value) {
+  if (value == null || value.length == 0) {
+    return null;
+  }
+  return value.split(',').map(function(x) { return parseFloat(x); });
+}
+
+Palette.parseIntArray = function(value) {
+  if (value == null || value.length == 0) {
+    return null;
+  }
+  return value.split(',').map(function(x) { return parseInt(x); });
+}
+
+Palette.prototype.read = function() {
+  var results = {};
+  for (var opt in this.model) {
+    if (this.model.hasOwnProperty(opt)) {
+      var type = opts[opt].type;
+      var isFunction = type.indexOf("function(") == 0;
+      var input = this.model[opt].input;
+      var value = isFunction ? this.model[opt].functionString : input.value;
+      if (value && value.length != 0) {
+        if (type == "boolean") {
+          results[opt] = value == "true";
+        } else if (type == "int") {
+          results[opt] = parseInt(value);
+        } else if (type == "float") {
+          results[opt] = parseFloat(value);
+        } else if (type == "array<string>") {
+          results[opt] = Palette.parseStringArray(value);
+        } else if (type == "array<float>") {
+          results[opt] = Palette.parseFloatArray(value);
+        } else if (type == "array<boolean>") {
+          results[opt] = Palette.parseBooleanArray(value);
+        } else if (type == "array<Date>") {
+          results[opt] = Palette.parseIntArray(value);
+        } else if (isFunction) {
+          var localVariable = null;
+          eval("localVariable = " + value);
+          results[opt] = localVariable;
+        } else {
+          results[opt] = value;
+        }
+      }
+    }
+  }
+  return results;
+}
+
+/**
+ * Write to input elements.
+ */
+Palette.prototype.write = function(hash) {
+  var results = {};
+  for (var opt in this.model) {
+    //  && hash.hasOwnProperty(opt)
+    if (this.model.hasOwnProperty(opt)) {
+      var input = this.model[opt].input;
+      var type = opts[opt].type;
+      var value = hash[opt];
+      if (type == "array<string>") {
+        if (value) {
+          input.value = value.join("; ");
+        }
+      } else if (type.indexOf("array") == 0) {
+        if (value) {
+          input.value = value.join(", ");
+        }
+      } else if (type.indexOf("function(") == 0) {
+        input.innerText = value ? "defined" : "not defined";
+        this.model[opt].functionString = value ? value.toString() : null;
+      } else {
+        if (value) {
+          input.value = value;
+        }
+      }
+    }
+  }
+}
+
+Palette.prototype.filter = function(pattern) {
+  pattern = pattern.toLowerCase();
+  for (var opt in this.model) {
+    if (this.model.hasOwnProperty(opt)) {
+      var row = this.model[opt].row;
+      row.style.display = (opt.toLowerCase().indexOf(pattern) >= 0) ? "block" : "none";
+    }
+  }
+}