Add experimental palette.
[dygraphs.git] / experimental / palette / palette.js
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";
+    }
+  }
+}