Commit | Line | Data |
---|---|---|
80be397c DV |
1 | <!DOCTYPE html> |
2 | <html> | |
3 | <head> | |
fd6b8dad | 4 | <link rel="stylesheet" href="../dist/dygraph.css"> |
80be397c | 5 | <title>Hairlines demo</title> |
fbd6834a | 6 | <script type="text/javascript" src="../dist/dygraph.js"></script> |
80be397c DV |
7 | |
8 | <!-- Include the Javascript for the plug-in --> | |
9 | <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> | |
10 | <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/jquery-ui.min.js"></script> | |
11 | ||
12 | <link rel='stylesheet' href='http://code.jquery.com/ui/1.10.1/themes/base/jquery-ui.css' /> | |
13 | ||
178b1e0a DV |
14 | <script type="text/javascript" src="../src/extras/hairlines.js"></script> |
15 | <script type="text/javascript" src="../src/extras/super-annotations.js"></script> | |
80be397c DV |
16 | |
17 | <style> | |
18 | #demodiv { | |
19 | position: absolute; | |
20 | left: 10px; | |
21 | right: 200px; | |
22 | height: 400px; | |
23 | display: inline-block; | |
24 | } | |
25 | #status { | |
26 | position: absolute; | |
27 | right: 10px; | |
28 | width: 180px; | |
29 | height: 400px; | |
30 | display: inline-block; | |
31 | } | |
32 | #controls { | |
33 | position: absolute; | |
34 | left: 10px; | |
35 | margin-top: 420px; | |
36 | } | |
37 | ||
38 | /* This style & the next show how you can customize the appearance of the | |
39 | hairlines */ | |
40 | .hairline-info { | |
41 | border: 1px solid black; | |
42 | border-top-right-radius: 5px; | |
43 | border-bottom-right-radius: 5px; | |
44 | ||
45 | display: table; /* shrink to fit */ | |
46 | min-width: 100px; | |
47 | ||
48 | z-index: 10; /* should appear on top of the chart */ | |
49 | padding: 3px; | |
50 | background: white; | |
51 | font-size: 14px; | |
52 | cursor: move; | |
53 | } | |
54 | ||
55 | .dygraph-hairline { | |
56 | /* border-right-style: dotted !important; */ | |
57 | cursor: move; | |
58 | } | |
59 | ||
60 | .dygraph-hairline.selected div { | |
61 | left: 2px !important; | |
62 | width: 2px !important; | |
63 | } | |
64 | .hairline-info.selected { | |
65 | border: 2px solid black; | |
66 | padding: 2px; | |
67 | } | |
68 | ||
69 | .annotation-info { | |
70 | background: white; | |
71 | border-width: 1px; | |
72 | border-style: solid; | |
73 | padding: 4px; | |
74 | display: table; /* shrink to fit */ | |
75 | box-shadow: 0 0 4px gray; | |
76 | cursor: move; | |
77 | ||
78 | min-width: 120px; /* prevents squishing at the right edge of the chart */ | |
79 | } | |
80 | .annotation-info.editable { | |
81 | min-width: 180px; /* prevents squishing at the right edge of the chart */ | |
82 | } | |
83 | ||
84 | .dygraph-annotation-line { | |
85 | box-shadow: 0 0 4px gray; | |
86 | } | |
87 | </style> | |
88 | </head> | |
89 | <body> | |
90 | <h2>Hairlines Demo</h2> | |
91 | ||
92 | <p>Click the chart to add a hairline. Drag the hairline to move it.</p> | |
93 | <p>Click a point to add an editable annotation. Drag it to move it up/down.</p> | |
94 | ||
95 | <!-- | |
96 | The "info box" for each hairline is based on this template. | |
97 | Customize it as you wish. The .hairline-legend element will be populated | |
98 | with data about the current points and the .hairline-kill-button element | |
99 | will remove the hairline when clicked. Everything else will be untouched. | |
100 | --> | |
101 | <div id="hairline-template" class="hairline-info" style="display:none"> | |
102 | <button class='hairline-kill-button'>Kill</button> | |
103 | <div class='hairline-legend'></div> | |
104 | </div> | |
105 | <div id="annotation-template" class="annotation-info" style="display:none"> | |
106 | <div>{{text}}</div> | |
107 | <div>{{x}}, {{series}}: {{y}}</div> | |
108 | </div> | |
109 | <div id="annotation-editable-template" class="annotation-info" style="display:none"> | |
110 | <button class='annotation-kill-button'>Delete</button> | |
111 | <button class='annotation-update'>Change</button> | |
112 | <button class='annotation-cancel'>Cancel</button><br/> | |
113 | <input dg-ann-field='text' type='text' size=30 value='{{text}}' /> | |
114 | <div>{{x}}, {{series}}: {{y}}</div> | |
115 | </div> | |
116 | <script type="text/javascript"> | |
117 | $(document).on('keyup', '.annotation-info input', function(e) { | |
118 | var $annotationDiv = $(this).parent('.annotation-info'); | |
119 | if (e.keyCode == 13 || e.keyCode == 10) { // enter | |
120 | $annotationDiv.find('.annotation-update').click(); | |
121 | } else if (e.keyCode == 27) { // escape | |
122 | $annotationDiv.find('.annotation-cancel').click(); | |
123 | } | |
124 | }) | |
125 | .on('dblclick', '.annotation-info', function(e) { | |
126 | if (e.target.tagName == 'INPUT') return; | |
127 | $(this).find('.annotation-cancel').click(); | |
128 | }); | |
129 | </script> | |
130 | ||
131 | <div id="demodiv"></div> | |
132 | <div id="status"></div> | |
133 | ||
134 | <div id="controls"> | |
135 | <input type="checkbox" id="update" checked=true><label for="update"> Update</label> | |
136 | ||
137 | <button id="add-button">Add a Hairline</button> | |
138 | <button id="remove-button">Remove a Hairline</button> | |
139 | <button id="reset-button">Reset Hairlines</button> | |
140 | <br/> | |
141 | Hairline mode: | |
142 | <input type=radio name="hairline-mode" id="hairline-interpolated" checked=true> | |
143 | <label for="hairline-interpolated"> Interpolated</label> | |
144 | <input type=radio name="hairline-mode" id="hairline-closest"> | |
145 | <label for="hairline-closest"> Closest</label> | |
146 | ||
147 | <p>Learn more about the <a href="https://docs.google.com/document/d/1OHNE8BNNmMtFlRQ969DACIYIJ9VVJ7w3dSPRJDEeIew/edit">Hairlines/Super-annotations plugins and their APIs</a>.</p> | |
148 | ||
149 | </div> | |
150 | ||
151 | ||
152 | <script type="text/javascript"> | |
153 | var last_t = 0; | |
154 | var data = []; | |
155 | var fn = function(t) { | |
156 | return Math.sin(Math.PI/180 * t * 4); | |
157 | }; | |
158 | for (; last_t < 200; last_t++) { | |
159 | data.push([last_t, fn(last_t)]); | |
160 | } | |
161 | ||
162 | hairlines = new Dygraph.Plugins.Hairlines({ | |
163 | divFiller: function(div, data) { | |
164 | // This behavior is identical to what you'd get if you didn't set | |
165 | // this option. It illustrates how to write a 'divFiller'. | |
166 | var html = Dygraph.Plugins.Legend.generateLegendHTML( | |
167 | data.dygraph, data.hairline.xval, data.points, 10); | |
168 | $('.hairline-legend', div).html(html); | |
169 | $(div).data({xval: data.hairline.xval}); // see .hover() below. | |
170 | } | |
171 | }); | |
172 | annotations = new Dygraph.Plugins.SuperAnnotations({ | |
173 | defaultAnnotationProperties: { | |
174 | 'text': 'Annotation Description' | |
175 | } | |
176 | }); | |
177 | g = new Dygraph( | |
178 | document.getElementById("demodiv"), | |
179 | data, | |
180 | { | |
181 | labelsDiv: document.getElementById('status'), | |
182 | labelsSeparateLines: true, | |
183 | legend: 'always', | |
184 | labels: [ 'Time', 'Value' ], | |
185 | ||
186 | axes: { | |
187 | x: { | |
188 | valueFormatter: function(val) { | |
189 | return val.toFixed(2); | |
190 | } | |
191 | }, | |
192 | y: { | |
193 | pixelsPerLabel: 50 | |
194 | } | |
195 | }, | |
196 | ||
197 | // Set the plug-ins in the options. | |
198 | plugins : [ | |
199 | annotations, | |
200 | hairlines | |
201 | ] | |
202 | } | |
203 | ); | |
204 | ||
205 | var shouldUpdate = true; | |
206 | var update = function() { | |
207 | if (!shouldUpdate) return; | |
208 | data.push([last_t, fn(last_t)]); | |
209 | last_t++; | |
210 | data.splice(0, 1); | |
211 | g.updateOptions({file: data}); | |
212 | }; | |
213 | window.setInterval(update, 1000); | |
214 | ||
215 | // Control handlers | |
216 | $('#update').on('change', function() { | |
217 | shouldUpdate = $(this).is(':checked'); | |
218 | }); | |
219 | ||
220 | $('#add-button').on('click', function(e) { | |
221 | var h = hairlines.get(); | |
222 | h.push({xval: 137}); | |
223 | hairlines.set(h); | |
224 | }); | |
225 | $('#remove-button').on('click', function(e) { | |
226 | var h = hairlines.get(); | |
227 | if (h.length > 0) { | |
228 | var idx = Math.floor(h.length / 2); | |
229 | h.splice(idx, 1); | |
230 | } | |
231 | hairlines.set(h); | |
232 | }); | |
233 | $('#reset-button').on('click', function(e) { | |
234 | setDefaultState(); | |
235 | }); | |
236 | function setHairlineModeRadio() { | |
237 | var hs = hairlines.get(); | |
238 | if (hs.length) { | |
239 | var interpolated = hs[0].interpolated; | |
240 | $('#hairline-interpolated').prop('checked', interpolated); | |
241 | $('#hairline-closest').prop('checked', !interpolated); | |
242 | } | |
243 | } | |
244 | $('[name=hairline-mode]').change(function() { | |
245 | var interpolated = $('#hairline-interpolated').is(':checked'); | |
246 | var hs = hairlines.get(); | |
247 | for (var i = 0; i < hs.length; i++) { | |
248 | hs[i].interpolated = interpolated; | |
249 | } | |
250 | hairlines.set(hs); | |
251 | }); | |
252 | ||
253 | // Persistence | |
254 | function loadFromStorage() { | |
255 | hairlines.set(JSON.parse(localStorage.getItem('hairlines'))); | |
256 | annotations.set(JSON.parse(localStorage.getItem('annotations'))); | |
257 | setHairlineModeRadio(); | |
258 | } | |
259 | $(hairlines).on('hairlinesChanged', function(e) { | |
260 | localStorage.setItem('hairlines', JSON.stringify(hairlines.get())); | |
261 | setHairlineModeRadio(); | |
262 | }); | |
263 | $(annotations).on('annotationsChanged', function(e) { | |
264 | localStorage.setItem('annotations', JSON.stringify(annotations.get())); | |
265 | }); | |
266 | function setDefaultState() { | |
267 | // triggers 'hairlinesChanged' and 'annotationsChanged' events, above. | |
268 | hairlines.set([{xval: 55}]); | |
269 | annotations.set([{ | |
270 | xval: 67, | |
271 | series: 'Value', | |
272 | text: 'Bottom' | |
273 | }, | |
274 | { | |
275 | xval: 137, | |
276 | series: 'Value', | |
277 | text: 'Fast Change' | |
278 | }]); | |
279 | } | |
280 | ||
281 | if (!localStorage.getItem('hairlines') || | |
282 | !localStorage.getItem('annotations')) { | |
283 | setDefaultState(); | |
284 | } else { | |
285 | loadFromStorage(); | |
286 | } | |
287 | ||
288 | // Set focus on text box when you edit an annotation. | |
289 | $(annotations).on('beganEditAnnotation', function(e, a) { | |
290 | $('input[type=text]', a.infoDiv).focus(); | |
291 | }); | |
292 | ||
293 | // Select/Deselect hairlines on click. | |
294 | $(document).on('click', '.hairline-info', function() { | |
295 | console.log('click'); | |
296 | var xval = $(this).data('xval'); | |
297 | var hs = hairlines.get(); | |
298 | for (var i = 0; i < hs.length; i++) { | |
299 | if (hs[i].xval == xval) { | |
300 | hs[i].selected = !hs[i].selected; | |
301 | } | |
302 | } | |
303 | hairlines.set(hs); | |
304 | }); | |
305 | ||
306 | // Demonstration of how to use various other event listeners | |
307 | $(hairlines).on({ | |
308 | 'hairlineMoved': function(e, data) { | |
309 | // console.log('hairline moved from', data.oldXVal, ' to ', data.newXVal); | |
310 | }, | |
311 | 'hairlineCreated': function(e, data) { | |
312 | console.log('hairline created at ', data.xval); | |
313 | }, | |
314 | 'hairlineDeleted': function(e, data) { | |
315 | console.log('hairline deleted at ', data.xval); | |
316 | } | |
317 | }); | |
318 | $(annotations).on({ | |
319 | 'annotationCreated': function(e, data) { | |
320 | console.log('annotation created at ', data.series, data.xval); | |
321 | }, | |
322 | 'annotationMoved': function(e, data) { | |
323 | console.log('annotation moved from ', data.oldYFrac, ' to ', data.newYFrac); | |
324 | }, | |
325 | 'annotationDeleted': function(e, data) { | |
326 | console.log('annotation deleted at ', data.series, data.xval); | |
327 | }, | |
328 | 'annotationEdited': function(e, data) { | |
329 | console.log('edited annotation at ', data.series, data.xval); | |
330 | }, | |
331 | 'cancelEditAnnotation': function(e, data) { | |
332 | console.log('edit canceled on annotation at ', data.series, data.xval); | |
333 | } | |
334 | }); | |
335 | ||
336 | // TODO(danvk): demonstrate other annotations API methods. | |
337 | </script> | |
338 | </body> | |
339 | </html> |