Merge pull request #673 from danvk/track-code-size
[dygraphs.git] / experimental / palette / multi-palette.js
CommitLineData
58a18b02
RK
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
27function 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
58a18b02
RK
36MultiPalette.optionSetValues = {
37 "global": "global",
38 "x": "x axis",
39 "y": "y axis",
40 "y2": "y2 axis",
41};
42
43MultiPalette.prototype.create = function(parentElement) {
44 var self = this;
45
46 this.root = $("<div>").addClass("palette").appendTo(parentElement);
47 var header = $("<div>").addClass("header").appendTo(this.root);
48 // Selector for series and axes.
49 var selectorRow = $("<div>").appendTo(header);
b5c7c7a9 50 this.optionSelector = $("<select>")
58a18b02 51 .change(function(x) {
b5c7c7a9 52 self.activate(self.optionSelector.val());
58a18b02
RK
53 });
54
55 selectorRow
56 .append($("<span>").text("Option Set:"))
b5c7c7a9 57 .append(this.optionSelector)
cd1d97d8
RK
58 .append($("<span>")
59 .append($("<a>")
60 .addClass("link")
61 .text("to hash")
62 .css("float", "right")
63 .css("padding-right", "8px")
64 .click(function() { self.showHash(); })));
58a18b02
RK
65
66 var filter = function() {
67 $.each(self.palettes, function(key, value) {
68 value.filter(self.filterBar.val());
69 });
70 }
71
72 this.filterBar = $("<input>", { type : "search" })
73 .keyup(filter)
74 .click(filter);
75
76 header.append($("<div>")
77 .append($("<span>").text("Filter:"))
78 .append($("<span>").append(this.filterBar))
79 .append($("<span>")
80 .append($("<a>")
58a18b02
RK
81 .addClass("link")
82 .text("Redraw")
83 .css("float", "right")
84 .css("padding-right", "8px")
85 .click(function() { self.onchange(); }))));
86
87 $.each(MultiPalette.optionSetValues, function(key, value) {
b5c7c7a9 88 self.createPalette_(key, key, value);
58a18b02
RK
89 });
90
91 this.activate("global");
92}
93
b5c7c7a9
RK
94MultiPalette.prototype.createPalette_ = function(key, scope, value) {
95 this.optionSelector
96 .append($("<option></option>")
97 .attr("value", key)
98 .text(value));
99 var palette = new Palette(scope);
100 palette.create(this.root);
9390f624 101 palette.root.hide();
b5c7c7a9
RK
102 var self = this;
103 palette.onchange = function() {
104 self.onchange();
105 };
106 this.palettes[key] = palette;
107}
108
109MultiPalette.prototype.setSeries = function(labels) {
110 for (var idx = 1; idx < labels.length; idx++) {
111 this.conditionallyAddSingleSeries_(labels[idx]);
112 }
113}
114
115MultiPalette.prototype.conditionallyAddSingleSeries_ = function(series) {
116 var key = "series:" + series;
117 if (!this.palettes.hasOwnProperty(key)) {
118 this.createPalette_(key, "series", series + " (series)");
119 }
120}
121
58a18b02
RK
122MultiPalette.prototype.activate = function(key) {
123 if (this.activePalette) {
9390f624 124 this.activePalette.root.hide();
58a18b02
RK
125 }
126 this.activePalette = this.palettes[key];
9390f624 127 this.activePalette.root.show();
58a18b02
RK
128}
129
cd1d97d8
RK
130MultiPalette.prototype.showHash = function() {
131 var hash = this.read();
132 var textarea = new TextArea();
32326a21
RK
133
134 var hashToString = function(hash) {
135 /*
136 * JSON.stringify isn't built to be nice to functions. The following fixes
137 * this.
138 *
139 * First, val.toString only does part of the work, turning it into
140 * "function () {\n alert(\"p-click!\");\n}",
141 *
142 * {start,end}Marker make the surrounding quotes easy to find, and then
143 * remove them. It also converts the instances of \n and \" so the
144 * result looks like:
145 * function () {
146 * alert("p-click!");
147 * }",
148 */
149 var startMarker = "<~%!<";
150 var endMarker = ">!%~>";
151 var replacer = function(key, val) {
152 if (typeof val === 'function') {
153 return startMarker + val.toString() + endMarker;
154 }
155 return val;
cd1d97d8 156 }
32326a21
RK
157 var text = JSON.stringify(hash, replacer, 2);
158 while(true) {
159 var start = text.indexOf(startMarker);
160 var end = text.indexOf(endMarker);
161 if (start == -1) {
162 break;
163 }
164 var substring = text.substring(start + startMarker.length, end);
165 while(substring.indexOf("\\n") >= 0) {
166 substring = substring.replace("\\n", "\n");
167 }
168 while(substring.indexOf("\\\"") >= 0) {
169 substring = substring.replace("\\\"", "\"");
170 }
171 text = text.substring(0, start - 1)
172 + substring
173 + text.substring(end + endMarker.length + 1);
cd1d97d8 174 }
32326a21 175 return text;
cd1d97d8 176 }
32326a21
RK
177
178 var text = hashToString(hash);
179 var self = this;
cd1d97d8 180 textarea.show("options", text);
32326a21
RK
181 textarea.okCallback = function(value) {
182 if (value != text) {
183 var newHash;
184 eval("newHash = " + value + ";");
185 self.write(newHash);
186 self.onchange();
187 }
188 };
cd1d97d8
RK
189}
190
58a18b02
RK
191/**
192 * Read from palette
193 */
194MultiPalette.prototype.read = function() {
195 var results = this.palettes.global.read();
196 results.axes = {};
b5c7c7a9 197 results.series = {};
58a18b02
RK
198 var clearIfEmpty = function(hash, key) {
199 var val = hash[key];
200 if ($.isEmptyObject(val)) {
201 delete hash[key];
202 }
203 }
b5c7c7a9
RK
204 var clearEmptyChildren = function(hash) {
205 for (var key in hash) {
206 if (hash.hasOwnProperty(key)) {
207 clearIfEmpty(hash, key);
208 }
209 }
210 }
211
58a18b02
RK
212 results.axes.x = this.palettes.x.read();
213 results.axes.y = this.palettes.y.read();
214 results.axes.y2 = this.palettes.y2.read();
b5c7c7a9
RK
215
216 clearEmptyChildren(results.axes);
58a18b02 217 clearIfEmpty(results, "axes");
b5c7c7a9
RK
218
219 for (var key in this.palettes) {
220 if (key.indexOf("series:") == 0) {
221 var series = key.substring("series:".length);
222 results.series[series] = this.palettes[key].read();
223 }
224 }
225
226 clearEmptyChildren(results.series);
227 clearIfEmpty(results, "series");
228
58a18b02
RK
229 return results;
230}
231
232/**
233 * Write to palette from hash.
234 */
235MultiPalette.prototype.write = function(hash) {
236 this.palettes.global.write(hash);
237 if (hash.hasOwnProperty("axes")) {
238 var axes = hash.axes;
239 this.palettes.x.write(axes["x"]);
240 this.palettes.y.write(axes["y"]);
241 this.palettes.y2.write(axes["y2"]);
242 }
b5c7c7a9
RK
243
244 if (hash.hasOwnProperty("series")) {
245 for (var key in hash.series) {
246 if (hash.series.hasOwnProperty(key)) {
247 this.conditionallyAddSingleSeries_(key);
248 this.palettes["series:" + key].write(hash.series[key]);
249 }
250 }
251 }
58a18b02 252}