|
1 /* Pretty handling of time axes. |
|
2 |
|
3 Copyright (c) 2007-2014 IOLA and Ole Laursen. |
|
4 Licensed under the MIT license. |
|
5 |
|
6 Set axis.mode to "time" to enable. See the section "Time series data" in |
|
7 API.txt for details. |
|
8 */ |
|
9 |
|
10 (function($) { |
|
11 'use strict'; |
|
12 |
|
13 var options = { |
|
14 xaxis: { |
|
15 timezone: null, // "browser" for local to the client or timezone for timezone-js |
|
16 timeformat: null, // format string to use |
|
17 twelveHourClock: false, // 12 or 24 time in time mode |
|
18 monthNames: null, // list of names of months |
|
19 timeBase: 'seconds' // are the values in given in mircoseconds, milliseconds or seconds |
|
20 }, |
|
21 yaxis: { |
|
22 timeBase: 'seconds' |
|
23 } |
|
24 }; |
|
25 |
|
26 var floorInBase = $.plot.saturated.floorInBase; |
|
27 |
|
28 // Method to provide microsecond support to Date like classes. |
|
29 var CreateMicroSecondDate = function(dateType, microEpoch) { |
|
30 var newDate = new dateType(microEpoch); |
|
31 |
|
32 var oldSetTime = newDate.setTime.bind(newDate); |
|
33 newDate.update = function(microEpoch) { |
|
34 oldSetTime(microEpoch); |
|
35 |
|
36 // Round epoch to 3 decimal accuracy |
|
37 microEpoch = Math.round(microEpoch*1000)/1000; |
|
38 |
|
39 // Microseconds are stored as integers |
|
40 var seconds = microEpoch/1000; |
|
41 this.microseconds = 1000000 * (seconds - Math.floor(seconds)); |
|
42 }; |
|
43 |
|
44 var oldGetTime = newDate.getTime.bind(newDate); |
|
45 newDate.getTime = function () { |
|
46 var microEpoch = oldGetTime() + this.microseconds / 1000; |
|
47 return microEpoch; |
|
48 }; |
|
49 |
|
50 newDate.setTime = function (microEpoch) { |
|
51 this.update(microEpoch); |
|
52 }; |
|
53 |
|
54 newDate.getMicroseconds = function() { |
|
55 return this.microseconds; |
|
56 }; |
|
57 |
|
58 newDate.setMicroseconds = function(microseconds) { |
|
59 // Replace the microsecond part (6 last digits) in microEpoch |
|
60 var epochWithoutMicroseconds = oldGetTime(); |
|
61 var newEpoch = epochWithoutMicroseconds + microseconds/1000; |
|
62 this.update(newEpoch); |
|
63 }; |
|
64 |
|
65 newDate.setUTCMicroseconds = function(microseconds) { this.setMicroseconds(microseconds); } |
|
66 |
|
67 newDate.getUTCMicroseconds = function() { return this.getMicroseconds(); } |
|
68 |
|
69 newDate.microseconds = null; |
|
70 newDate.microEpoch = null; |
|
71 newDate.update(microEpoch); |
|
72 return newDate; |
|
73 } |
|
74 |
|
75 // Returns a string with the date d formatted according to fmt. |
|
76 // A subset of the Open Group's strftime format is supported. |
|
77 |
|
78 function formatDate(d, fmt, monthNames, dayNames) { |
|
79 if (typeof d.strftime === "function") { |
|
80 return d.strftime(fmt); |
|
81 } |
|
82 |
|
83 var leftPad = function(n, pad) { |
|
84 n = "" + n; |
|
85 pad = "" + (pad == null ? "0" : pad); |
|
86 return n.length == 1 ? pad + n : n; |
|
87 }; |
|
88 |
|
89 var formatMicroseconds = function(n, dec) { |
|
90 if (dec < 6 && dec > 0) { |
|
91 var magnitude = parseFloat('1e' + (dec-6)); |
|
92 n = Math.round(Math.round(n*magnitude)/magnitude); |
|
93 n = ('00000' + n).slice(-6,-(6 - dec)); |
|
94 } else { |
|
95 n = Math.round(n) |
|
96 n = ('00000' + n).slice(-6); |
|
97 } |
|
98 return n; |
|
99 }; |
|
100 |
|
101 var r = []; |
|
102 var escape = false; |
|
103 var hours = d.getHours(); |
|
104 var isAM = hours < 12; |
|
105 |
|
106 if (!monthNames) { |
|
107 monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; |
|
108 } |
|
109 |
|
110 if (!dayNames) { |
|
111 dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; |
|
112 } |
|
113 |
|
114 var hours12; |
|
115 if (hours > 12) { |
|
116 hours12 = hours - 12; |
|
117 } else if (hours == 0) { |
|
118 hours12 = 12; |
|
119 } else { |
|
120 hours12 = hours; |
|
121 } |
|
122 |
|
123 var decimals = -1; |
|
124 for (var i = 0; i < fmt.length; ++i) { |
|
125 var c = fmt.charAt(i); |
|
126 |
|
127 if (!isNaN(Number(c)) && Number(c) > 0) { |
|
128 decimals = Number(c); |
|
129 } else if (escape) { |
|
130 switch (c) { |
|
131 case 'a': c = "" + dayNames[d.getDay()]; break; |
|
132 case 'b': c = "" + monthNames[d.getMonth()]; break; |
|
133 case 'd': c = leftPad(d.getDate()); break; |
|
134 case 'e': c = leftPad(d.getDate(), " "); break; |
|
135 case 'h': // For back-compat with 0.7; remove in 1.0 |
|
136 case 'H': c = leftPad(hours); break; |
|
137 case 'I': c = leftPad(hours12); break; |
|
138 case 'l': c = leftPad(hours12, " "); break; |
|
139 case 'm': c = leftPad(d.getMonth() + 1); break; |
|
140 case 'M': c = leftPad(d.getMinutes()); break; |
|
141 // quarters not in Open Group's strftime specification |
|
142 case 'q': |
|
143 c = "" + (Math.floor(d.getMonth() / 3) + 1); break; |
|
144 case 'S': c = leftPad(d.getSeconds()); break; |
|
145 case 's': c = "" + formatMicroseconds(d.getMicroseconds(),decimals); break; |
|
146 case 'y': c = leftPad(d.getFullYear() % 100); break; |
|
147 case 'Y': c = "" + d.getFullYear(); break; |
|
148 case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; |
|
149 case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; |
|
150 case 'w': c = "" + d.getDay(); break; |
|
151 } |
|
152 r.push(c); |
|
153 escape = false; |
|
154 } else { |
|
155 if (c == "%") { |
|
156 escape = true; |
|
157 } else { |
|
158 r.push(c); |
|
159 } |
|
160 } |
|
161 } |
|
162 |
|
163 return r.join(""); |
|
164 } |
|
165 |
|
166 // To have a consistent view of time-based data independent of which time |
|
167 // zone the client happens to be in we need a date-like object independent |
|
168 // of time zones. This is done through a wrapper that only calls the UTC |
|
169 // versions of the accessor methods. |
|
170 |
|
171 function makeUtcWrapper(d) { |
|
172 function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) { |
|
173 sourceObj[sourceMethod] = function() { |
|
174 return targetObj[targetMethod].apply(targetObj, arguments); |
|
175 }; |
|
176 } |
|
177 |
|
178 var utc = { |
|
179 date: d |
|
180 }; |
|
181 |
|
182 // support strftime, if found |
|
183 if (d.strftime !== undefined) { |
|
184 addProxyMethod(utc, "strftime", d, "strftime"); |
|
185 } |
|
186 |
|
187 addProxyMethod(utc, "getTime", d, "getTime"); |
|
188 addProxyMethod(utc, "setTime", d, "setTime"); |
|
189 |
|
190 var props = ["Date", "Day", "FullYear", "Hours", "Minutes", "Month", "Seconds", "Milliseconds", "Microseconds"]; |
|
191 |
|
192 for (var p = 0; p < props.length; p++) { |
|
193 addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); |
|
194 addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); |
|
195 } |
|
196 |
|
197 return utc; |
|
198 } |
|
199 |
|
200 // select time zone strategy. This returns a date-like object tied to the |
|
201 // desired timezone |
|
202 function dateGenerator(ts, opts) { |
|
203 var maxDateValue = 8640000000000000; |
|
204 |
|
205 if (opts && opts.timeBase === 'seconds') { |
|
206 ts *= 1000; |
|
207 } else if (opts.timeBase === 'microseconds') { |
|
208 ts /= 1000; |
|
209 } |
|
210 |
|
211 if (ts > maxDateValue) { |
|
212 ts = maxDateValue; |
|
213 } else if (ts < -maxDateValue) { |
|
214 ts = -maxDateValue; |
|
215 } |
|
216 |
|
217 if (opts.timezone === "browser") { |
|
218 return CreateMicroSecondDate(Date, ts); |
|
219 } else if (!opts.timezone || opts.timezone === "utc") { |
|
220 return makeUtcWrapper(CreateMicroSecondDate(Date, ts)); |
|
221 } else if (typeof timezoneJS !== "undefined" && typeof timezoneJS.Date !== "undefined") { |
|
222 var d = CreateMicroSecondDate(timezoneJS.Date, ts); |
|
223 // timezone-js is fickle, so be sure to set the time zone before |
|
224 // setting the time. |
|
225 d.setTimezone(opts.timezone); |
|
226 d.setTime(ts); |
|
227 return d; |
|
228 } else { |
|
229 return makeUtcWrapper(CreateMicroSecondDate(Date, ts)); |
|
230 } |
|
231 } |
|
232 |
|
233 // map of app. size of time units in seconds |
|
234 var timeUnitSizeSeconds = { |
|
235 "microsecond": 0.000001, |
|
236 "millisecond": 0.001, |
|
237 "second": 1, |
|
238 "minute": 60, |
|
239 "hour": 60 * 60, |
|
240 "day": 24 * 60 * 60, |
|
241 "month": 30 * 24 * 60 * 60, |
|
242 "quarter": 3 * 30 * 24 * 60 * 60, |
|
243 "year": 365.2425 * 24 * 60 * 60 |
|
244 }; |
|
245 |
|
246 // map of app. size of time units in milliseconds |
|
247 var timeUnitSizeMilliseconds = { |
|
248 "microsecond": 0.001, |
|
249 "millisecond": 1, |
|
250 "second": 1000, |
|
251 "minute": 60 * 1000, |
|
252 "hour": 60 * 60 * 1000, |
|
253 "day": 24 * 60 * 60 * 1000, |
|
254 "month": 30 * 24 * 60 * 60 * 1000, |
|
255 "quarter": 3 * 30 * 24 * 60 * 60 * 1000, |
|
256 "year": 365.2425 * 24 * 60 * 60 * 1000 |
|
257 }; |
|
258 |
|
259 // map of app. size of time units in microseconds |
|
260 var timeUnitSizeMicroseconds = { |
|
261 "microsecond": 1, |
|
262 "millisecond": 1000, |
|
263 "second": 1000000, |
|
264 "minute": 60 * 1000000, |
|
265 "hour": 60 * 60 * 1000000, |
|
266 "day": 24 * 60 * 60 * 1000000, |
|
267 "month": 30 * 24 * 60 * 60 * 1000000, |
|
268 "quarter": 3 * 30 * 24 * 60 * 60 * 1000000, |
|
269 "year": 365.2425 * 24 * 60 * 60 * 1000000 |
|
270 }; |
|
271 |
|
272 // the allowed tick sizes, after 1 year we use |
|
273 // an integer algorithm |
|
274 |
|
275 var baseSpec = [ |
|
276 [1, "microsecond"], [2, "microsecond"], [5, "microsecond"], [10, "microsecond"], |
|
277 [25, "microsecond"], [50, "microsecond"], [100, "microsecond"], [250, "microsecond"], [500, "microsecond"], |
|
278 [1, "millisecond"], [2, "millisecond"], [5, "millisecond"], [10, "millisecond"], |
|
279 [25, "millisecond"], [50, "millisecond"], [100, "millisecond"], [250, "millisecond"], [500, "millisecond"], |
|
280 [1, "second"], [2, "second"], [5, "second"], [10, "second"], |
|
281 [30, "second"], |
|
282 [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], |
|
283 [30, "minute"], |
|
284 [1, "hour"], [2, "hour"], [4, "hour"], |
|
285 [8, "hour"], [12, "hour"], |
|
286 [1, "day"], [2, "day"], [3, "day"], |
|
287 [0.25, "month"], [0.5, "month"], [1, "month"], |
|
288 [2, "month"] |
|
289 ]; |
|
290 |
|
291 // we don't know which variant(s) we'll need yet, but generating both is |
|
292 // cheap |
|
293 |
|
294 var specMonths = baseSpec.concat([[3, "month"], [6, "month"], |
|
295 [1, "year"]]); |
|
296 var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"], |
|
297 [1, "year"]]); |
|
298 |
|
299 |
|
300 function dateTickGenerator(axis) { |
|
301 var opts = axis.options, |
|
302 ticks = [], |
|
303 d = dateGenerator(axis.min, opts), |
|
304 minSize = 0; |
|
305 |
|
306 // make quarter use a possibility if quarters are |
|
307 // mentioned in either of these options |
|
308 var spec = (opts.tickSize && opts.tickSize[1] === |
|
309 "quarter") || |
|
310 (opts.minTickSize && opts.minTickSize[1] === |
|
311 "quarter") ? specQuarters : specMonths; |
|
312 |
|
313 var timeUnitSize; |
|
314 if (opts.timeBase === 'seconds') { |
|
315 timeUnitSize = timeUnitSizeSeconds; |
|
316 } else if (opts.timeBase === 'microseconds') { |
|
317 timeUnitSize = timeUnitSizeMicroseconds; |
|
318 } else { |
|
319 timeUnitSize = timeUnitSizeMilliseconds; |
|
320 } |
|
321 |
|
322 if (opts.minTickSize !== null && opts.minTickSize !== undefined) { |
|
323 if (typeof opts.tickSize === "number") { |
|
324 minSize = opts.tickSize; |
|
325 } else { |
|
326 minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; |
|
327 } |
|
328 } |
|
329 |
|
330 for (var i = 0; i < spec.length - 1; ++i) { |
|
331 if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] + |
|
332 spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 && |
|
333 spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) { |
|
334 break; |
|
335 } |
|
336 } |
|
337 |
|
338 var size = spec[i][0]; |
|
339 var unit = spec[i][1]; |
|
340 // special-case the possibility of several years |
|
341 if (unit === "year") { |
|
342 // if given a minTickSize in years, just use it, |
|
343 // ensuring that it's an integer |
|
344 |
|
345 if (opts.minTickSize !== null && opts.minTickSize !== undefined && opts.minTickSize[1] === "year") { |
|
346 size = Math.floor(opts.minTickSize[0]); |
|
347 } else { |
|
348 var magn = parseFloat('1e' + Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); |
|
349 var norm = (axis.delta / timeUnitSize.year) / magn; |
|
350 |
|
351 if (norm < 1.5) { |
|
352 size = 1; |
|
353 } else if (norm < 3) { |
|
354 size = 2; |
|
355 } else if (norm < 7.5) { |
|
356 size = 5; |
|
357 } else { |
|
358 size = 10; |
|
359 } |
|
360 |
|
361 size *= magn; |
|
362 } |
|
363 |
|
364 // minimum size for years is 1 |
|
365 |
|
366 if (size < 1) { |
|
367 size = 1; |
|
368 } |
|
369 } |
|
370 |
|
371 axis.tickSize = opts.tickSize || [size, unit]; |
|
372 var tickSize = axis.tickSize[0]; |
|
373 unit = axis.tickSize[1]; |
|
374 |
|
375 var step = tickSize * timeUnitSize[unit]; |
|
376 |
|
377 if (unit === "microsecond") { |
|
378 d.setMicroseconds(floorInBase(d.getMicroseconds(), tickSize)); |
|
379 } else if (unit === "millisecond") { |
|
380 d.setMilliseconds(floorInBase(d.getMilliseconds(), tickSize)); |
|
381 } else if (unit === "second") { |
|
382 d.setSeconds(floorInBase(d.getSeconds(), tickSize)); |
|
383 } else if (unit === "minute") { |
|
384 d.setMinutes(floorInBase(d.getMinutes(), tickSize)); |
|
385 } else if (unit === "hour") { |
|
386 d.setHours(floorInBase(d.getHours(), tickSize)); |
|
387 } else if (unit === "month") { |
|
388 d.setMonth(floorInBase(d.getMonth(), tickSize)); |
|
389 } else if (unit === "quarter") { |
|
390 d.setMonth(3 * floorInBase(d.getMonth() / 3, |
|
391 tickSize)); |
|
392 } else if (unit === "year") { |
|
393 d.setFullYear(floorInBase(d.getFullYear(), tickSize)); |
|
394 } |
|
395 |
|
396 // reset smaller components |
|
397 |
|
398 if (step >= timeUnitSize.millisecond) { |
|
399 if (step >= timeUnitSize.second) { |
|
400 d.setMicroseconds(0); |
|
401 } else { |
|
402 d.setMicroseconds(d.getMilliseconds()*1000); |
|
403 } |
|
404 } |
|
405 if (step >= timeUnitSize.minute) { |
|
406 d.setSeconds(0); |
|
407 } |
|
408 if (step >= timeUnitSize.hour) { |
|
409 d.setMinutes(0); |
|
410 } |
|
411 if (step >= timeUnitSize.day) { |
|
412 d.setHours(0); |
|
413 } |
|
414 if (step >= timeUnitSize.day * 4) { |
|
415 d.setDate(1); |
|
416 } |
|
417 if (step >= timeUnitSize.month * 2) { |
|
418 d.setMonth(floorInBase(d.getMonth(), 3)); |
|
419 } |
|
420 if (step >= timeUnitSize.quarter * 2) { |
|
421 d.setMonth(floorInBase(d.getMonth(), 6)); |
|
422 } |
|
423 if (step >= timeUnitSize.year) { |
|
424 d.setMonth(0); |
|
425 } |
|
426 |
|
427 var carry = 0; |
|
428 var v = Number.NaN; |
|
429 var v1000; |
|
430 var prev; |
|
431 do { |
|
432 prev = v; |
|
433 v1000 = d.getTime(); |
|
434 if (opts && opts.timeBase === 'seconds') { |
|
435 v = v1000 / 1000; |
|
436 } else if (opts && opts.timeBase === 'microseconds') { |
|
437 v = v1000 * 1000; |
|
438 } else { |
|
439 v = v1000; |
|
440 } |
|
441 |
|
442 ticks.push(v); |
|
443 |
|
444 if (unit === "month" || unit === "quarter") { |
|
445 if (tickSize < 1) { |
|
446 // a bit complicated - we'll divide the |
|
447 // month/quarter up but we need to take |
|
448 // care of fractions so we don't end up in |
|
449 // the middle of a day |
|
450 d.setDate(1); |
|
451 var start = d.getTime(); |
|
452 d.setMonth(d.getMonth() + |
|
453 (unit === "quarter" ? 3 : 1)); |
|
454 var end = d.getTime(); |
|
455 d.setTime((v + carry * timeUnitSize.hour + (end - start) * tickSize)); |
|
456 carry = d.getHours(); |
|
457 d.setHours(0); |
|
458 } else { |
|
459 d.setMonth(d.getMonth() + |
|
460 tickSize * (unit === "quarter" ? 3 : 1)); |
|
461 } |
|
462 } else if (unit === "year") { |
|
463 d.setFullYear(d.getFullYear() + tickSize); |
|
464 } else { |
|
465 if (opts.timeBase === 'seconds') { |
|
466 d.setTime((v + step) * 1000); |
|
467 } else if (opts.timeBase === 'microseconds') { |
|
468 d.setTime((v + step) / 1000); |
|
469 } else { |
|
470 d.setTime(v + step); |
|
471 } |
|
472 } |
|
473 } while (v < axis.max && v !== prev); |
|
474 |
|
475 return ticks; |
|
476 }; |
|
477 |
|
478 function init(plot) { |
|
479 plot.hooks.processOptions.push(function (plot) { |
|
480 $.each(plot.getAxes(), function(axisName, axis) { |
|
481 var opts = axis.options; |
|
482 if (opts.mode === "time") { |
|
483 axis.tickGenerator = dateTickGenerator; |
|
484 |
|
485 axis.tickFormatter = function (v, axis) { |
|
486 var d = dateGenerator(v, axis.options); |
|
487 |
|
488 // first check global format |
|
489 if (opts.timeformat != null) { |
|
490 return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames); |
|
491 } |
|
492 |
|
493 // possibly use quarters if quarters are mentioned in |
|
494 // any of these places |
|
495 var useQuarters = (axis.options.tickSize && |
|
496 axis.options.tickSize[1] == "quarter") || |
|
497 (axis.options.minTickSize && |
|
498 axis.options.minTickSize[1] == "quarter"); |
|
499 |
|
500 var timeUnitSize; |
|
501 if (opts.timeBase === 'seconds') { |
|
502 timeUnitSize = timeUnitSizeSeconds; |
|
503 } else if (opts.timeBase === 'microseconds') { |
|
504 timeUnitSize = timeUnitSizeMicroseconds; |
|
505 } else { |
|
506 timeUnitSize = timeUnitSizeMilliseconds; |
|
507 } |
|
508 |
|
509 var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; |
|
510 var span = axis.max - axis.min; |
|
511 var suffix = (opts.twelveHourClock) ? " %p" : ""; |
|
512 var hourCode = (opts.twelveHourClock) ? "%I" : "%H"; |
|
513 var factor; |
|
514 var fmt; |
|
515 |
|
516 if (opts.timeBase === 'seconds') { |
|
517 factor = 1; |
|
518 } else if (opts.timeBase === 'microseconds') { |
|
519 factor = 1000000 |
|
520 } else { |
|
521 factor = 1000; |
|
522 } |
|
523 |
|
524 if (t < timeUnitSize.second) { |
|
525 var decimals = -Math.floor(Math.log10(t/factor)) |
|
526 |
|
527 // the two-and-halves require an additional decimal |
|
528 if (String(t).indexOf('25') > -1) { |
|
529 decimals++; |
|
530 } |
|
531 |
|
532 fmt = "%S.%" + decimals + "s"; |
|
533 } else |
|
534 if (t < timeUnitSize.minute) { |
|
535 fmt = hourCode + ":%M:%S" + suffix; |
|
536 } else if (t < timeUnitSize.day) { |
|
537 if (span < 2 * timeUnitSize.day) { |
|
538 fmt = hourCode + ":%M" + suffix; |
|
539 } else { |
|
540 fmt = "%b %d " + hourCode + ":%M" + suffix; |
|
541 } |
|
542 } else if (t < timeUnitSize.month) { |
|
543 fmt = "%b %d"; |
|
544 } else if ((useQuarters && t < timeUnitSize.quarter) || |
|
545 (!useQuarters && t < timeUnitSize.year)) { |
|
546 if (span < timeUnitSize.year) { |
|
547 fmt = "%b"; |
|
548 } else { |
|
549 fmt = "%b %Y"; |
|
550 } |
|
551 } else if (useQuarters && t < timeUnitSize.year) { |
|
552 if (span < timeUnitSize.year) { |
|
553 fmt = "Q%q"; |
|
554 } else { |
|
555 fmt = "Q%q %Y"; |
|
556 } |
|
557 } else { |
|
558 fmt = "%Y"; |
|
559 } |
|
560 |
|
561 var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames); |
|
562 |
|
563 return rt; |
|
564 }; |
|
565 } |
|
566 }); |
|
567 }); |
|
568 } |
|
569 |
|
570 $.plot.plugins.push({ |
|
571 init: init, |
|
572 options: options, |
|
573 name: 'time', |
|
574 version: '1.0' |
|
575 }); |
|
576 |
|
577 // Time-axis support used to be in Flot core, which exposed the |
|
578 // formatDate function on the plot object. Various plugins depend |
|
579 // on the function, so we need to re-expose it here. |
|
580 |
|
581 $.plot.formatDate = formatDate; |
|
582 $.plot.dateGenerator = dateGenerator; |
|
583 $.plot.dateTickGenerator = dateTickGenerator; |
|
584 $.plot.makeUtcWrapper = makeUtcWrapper; |
|
585 })(jQuery); |