|
2568 | 2568 |
|
2569 | 2569 | })(jQuery); |
2570 | 2570 | /* |
2571 | | -Pretty handling of time axes. |
2572 | | -
|
2573 | | -Set axis.mode to "time" to enable. See the section "Time series data" in API.txt |
2574 | | -for details. |
2575 | | -*/ |
2576 | | -(function ($) { |
2577 | | - var options = {}; |
2578 | | - |
2579 | | - // round to nearby lower multiple of base |
2580 | | - function floorInBase(n, base) { |
2581 | | - return base * Math.floor(n / base); |
2582 | | - } |
2583 | | - |
2584 | | - // Returns a string with the date d formatted according to fmt. |
2585 | | - // A subset of the Open Group's strftime format is supported. |
2586 | | - function formatDate(d, fmt, monthNames, dayNames) { |
2587 | | - if (typeof d.strftime == "function") { |
2588 | | - return d.strftime(fmt); |
2589 | | - } |
2590 | | - var leftPad = function(n, pad) { |
2591 | | - n = "" + n; |
2592 | | - pad = "" + (pad == null ? "0" : pad); |
2593 | | - return n.length == 1 ? pad + n : n; |
2594 | | - }; |
2595 | | - |
2596 | | - var r = []; |
2597 | | - var escape = false; |
2598 | | - var hours = d.getHours(); |
2599 | | - var isAM = hours < 12; |
2600 | | - if (monthNames == null) |
2601 | | - monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; |
2602 | | - if (dayNames == null) |
2603 | | - dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; |
2604 | | - |
2605 | | - var hours12; |
2606 | | - if (hours > 12) { |
2607 | | - hours12 = hours - 12; |
2608 | | - } else if (hours == 0) { |
2609 | | - hours12 = 12; |
2610 | | - } else { |
2611 | | - hours12 = hours; |
2612 | | - } |
2613 | | - |
2614 | | - for (var i = 0; i < fmt.length; ++i) { |
2615 | | - var c = fmt.charAt(i); |
2616 | | - |
2617 | | - if (escape) { |
2618 | | - switch (c) { |
2619 | | - case 'a': c = "" + dayNames[d.getDay()]; break; |
2620 | | - case 'b': c = "" + monthNames[d.getMonth()]; break; |
2621 | | - case 'd': c = leftPad(d.getDate()); break; |
2622 | | - case 'e': c = leftPad(d.getDate(), " "); break; |
2623 | | - case 'H': c = leftPad(hours); break; |
2624 | | - case 'I': c = leftPad(hours12); break; |
2625 | | - case 'l': c = leftPad(hours12, " "); break; |
2626 | | - case 'm': c = leftPad(d.getMonth() + 1); break; |
2627 | | - case 'M': c = leftPad(d.getMinutes()); break; |
2628 | | - case 'S': c = leftPad(d.getSeconds()); break; |
2629 | | - case 'y': c = leftPad(d.getFullYear() % 100); break; |
2630 | | - case 'Y': c = "" + d.getFullYear(); break; |
2631 | | - case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; |
2632 | | - case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; |
2633 | | - case 'w': c = "" + d.getDay(); break; |
2634 | | - } |
2635 | | - r.push(c); |
2636 | | - escape = false; |
2637 | | - } |
2638 | | - else { |
2639 | | - if (c == "%") |
2640 | | - escape = true; |
2641 | | - else |
2642 | | - r.push(c); |
2643 | | - } |
2644 | | - } |
2645 | | - return r.join(""); |
2646 | | - } |
2647 | | - |
2648 | | - // To have a consistent view of time-based data independent of which time |
2649 | | - // zone the client happens to be in we need a date-like object independent |
2650 | | - // of time zones. This is done through a wrapper that only calls the UTC |
2651 | | - // versions of the accessor methods. |
2652 | | - function makeUtcWrapper(d) { |
2653 | | - function addProxyMethod(sourceObj, sourceMethod, targetObj, |
2654 | | - targetMethod) { |
2655 | | - sourceObj[sourceMethod] = function() { |
2656 | | - return targetObj[targetMethod].apply(targetObj, arguments); |
2657 | | - }; |
2658 | | - }; |
2659 | | - var utc = { |
2660 | | - date: d |
2661 | | - }; |
2662 | | - // support strftime, if found |
2663 | | - if (d.strftime != undefined) |
2664 | | - addProxyMethod(utc, "strftime", d, "strftime"); |
2665 | | - addProxyMethod(utc, "getTime", d, "getTime"); |
2666 | | - addProxyMethod(utc, "setTime", d, "setTime"); |
2667 | | - var props = [ "Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds" ]; |
2668 | | - for (var p = 0; p < props.length; p++) { |
2669 | | - addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); |
2670 | | - addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); |
2671 | | - } |
2672 | | - return utc; |
2673 | | - }; |
2674 | | - |
2675 | | - // select time zone strategy. This returns a date-like object tied to the |
2676 | | - // desired timezone |
2677 | | - function dateGenerator(ts, opts) { |
2678 | | - if (opts.timezone == "browser") { |
2679 | | - return new Date(ts); |
2680 | | - } else if (!opts.timezone || opts.timezone == "utc") { |
2681 | | - return makeUtcWrapper(new Date(ts)); |
2682 | | - } else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") { |
2683 | | - var d = new timezoneJS.Date(); |
2684 | | - // timezone-js is fickle, so be sure to set the time zone before |
2685 | | - // setting the time. |
2686 | | - d.setTimezone(opts.timezone); |
2687 | | - d.setTime(ts); |
2688 | | - return d; |
2689 | | - } else { |
2690 | | - return makeUtcWrapper(new Date(ts)); |
2691 | | - } |
2692 | | - } |
2693 | | - |
2694 | | - // map of app. size of time units in milliseconds |
2695 | | - var timeUnitSize = { |
2696 | | - "second": 1000, |
2697 | | - "minute": 60 * 1000, |
2698 | | - "hour": 60 * 60 * 1000, |
2699 | | - "day": 24 * 60 * 60 * 1000, |
2700 | | - "month": 30 * 24 * 60 * 60 * 1000, |
2701 | | - "year": 365.2425 * 24 * 60 * 60 * 1000 |
2702 | | - }; |
2703 | | - |
2704 | | - // the allowed tick sizes, after 1 year we use |
2705 | | - // an integer algorithm |
2706 | | - var spec = [ |
2707 | | - [1, "second"], [2, "second"], [5, "second"], [10, "second"], |
2708 | | - [30, "second"], |
2709 | | - [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], |
2710 | | - [30, "minute"], |
2711 | | - [1, "hour"], [2, "hour"], [4, "hour"], |
2712 | | - [8, "hour"], [12, "hour"], |
2713 | | - [1, "day"], [2, "day"], [3, "day"], |
2714 | | - [0.25, "month"], [0.5, "month"], [1, "month"], |
2715 | | - [2, "month"], [3, "month"], [6, "month"], |
2716 | | - [1, "year"] |
2717 | | - ]; |
2718 | | - |
2719 | | - function init(plot) { |
2720 | | - plot.hooks.processDatapoints.push(function (plot, series, datapoints) { |
2721 | | - $.each(plot.getAxes(), function(axisName, axis) { |
2722 | | - var opts = axis.options; |
2723 | | - if (opts.mode == "time") { |
2724 | | - axis.tickGenerator = function(axis) { |
2725 | | - var ticks = [], |
2726 | | - d = dateGenerator(axis.min, opts), |
2727 | | - minSize = 0; |
2728 | | - |
2729 | | - if (opts.minTickSize != null) { |
2730 | | - if (typeof opts.tickSize == "number") |
2731 | | - minSize = opts.tickSize; |
2732 | | - else |
2733 | | - minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; |
2734 | | - } |
2735 | | - |
2736 | | - for (var i = 0; i < spec.length - 1; ++i) |
2737 | | - if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] |
2738 | | - + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 |
2739 | | - && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) |
2740 | | - break; |
2741 | | - var size = spec[i][0]; |
2742 | | - var unit = spec[i][1]; |
2743 | | - |
2744 | | - // special-case the possibility of several years |
2745 | | - if (unit == "year") { |
2746 | | - // if given a minTickSize in years, just use it, |
2747 | | - // ensuring that it's an integer |
2748 | | - if (opts.minTickSize != null && opts.minTickSize[1] == "year") { |
2749 | | - size = Math.floor(opts.minTickSize[0]); |
2750 | | - } else { |
2751 | | - var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); |
2752 | | - var norm = (axis.delta / timeUnitSize.year) / magn; |
2753 | | - if (norm < 1.5) |
2754 | | - size = 1; |
2755 | | - else if (norm < 3) |
2756 | | - size = 2; |
2757 | | - else if (norm < 7.5) |
2758 | | - size = 5; |
2759 | | - else |
2760 | | - size = 10; |
2761 | | - |
2762 | | - size *= magn; |
2763 | | - } |
2764 | | - |
2765 | | - // minimum size for years is 1 |
2766 | | - if (size < 1) |
2767 | | - size = 1; |
2768 | | - } |
2769 | | - |
2770 | | - axis.tickSize = opts.tickSize || [size, unit]; |
2771 | | - var tickSize = axis.tickSize[0]; |
2772 | | - unit = axis.tickSize[1]; |
2773 | | - |
2774 | | - var step = tickSize * timeUnitSize[unit]; |
2775 | | - |
2776 | | - if (unit == "second") |
2777 | | - d.setSeconds(floorInBase(d.getSeconds(), tickSize)); |
2778 | | - if (unit == "minute") |
2779 | | - d.setMinutes(floorInBase(d.getMinutes(), tickSize)); |
2780 | | - if (unit == "hour") |
2781 | | - d.setHours(floorInBase(d.getHours(), tickSize)); |
2782 | | - if (unit == "month") |
2783 | | - d.setMonth(floorInBase(d.getMonth(), tickSize)); |
2784 | | - if (unit == "year") |
2785 | | - d.setFullYear(floorInBase(d.getFullYear(), tickSize)); |
2786 | | - |
2787 | | - // reset smaller components |
2788 | | - d.setMilliseconds(0); |
2789 | | - if (step >= timeUnitSize.minute) |
2790 | | - d.setSeconds(0); |
2791 | | - if (step >= timeUnitSize.hour) |
2792 | | - d.setMinutes(0); |
2793 | | - if (step >= timeUnitSize.day) |
2794 | | - d.setHours(0); |
2795 | | - if (step >= timeUnitSize.day * 4) |
2796 | | - d.setDate(1); |
2797 | | - if (step >= timeUnitSize.year) |
2798 | | - d.setMonth(0); |
2799 | | - |
2800 | | - |
2801 | | - var carry = 0, v = Number.NaN, prev; |
2802 | | - do { |
2803 | | - prev = v; |
2804 | | - v = d.getTime(); |
2805 | | - ticks.push(v); |
2806 | | - if (unit == "month") { |
2807 | | - if (tickSize < 1) { |
2808 | | - // a bit complicated - we'll divide the month |
2809 | | - // up but we need to take care of fractions |
2810 | | - // so we don't end up in the middle of a day |
2811 | | - d.setDate(1); |
2812 | | - var start = d.getTime(); |
2813 | | - d.setMonth(d.getMonth() + 1); |
2814 | | - var end = d.getTime(); |
2815 | | - d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize); |
2816 | | - carry = d.getHours(); |
2817 | | - d.setHours(0); |
2818 | | - } |
2819 | | - else |
2820 | | - d.setMonth(d.getMonth() + tickSize); |
2821 | | - } |
2822 | | - else if (unit == "year") { |
2823 | | - d.setFullYear(d.getFullYear() + tickSize); |
2824 | | - } |
2825 | | - else |
2826 | | - d.setTime(v + step); |
2827 | | - } while (v < axis.max && v != prev); |
2828 | | - |
2829 | | - return ticks; |
2830 | | - }; |
2831 | | - |
2832 | | - axis.tickFormatter = function (v, axis) { |
2833 | | - var d = dateGenerator(v, axis.options); |
2834 | | - |
2835 | | - // first check global format |
2836 | | - if (opts.timeformat != null) |
2837 | | - return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames); |
2838 | | - |
2839 | | - var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; |
2840 | | - var span = axis.max - axis.min; |
2841 | | - var suffix = (opts.twelveHourClock) ? " %p" : ""; |
2842 | | - var hourCode = (opts.twelveHourClock) ? "%I" : "%H"; |
2843 | | - |
2844 | | - if (t < timeUnitSize.minute) |
2845 | | - fmt = hourCode + ":%M:%S" + suffix; |
2846 | | - else if (t < timeUnitSize.day) { |
2847 | | - if (span < 2 * timeUnitSize.day) |
2848 | | - fmt = hourCode + ":%M" + suffix; |
2849 | | - else |
2850 | | - fmt = "%b %d " + hourCode + ":%M" + suffix; |
2851 | | - } |
2852 | | - else if (t < timeUnitSize.month) |
2853 | | - fmt = "%b %d"; |
2854 | | - else if (t < timeUnitSize.year) { |
2855 | | - if (span < timeUnitSize.year) |
2856 | | - fmt = "%b"; |
2857 | | - else |
2858 | | - fmt = "%b %Y"; |
2859 | | - } |
2860 | | - else |
2861 | | - fmt = "%Y"; |
2862 | | - |
2863 | | - var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames); |
2864 | | - return rt; |
2865 | | - }; |
2866 | | - } |
2867 | | - }); |
2868 | | - }); |
2869 | | - } |
2870 | | - |
2871 | | - $.plot.plugins.push({ |
2872 | | - init: init, |
2873 | | - options: options, |
2874 | | - name: 'time', |
2875 | | - version: '1.0' |
2876 | | - }); |
2877 | | -})(jQuery); |
2878 | | -/* |
2879 | 2571 | Flot plugin for showing crosshairs, thin lines, when the mouse hovers |
2880 | 2572 | over the plot. |
2881 | 2573 |
|
|
0 commit comments