Add "to hash" feature to make palette more useful!
[dygraphs.git] / experimental / palette / multi-palette.js
1 // Copyright (c) 2011 Google, Inc.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 // THE SOFTWARE.
20
21 /**
22 * @fileoverview Multiple Dygraphs palettes, grouped by global, series, etc..
23 *
24 * @author konigsberg@google.com (Robert Konigsberg)
25 */
26
27 function MultiPalette() {
28 this.palettes = {};
29 this.root = null;
30 this.filterBar = null;
31 // This is meant to be overridden by a palette host.
32 this.activePalette = null;
33 this.onchange = function() {};
34 }
35
36
37 MultiPalette.optionSetValues = {
38 "global": "global",
39 "x": "x axis",
40 "y": "y axis",
41 "y2": "y2 axis",
42 };
43
44 MultiPalette.prototype.create = function(parentElement) {
45 var self = this;
46
47 this.root = $("<div>").addClass("palette").appendTo(parentElement);
48 var header = $("<div>").addClass("header").appendTo(this.root);
49 // Selector for series and axes.
50 var selectorRow = $("<div>").appendTo(header);
51 var optionSelector = $("<select>")
52 .change(function(x) {
53 self.activate(optionSelector.val());
54 });
55
56 selectorRow
57 .append($("<span>").text("Option Set:"))
58 .append(optionSelector)
59 .append($("<span>")
60 .append($("<a>")
61 .addClass("link")
62 .text("to hash")
63 .css("float", "right")
64 .css("padding-right", "8px")
65 .click(function() { self.showHash(); })));
66
67 var filter = function() {
68 $.each(self.palettes, function(key, value) {
69 value.filter(self.filterBar.val());
70 });
71 }
72
73 this.filterBar = $("<input>", { type : "search" })
74 .keyup(filter)
75 .click(filter);
76
77 header.append($("<div>")
78 .append($("<span>").text("Filter:"))
79 .append($("<span>").append(this.filterBar))
80 .append($("<span>")
81 .append($("<a>")
82 .addClass("link")
83 .text("Redraw")
84 .css("float", "right")
85 .css("padding-right", "8px")
86 .click(function() { self.onchange(); }))));
87
88 $.each(MultiPalette.optionSetValues, function(key, value) {
89 $(optionSelector)
90 .append($("<option></option>")
91 .attr("value", key)
92 .text(value));
93 var palette = new Palette(key);
94 palette.create(self.root);
95 palette.root.style.display = "none";
96 palette.onchange = function() {
97 self.onchange();
98 };
99 self.palettes[key] = palette;
100 });
101
102 this.activate("global");
103 }
104
105 MultiPalette.prototype.activate = function(key) {
106 if (this.activePalette) {
107 this.activePalette.root.style.display = "none";
108 }
109 this.activePalette = this.palettes[key];
110 this.activePalette.root.style.display = "block";
111 }
112
113 MultiPalette.prototype.showHash = function() {
114 var hash = this.read();
115 var textarea = new TextArea();
116 textarea.cancel.style.display = "none";
117
118 /*
119 * JSON.stringify isn't built to be nice to functions. The following fixes
120 * this.
121 *
122 * First, val.toString only does part of the work, turning it into
123 * "function () {\n alert(\"p-click!\");\n}",
124 *
125 * {start,end}Marker make the surrounding quotes easy to find, and then
126 * remove them. It also converts the instances of \n and \" so the
127 * result looks like:
128 * function () {
129 * alert("p-click!");
130 * }",
131 */
132 var startMarker = "<~%!<";
133 var endMarker = ">!%~>";
134 var replacer = function(key, val) {
135 if (typeof val === 'function') {
136 return startMarker + val.toString() + endMarker;
137 }
138 return val;
139 }
140 var text = JSON.stringify(hash, replacer, 2);
141 console.log(text);
142 while(true) {
143 var start = text.indexOf(startMarker);
144 var end = text.indexOf(endMarker);
145 if (start == -1) {
146 break;
147 }
148 var substring = text.substring(start + startMarker.length, end);
149 while(substring.indexOf("\\n") >= 0) {
150 substring = substring.replace("\\n", "\n");
151 }
152 while(substring.indexOf("\\\"") >= 0) {
153 substring = substring.replace("\\\"", "\"");
154 }
155 text = text.substring(0, start - 1)
156 + substring
157 + text.substring(end + endMarker.length + 1);
158 }
159 textarea.show("options", text);
160 }
161
162 /**
163 * Read from palette
164 */
165 MultiPalette.prototype.read = function() {
166 var results = this.palettes.global.read();
167 results.axes = {};
168 var clearIfEmpty = function(hash, key) {
169 var val = hash[key];
170 if ($.isEmptyObject(val)) {
171 delete hash[key];
172 }
173 }
174 results.axes.x = this.palettes.x.read();
175 results.axes.y = this.palettes.y.read();
176 results.axes.y2 = this.palettes.y2.read();
177 clearIfEmpty(results.axes, "x");
178 clearIfEmpty(results.axes, "y");
179 clearIfEmpty(results.axes, "y2");
180 clearIfEmpty(results, "axes");
181 return results;
182 }
183
184 /**
185 * Write to palette from hash.
186 */
187 MultiPalette.prototype.write = function(hash) {
188 this.palettes.global.write(hash);
189 if (hash.hasOwnProperty("axes")) {
190 var axes = hash.axes;
191 this.palettes.x.write(axes["x"]);
192 this.palettes.y.write(axes["y"]);
193 this.palettes.y2.write(axes["y2"]);
194 }
195 }