Editor title description.
[dygraphs.git] / experimental / palette / 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 Dygraphs options palette.
23 *
24 * @author konigsberg@google.com (Robert Konigsberg)
25 */
26 "use strict";
27
28 function Palette() {
29 this.model = {};
30 this.onchange = function() {};
31 this.filterBar = null;
32 }
33
34 Palette.createChild = function(type, parentElement) {
35 var element = document.createElement(type);
36 parentElement.appendChild(element);
37 return element;
38 };
39
40 Palette.prototype.create = function(document, parentElement) {
41 var palette = this;
42
43 var table = Palette.createChild("div", parentElement);
44 table.className = "palette";
45 table.width="300px";
46
47 this.tooltip = new Tooltip();
48
49 var row = Palette.createChild("div", table);
50 row.style.visibility = "visible";
51 row.className = "header";
52
53 Palette.createChild("span", row).innerText = "Filter:";
54 this.filterBar = Palette.createChild("input", Palette.createChild("span", row));
55 this.filterBar.type = "search";
56 this.filterBar.onkeyup = function() {
57 palette.filter(palette.filterBar.value)
58 };
59 this.filterBar.onclick = this.filterBar.onkeyup;
60 var go = Palette.createChild("button", Palette.createChild("span", row));
61 go.innerText = "Redraw"
62 go.onclick = function() {
63 palette.onchange();
64 };
65
66 var tmp = Palette.createChild("button", Palette.createChild("span", row));
67 tmp.innerText = "Copy"
68 tmp.onclick = function() {
69 var textarea = new TextArea();
70 textarea.show("Now is the time for all good men\nto come to the aid of their country");
71 };
72
73 for (var opt in opts) {
74 try {
75 if (opts.hasOwnProperty(opt)) {
76 var type = opts[opt].type;
77 var isFunction = type.indexOf("function(") == 0;
78 var row = Palette.createChild("div", table);
79 row.onmouseover = function(source, title, type, body, e) {
80 return function(e) {
81 palette.tooltip.show(source, e, title, type, body);
82 };
83 } (row, opt, type, Dygraph.OPTIONS_REFERENCE[opt].description);
84 row.onmouseout = function() { palette.tooltip.hide(); };
85
86 var div = Palette.createChild("span", row);
87 div.innerText = opt;
88 div.className = "name";
89
90 var value = Palette.createChild("span", row);
91 value.className = "option";
92
93 if (isFunction) {
94 var input = Palette.createChild("button", value);
95 input.onclick = function(opt, palette) {
96 return function(event) {
97 var entry = palette.model[opt];
98 var inputValue = entry.functionString;
99 if (inputValue == null || inputValue.length == 0) {
100 inputValue = opts[opt].type + "{\n\n}";
101 }
102 var textarea = new TextArea();
103 textarea.show("Function for " + opt, inputValue);
104 textarea.okCallback = function(value) {
105 if (value != inputValue) {
106 entry.functionString = value;
107 entry.input.innerText = value ? "defined" : "not defined";
108 palette.onchange();
109 }
110 }
111 }
112 }(opt, this);
113 } else {
114 var input = Palette.createChild("input", value);
115 input.onkeypress = function(event) {
116 var keycode = event.which;
117 if (keycode == 13 || keycode == 8) {
118 palette.onchange();
119 }
120 }
121
122 input.type="text";
123 }
124 this.model[opt] = { input: input, row: row };
125 }
126 } catch(err) {
127 throw "For option " + opt + ":" + err;
128 }
129 }
130 this.filter("");
131 }
132
133 // TODO: replace semicolon parsing with comma parsing, and supporting quotes.
134 Palette.parseStringArray = function(value) {
135 if (value == null || value.length == 0) {
136 return null;
137 }
138 return value.split(";");
139 }
140
141 Palette.parseBooleanArray = function(value) {
142 if (value == null || value.length == 0) {
143 return null;
144 }
145 return value.split(',').map(function(x) { return x.trim() == "true"; });
146 }
147
148 Palette.parseFloatArray = function(value) {
149 if (value == null || value.length == 0) {
150 return null;
151 }
152 return value.split(',').map(function(x) { return parseFloat(x); });
153 }
154
155 Palette.parseIntArray = function(value) {
156 if (value == null || value.length == 0) {
157 return null;
158 }
159 return value.split(',').map(function(x) { return parseInt(x); });
160 }
161
162 Palette.prototype.read = function() {
163 var results = {};
164 for (var opt in this.model) {
165 if (this.model.hasOwnProperty(opt)) {
166 var type = opts[opt].type;
167 var isFunction = type.indexOf("function(") == 0;
168 var input = this.model[opt].input;
169 var value = isFunction ? this.model[opt].functionString : input.value;
170 if (value && value.length != 0) {
171 if (type == "boolean") {
172 results[opt] = value == "true";
173 } else if (type == "int") {
174 results[opt] = parseInt(value);
175 } else if (type == "float") {
176 results[opt] = parseFloat(value);
177 } else if (type == "array<string>") {
178 results[opt] = Palette.parseStringArray(value);
179 } else if (type == "array<float>") {
180 results[opt] = Palette.parseFloatArray(value);
181 } else if (type == "array<boolean>") {
182 results[opt] = Palette.parseBooleanArray(value);
183 } else if (type == "array<Date>") {
184 results[opt] = Palette.parseIntArray(value);
185 } else if (isFunction) {
186 var localVariable = null;
187 eval("localVariable = " + value);
188 results[opt] = localVariable;
189 } else {
190 results[opt] = value;
191 }
192 }
193 }
194 }
195 return results;
196 }
197
198 /**
199 * Write to input elements.
200 */
201 Palette.prototype.write = function(hash) {
202 var results = {};
203 for (var opt in this.model) {
204 // && hash.hasOwnProperty(opt)
205 if (this.model.hasOwnProperty(opt)) {
206 var input = this.model[opt].input;
207 var type = opts[opt].type;
208 var value = hash[opt];
209 if (type == "array<string>") {
210 if (value) {
211 input.value = value.join("; ");
212 }
213 } else if (type.indexOf("array") == 0) {
214 if (value) {
215 input.value = value.join(", ");
216 }
217 } else if (type.indexOf("function(") == 0) {
218 input.innerText = value ? "defined" : "not defined";
219 this.model[opt].functionString = value ? value.toString() : null;
220 } else {
221 if (value) {
222 input.value = value;
223 }
224 }
225 }
226 }
227 }
228
229 Palette.prototype.filter = function(pattern) {
230 pattern = pattern.toLowerCase();
231 var even = true;
232 for (var opt in this.model) {
233 if (this.model.hasOwnProperty(opt)) {
234 var row = this.model[opt].row;
235 var matches = opt.toLowerCase().indexOf(pattern) >= 0;
236 row.style.display = matches ? "block" : "none";
237 if (matches) {
238 row.className = even ? "even" : "odd";
239 even = !even;
240 }
241 }
242 }
243 }