PluginProbe
Property Hive / trunk
Property Hive vtrunk
2.4.0 2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 All 262 releases
propertyhive / assets / js / jquery-flot / jquery.flot.time.js

jquery.flot.time.js in Property Hive trunk, at assets/js/jquery-flot/jquery.flot.time.js

432 lines 11.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* Pretty handling of time axes.
2
3 Copyright (c) 2007-2013 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
11 (function($) {
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 }
20 };
21
22 // round to nearby lower multiple of base
23
24 function floorInBase(n, base) {
25 return base * Math.floor(n / base);
26 }
27
28 // Returns a string with the date d formatted according to fmt.
29 // A subset of the Open Group's strftime format is supported.
30
31 function formatDate(d, fmt, monthNames, dayNames) {
32
33 if (typeof d.strftime == "function") {
34 return d.strftime(fmt);
35 }
36
37 var leftPad = function(n, pad) {
38 n = "" + n;
39 pad = "" + (pad == null ? "0" : pad);
40 return n.length == 1 ? pad + n : n;
41 };
42
43 var r = [];
44 var escape = false;
45 var hours = d.getHours();
46 var isAM = hours < 12;
47
48 if (monthNames == null) {
49 monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
50 }
51
52 if (dayNames == null) {
53 dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
54 }
55
56 var hours12;
57
58 if (hours > 12) {
59 hours12 = hours - 12;
60 } else if (hours == 0) {
61 hours12 = 12;
62 } else {
63 hours12 = hours;
64 }
65
66 for (var i = 0; i < fmt.length; ++i) {
67
68 var c = fmt.charAt(i);
69
70 if (escape) {
71 switch (c) {
72 case 'a': c = "" + dayNames[d.getDay()]; break;
73 case 'b': c = "" + monthNames[d.getMonth()]; break;
74 case 'd': c = leftPad(d.getDate()); break;
75 case 'e': c = leftPad(d.getDate(), " "); break;
76 case 'h': // For back-compat with 0.7; remove in 1.0
77 case 'H': c = leftPad(hours); break;
78 case 'I': c = leftPad(hours12); break;
79 case 'l': c = leftPad(hours12, " "); break;
80 case 'm': c = leftPad(d.getMonth() + 1); break;
81 case 'M': c = leftPad(d.getMinutes()); break;
82 // quarters not in Open Group's strftime specification
83 case 'q':
84 c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
85 case 'S': c = leftPad(d.getSeconds()); break;
86 case 'y': c = leftPad(d.getFullYear() % 100); break;
87 case 'Y': c = "" + d.getFullYear(); break;
88 case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
89 case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
90 case 'w': c = "" + d.getDay(); break;
91 }
92 r.push(c);
93 escape = false;
94 } else {
95 if (c == "%") {
96 escape = true;
97 } else {
98 r.push(c);
99 }
100 }
101 }
102
103 return r.join("");
104 }
105
106 // To have a consistent view of time-based data independent of which time
107 // zone the client happens to be in we need a date-like object independent
108 // of time zones. This is done through a wrapper that only calls the UTC
109 // versions of the accessor methods.
110
111 function makeUtcWrapper(d) {
112
113 function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
114 sourceObj[sourceMethod] = function() {
115 return targetObj[targetMethod].apply(targetObj, arguments);
116 };
117 };
118
119 var utc = {
120 date: d
121 };
122
123 // support strftime, if found
124
125 if (d.strftime != undefined) {
126 addProxyMethod(utc, "strftime", d, "strftime");
127 }
128
129 addProxyMethod(utc, "getTime", d, "getTime");
130 addProxyMethod(utc, "setTime", d, "setTime");
131
132 var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
133
134 for (var p = 0; p < props.length; p++) {
135 addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
136 addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
137 }
138
139 return utc;
140 };
141
142 // select time zone strategy. This returns a date-like object tied to the
143 // desired timezone
144
145 function dateGenerator(ts, opts) {
146 if (opts.timezone == "browser") {
147 return new Date(ts);
148 } else if (!opts.timezone || opts.timezone == "utc") {
149 return makeUtcWrapper(new Date(ts));
150 } else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
151 var d = new timezoneJS.Date();
152 // timezone-js is fickle, so be sure to set the time zone before
153 // setting the time.
154 d.setTimezone(opts.timezone);
155 d.setTime(ts);
156 return d;
157 } else {
158 return makeUtcWrapper(new Date(ts));
159 }
160 }
161
162 // map of app. size of time units in milliseconds
163
164 var timeUnitSize = {
165 "second": 1000,
166 "minute": 60 * 1000,
167 "hour": 60 * 60 * 1000,
168 "day": 24 * 60 * 60 * 1000,
169 "month": 30 * 24 * 60 * 60 * 1000,
170 "quarter": 3 * 30 * 24 * 60 * 60 * 1000,
171 "year": 365.2425 * 24 * 60 * 60 * 1000
172 };
173
174 // the allowed tick sizes, after 1 year we use
175 // an integer algorithm
176
177 var baseSpec = [
178 [1, "second"], [2, "second"], [5, "second"], [10, "second"],
179 [30, "second"],
180 [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
181 [30, "minute"],
182 [1, "hour"], [2, "hour"], [4, "hour"],
183 [8, "hour"], [12, "hour"],
184 [1, "day"], [2, "day"], [3, "day"],
185 [0.25, "month"], [0.5, "month"], [1, "month"],
186 [2, "month"]
187 ];
188
189 // we don't know which variant(s) we'll need yet, but generating both is
190 // cheap
191
192 var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
193 [1, "year"]]);
194 var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
195 [1, "year"]]);
196
197 function init(plot) {
198 plot.hooks.processOptions.push(function (plot, options) {
199 $.each(plot.getAxes(), function(axisName, axis) {
200
201 var opts = axis.options;
202
203 if (opts.mode == "time") {
204 axis.tickGenerator = function(axis) {
205
206 var ticks = [];
207 var d = dateGenerator(axis.min, opts);
208 var minSize = 0;
209
210 // make quarter use a possibility if quarters are
211 // mentioned in either of these options
212
213 var spec = (opts.tickSize && opts.tickSize[1] ===
214 "quarter") ||
215 (opts.minTickSize && opts.minTickSize[1] ===
216 "quarter") ? specQuarters : specMonths;
217
218 if (opts.minTickSize != null) {
219 if (typeof opts.tickSize == "number") {
220 minSize = opts.tickSize;
221 } else {
222 minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
223 }
224 }
225
226 for (var i = 0; i < spec.length - 1; ++i) {
227 if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
228 + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
229 && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
230 break;
231 }
232 }
233
234 var size = spec[i][0];
235 var unit = spec[i][1];
236
237 // special-case the possibility of several years
238
239 if (unit == "year") {
240
241 // if given a minTickSize in years, just use it,
242 // ensuring that it's an integer
243
244 if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
245 size = Math.floor(opts.minTickSize[0]);
246 } else {
247
248 var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
249 var norm = (axis.delta / timeUnitSize.year) / magn;
250
251 if (norm < 1.5) {
252 size = 1;
253 } else if (norm < 3) {
254 size = 2;
255 } else if (norm < 7.5) {
256 size = 5;
257 } else {
258 size = 10;
259 }
260
261 size *= magn;
262 }
263
264 // minimum size for years is 1
265
266 if (size < 1) {
267 size = 1;
268 }
269 }
270
271 axis.tickSize = opts.tickSize || [size, unit];
272 var tickSize = axis.tickSize[0];
273 unit = axis.tickSize[1];
274
275 var step = tickSize * timeUnitSize[unit];
276
277 if (unit == "second") {
278 d.setSeconds(floorInBase(d.getSeconds(), tickSize));
279 } else if (unit == "minute") {
280 d.setMinutes(floorInBase(d.getMinutes(), tickSize));
281 } else if (unit == "hour") {
282 d.setHours(floorInBase(d.getHours(), tickSize));
283 } else if (unit == "month") {
284 d.setMonth(floorInBase(d.getMonth(), tickSize));
285 } else if (unit == "quarter") {
286 d.setMonth(3 * floorInBase(d.getMonth() / 3,
287 tickSize));
288 } else if (unit == "year") {
289 d.setFullYear(floorInBase(d.getFullYear(), tickSize));
290 }
291
292 // reset smaller components
293
294 d.setMilliseconds(0);
295
296 if (step >= timeUnitSize.minute) {
297 d.setSeconds(0);
298 }
299 if (step >= timeUnitSize.hour) {
300 d.setMinutes(0);
301 }
302 if (step >= timeUnitSize.day) {
303 d.setHours(0);
304 }
305 if (step >= timeUnitSize.day * 4) {
306 d.setDate(1);
307 }
308 if (step >= timeUnitSize.month * 2) {
309 d.setMonth(floorInBase(d.getMonth(), 3));
310 }
311 if (step >= timeUnitSize.quarter * 2) {
312 d.setMonth(floorInBase(d.getMonth(), 6));
313 }
314 if (step >= timeUnitSize.year) {
315 d.setMonth(0);
316 }
317
318 var carry = 0;
319 var v = Number.NaN;
320 var prev;
321
322 do {
323
324 prev = v;
325 v = d.getTime();
326 ticks.push(v);
327
328 if (unit == "month" || unit == "quarter") {
329 if (tickSize < 1) {
330
331 // a bit complicated - we'll divide the
332 // month/quarter up but we need to take
333 // care of fractions so we don't end up in
334 // the middle of a day
335
336 d.setDate(1);
337 var start = d.getTime();
338 d.setMonth(d.getMonth() +
339 (unit == "quarter" ? 3 : 1));
340 var end = d.getTime();
341 d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
342 carry = d.getHours();
343 d.setHours(0);
344 } else {
345 d.setMonth(d.getMonth() +
346 tickSize * (unit == "quarter" ? 3 : 1));
347 }
348 } else if (unit == "year") {
349 d.setFullYear(d.getFullYear() + tickSize);
350 } else {
351 d.setTime(v + step);
352 }
353 } while (v < axis.max && v != prev);
354
355 return ticks;
356 };
357
358 axis.tickFormatter = function (v, axis) {
359
360 var d = dateGenerator(v, axis.options);
361
362 // first check global format
363
364 if (opts.timeformat != null) {
365 return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
366 }
367
368 // possibly use quarters if quarters are mentioned in
369 // any of these places
370
371 var useQuarters = (axis.options.tickSize &&
372 axis.options.tickSize[1] == "quarter") ||
373 (axis.options.minTickSize &&
374 axis.options.minTickSize[1] == "quarter");
375
376 var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
377 var span = axis.max - axis.min;
378 var suffix = (opts.twelveHourClock) ? " %p" : "";
379 var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
380 var fmt;
381
382 if (t < timeUnitSize.minute) {
383 fmt = hourCode + ":%M:%S" + suffix;
384 } else if (t < timeUnitSize.day) {
385 if (span < 2 * timeUnitSize.day) {
386 fmt = hourCode + ":%M" + suffix;
387 } else {
388 fmt = "%b %d " + hourCode + ":%M" + suffix;
389 }
390 } else if (t < timeUnitSize.month) {
391 fmt = "%b %d";
392 } else if ((useQuarters && t < timeUnitSize.quarter) ||
393 (!useQuarters && t < timeUnitSize.year)) {
394 if (span < timeUnitSize.year) {
395 fmt = "%b";
396 } else {
397 fmt = "%b %Y";
398 }
399 } else if (useQuarters && t < timeUnitSize.year) {
400 if (span < timeUnitSize.year) {
401 fmt = "Q%q";
402 } else {
403 fmt = "Q%q %Y";
404 }
405 } else {
406 fmt = "%Y";
407 }
408
409 var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
410
411 return rt;
412 };
413 }
414 });
415 });
416 }
417
418 $.plot.plugins.push({
419 init: init,
420 options: options,
421 name: 'time',
422 version: '1.0'
423 });
424
425 // Time-axis support used to be in Flot core, which exposed the
426 // formatDate function on the plot object. Various plugins depend
427 // on the function, so we need to re-expose it here.
428
429 $.plot.formatDate = formatDate;
430
431 })(jQuery);
432