| @@ -52,8 +52,82 @@ | ||
| 52 | 52 | } |
| 53 | 53 | }); |
| 54 | 54 | })(); |
| 55 | 55 | |
| 56 | +/* "Scale X axis by date/time" option: the PHP side emits {x: <utc ms>, y: value} points on a linear | |
| 57 | + X scale whose tick callback and tooltip title callback are the two globals below. UTC getters are | |
| 58 | + used on purpose so a date never shifts by a day in the viewer's time zone. */ | |
| 59 | +(function () { | |
| 60 | + function pad(n) { return (n < 10 ? '0' : '') + n; } | |
| 61 | + function fmt(ms, withTime) { | |
| 62 | + var d = new Date(ms); | |
| 63 | + if (isNaN(d.getTime())) return ms; | |
| 64 | + var s = d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()); | |
| 65 | + if (withTime) s += ' ' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()); | |
| 66 | + return s; | |
| 67 | + } | |
| 68 | + function hasTime(chart) { | |
| 69 | + if (!chart || chart.__gvnHasTime !== undefined) return chart ? chart.__gvnHasTime : false; | |
| 70 | + var found = false; | |
| 71 | + (chart.data.datasets || []).forEach(function (ds) { | |
| 72 | + (ds.data || []).forEach(function (p) { | |
| 73 | + if (p && typeof p === 'object' && p.x % 86400000 !== 0) found = true; | |
| 74 | + }); | |
| 75 | + }); | |
| 76 | + chart.__gvnHasTime = found; | |
| 77 | + return found; | |
| 78 | + } | |
| 79 | + // Labels of dates that sit too close together on the axis are moved to a lower line (one below the | |
| 80 | + // other) instead of being drawn over each other. Returns the line level (0, 1, 2) per tick index. | |
| 81 | + function staggerLevels(scale, ticks, withTime) { | |
| 82 | + var sig = ticks.length + ':' + scale.min + ':' + scale.max + ':' + scale.width; | |
| 83 | + if (scale.__gvnStagger && scale.__gvnStagger.sig === sig) return scale.__gvnStagger.levels; | |
| 84 | + var span = (scale.max - scale.min) || 1, width = scale.width || 0, levels = [], px = [], i, j, L, ok; | |
| 85 | + var labelW = (withTime ? 16 : 10) * 7 + 12; // approx. pixel width of "YYYY-MM-DD[ HH:MM]" plus a gap | |
| 86 | + for (i = 0; i < ticks.length; i++) { | |
| 87 | + px[i] = (ticks[i].value - scale.min) / span * width; | |
| 88 | + for (L = 0; L < 3; L++) { | |
| 89 | + ok = true; | |
| 90 | + for (j = 0; j < i; j++) if (levels[j] === L && Math.abs(px[i] - px[j]) < labelW) { ok = false; break; } | |
| 91 | + if (ok) break; | |
| 92 | + } | |
| 93 | + levels[i] = ok ? L : -1; // -1: no free line, the label is hidden (the tooltip still shows the date) | |
| 94 | + } | |
| 95 | + scale.__gvnStagger = { sig: sig, levels: levels }; | |
| 96 | + return levels; | |
| 97 | + } | |
| 98 | + // scales.x.ticks.callback – "this" is the scale | |
| 99 | + window.gvnSqlChartsTimeTick = function (value, index, ticks) { | |
| 100 | + var withTime = hasTime(this && this.chart); | |
| 101 | + var label = fmt(value, withTime); | |
| 102 | + if (!this || !ticks || !ticks.length || typeof index !== 'number') return label; | |
| 103 | + var level = staggerLevels(this, ticks, withTime)[index] || 0; | |
| 104 | + if (level < 0) return ''; | |
| 105 | + if (!level) return label; | |
| 106 | + var lines = []; | |
| 107 | + for (var k = 0; k < level; k++) lines.push(''); | |
| 108 | + lines.push(label); | |
| 109 | + return lines; | |
| 110 | + }; | |
| 111 | + // scales.x.afterBuildTicks – label only the dates that exist in the data instead of evenly spaced values | |
| 112 | + window.gvnSqlChartsTimeTicks = function (scale) { | |
| 113 | + var seen = {}, xs = []; | |
| 114 | + (scale.chart.data.datasets || []).forEach(function (ds, i) { | |
| 115 | + if (scale.chart.getDatasetMeta(i).hidden) return; | |
| 116 | + (ds.data || []).forEach(function (p) { | |
| 117 | + if (p && typeof p === 'object' && typeof p.x === 'number' && !seen[p.x]) { seen[p.x] = true; xs.push(p.x); } | |
| 118 | + }); | |
| 119 | + }); | |
| 120 | + if (!xs.length) return; | |
| 121 | + xs.sort(function (a, b) { return a - b; }); | |
| 122 | + scale.ticks = xs.map(function (v) { return { value: v }; }); | |
| 123 | + }; | |
| 124 | + // plugins.tooltip.callbacks.title | |
| 125 | + window.gvnSqlChartsTimeTooltipTitle = function (items) { | |
| 126 | + return items && items.length ? fmt(items[0].parsed.x, hasTime(items[0].chart)) : ''; | |
| 127 | + }; | |
| 128 | +})(); | |
| 129 | + | |
| 56 | 130 | /* Datepicker for [type: date] dynamic filters */ |
| 57 | 131 | jQuery(function ($) { |
| 58 | 132 | if ($.fn.datepicker) { |
| 59 | 133 | $('[data-toggle="datepicker"]').datepicker({ format: 'yyyy-mm-dd', autoHide: true }); |