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