1 // Copyright (c) 2011 Google, Inc.
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:
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
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
22 * @fileoverview Dygraphs options palette.
24 * @author konigsberg@google.com (Robert Konigsberg)
29 * scope is either "global", "series", "x", "y" or "y2".
31 function Palette(scope
) {
32 // Contains pair of "input" (the input object) and "row" (the parent row)
33 // Also contains functionString.
35 // This is meant to be overridden by a palette host.
36 this.onchange
= function() {};
41 Palette
.createChild
= function(type
, parentElement
, className
) {
42 var element
= document
.createElement(type
);
43 parentElement
.appendChild(element
);
45 element
.className
= className
;
50 Palette
.prototype.create
= function(parentElement
) {
53 var table
= Palette
.createChild("div", parentElement
[0], "palette");
57 this.tooltip
= new Tooltip();
60 var header
= Palette
.createChild("div", table
, "header");
61 header
.style
.visibility
= "visible";
64 var tmp
= Palette
.createChild("button", Palette
.createChild("span", header
));
65 tmp
.textContent
= "Copy"
66 tmp
.onclick
= function() {
67 var textarea
= new TextArea();
68 textarea
.show("header", "Now is the time for all good men\nto come to the aid of their country");
70 tmp
.style
.display
= "none";
72 // One row per option.
73 for (var opt
in opts
) {
75 if (opts
.hasOwnProperty(opt
)) {
76 var type
= opts
[opt
].type
;
78 var scope
= opts
[opt
].scope
|| [ "global" ]; // Scope can be empty, infer "global" only.
79 var valid
= scope
[0] == "*" || $.inArray(this.scope
, scope
) >= 0;
84 var isFunction
= type
.indexOf("function(") == 0;
85 var row
= Palette
.createChild("div", table
);
86 row
.onmouseover
= function(source
, title
, type
, body
) {
88 palette
.tooltip
.show(source
, title
, type
, body
);
90 } (row
, opt
, type
, Dygraph
.OPTIONS_REFERENCE
[opt
].description
);
91 row
.onmouseout
= function() { palette
.tooltip
.hide(); };
93 var div
= Palette
.createChild("span", row
, "name");
94 div
.textContent
= opt
;
96 var value
= Palette
.createChild("span", row
, "option");
99 var input
= Palette
.createChild("button", value
);
100 input
.onclick
= function(opt
, palette
) {
101 return function(event
) {
102 var entry
= palette
.model
[opt
];
103 var inputValue
= entry
.functionString
;
104 if (inputValue
== null || inputValue
.length
== 0) {
105 inputValue
= opts
[opt
].type
+ "{\n\n}";
107 var textarea
= new TextArea();
108 textarea
.show(opt
, inputValue
);
109 textarea
.okCallback
= function(value
) {
110 if (value
!= inputValue
) {
111 entry
.functionString
= value
;
112 entry
.input
.textContent
= value
? "defined" : "not defined";
118 } else if (type
== "boolean") {
119 var input
= Palette
.createChild("button", value
);
120 input
.onclick
= function(e
) {
122 if (btn
.value
== "none") {
123 Palette
.populateBooleanButton(btn
, "true");
124 } else if (btn
.value
== "true") {
125 Palette
.populateBooleanButton(btn
, "false");
127 Palette
.populateBooleanButton(btn
, "none");
132 var input
= Palette
.createChild("input", value
, "textInput");
134 input
.onkeypress
= function(event
) {
135 var keycode
= event
.which
;
136 if (keycode
== 13 || keycode
== 8) {
141 this.model
[opt
] = { input
: input
, row
: row
};
144 throw "For option " + opt
+ ":" + err
;
150 // TODO: replace semicolon parsing with comma parsing, and supporting quotes.
151 Palette
.parseStringArray
= function(value
) {
152 if (value
== null || value
.length
== 0) {
155 return value
.split(";");
158 Palette
.parseBooleanArray
= function(value
) {
159 if (value
== null || value
.length
== 0) {
162 return value
.split(',').map(function(x
) { return x
.trim() == "true"; });
165 Palette
.parseFloatArray
= function(value
) {
166 if (value
== null || value
.length
== 0) {
169 return value
.split(',').map(function(x
) { return parseFloat(x
); });
172 Palette
.parseIntArray
= function(value
) {
173 if (value
== null || value
.length
== 0) {
176 return value
.split(',').map(function(x
) { return parseInt(x
); });
179 Palette
.prototype.read
= function() {
181 for (var opt
in this.model
) {
182 if (this.model
.hasOwnProperty(opt
)) {
183 var type
= opts
[opt
].type
;
184 var isFunction
= type
.indexOf("function(") == 0;
185 var input
= this.model
[opt
].input
;
186 var value
= isFunction
? this.model
[opt
].functionString
: input
.value
;
187 if (value
&& value
.length
!= 0) {
188 if (type
== "boolean") {
189 if (value
== "false") {
190 results
[opt
] = false;
192 if (value
== "true") {
195 // Ignore value == "none"
196 } else if (type
== "int") {
197 results
[opt
] = parseInt(value
);
198 } else if (type
== "float") {
199 results
[opt
] = parseFloat(value
);
200 } else if (type
== "array<string>") {
201 results
[opt
] = Palette
.parseStringArray(value
);
202 } else if (type
== "array<float>") {
203 results
[opt
] = Palette
.parseFloatArray(value
);
204 } else if (type
== "array<boolean>") {
205 results
[opt
] = Palette
.parseBooleanArray(value
);
206 } else if (type
== "array<int>") {
207 results
[opt
] = Palette
.parseIntArray(value
);
208 } else if (type
== "array<Date>") {
209 results
[opt
] = Palette
.parseIntArray(value
);
210 } else if (isFunction
) {
211 var localVariable
= null;
212 eval("localVariable = " + value
);
213 results
[opt
] = localVariable
;
215 results
[opt
] = value
;
224 * Write to input elements.
226 Palette
.prototype.write
= function(hash
) {
228 for (var opt
in this.model
) {
229 if (this.model
.hasOwnProperty(opt
)) {
230 var input
= this.model
[opt
].input
;
231 var type
= opts
[opt
].type
;
232 var value
= hash
[opt
];
233 if (type
== "boolean") {
234 var text
= value
== true ? "true" : (value
== false ? "false" : "none");
235 Palette
.populateBooleanButton(input
, text
);
236 } else if (type
== "array<string>") {
238 input
.value
= value
.join("; ");
240 } else if (type
.indexOf("array") == 0) {
242 input
.value
= value
.join(", ");
244 } else if (type
.indexOf("function(") == 0) {
245 input
.textContent
= value
? "defined" : "not defined";
246 this.model
[opt
].functionString
= value
? value
.toString() : null;
248 if (value
!= undefined
) {
256 Palette
.populateBooleanButton
= function(button
, value
) {
257 button
.innerHTML
= value
;
258 button
.value
= value
;
261 Palette
.prototype.filter
= function(pattern
) {
262 pattern
= pattern
.toLowerCase();
264 for (var opt
in this.model
) {
265 if (this.model
.hasOwnProperty(opt
)) {
266 var row
= this.model
[opt
].row
;
267 var matches
= opt
.toLowerCase().indexOf(pattern
) >= 0;
268 row
.style
.display
= matches
? "block" : "none";
270 row
.className
= even
? "even" : "odd";