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