summaryrefslogtreecommitdiffstats
path: root/extensions/BugModal/web/time_ago.js
blob: 59f12692a5825d2b366241b4a93796a5dca81618 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * this is a port of perl's Time::Duration module, which has the following
 * license:
 *
 * Copyright 2006, Sean M. Burke C<sburke@cpan.org>, all rights reserved.  This
 * program is free software; you can redistribute it and/or modify it under the
 * same terms as Perl itself.
 *
 * This program is distributed in the hope that it will be useful, but without
 * any warranty; without even the implied warranty of merchantability or
 * fitness for a particular purpose.
 */

$(function() {
    'use strict';

    function separate(seconds) {
        // breakdown of seconds into units, starting with the most significant

        var remainder = seconds;
        var tmp;
        var wheel = [];

        // years
        tmp = Math.floor(remainder / (365 * 24 * 60 * 60));
        wheel.push([ 'year', tmp, 1000000000 ]);
        remainder -= tmp * (365 * 24 * 60 * 60);

        // days
        tmp = Math.floor(remainder / (24 * 60 * 60));
        wheel.push([ 'day', tmp, 365 ]);
        remainder -= tmp * (24 * 60 * 60);

        // hours
        tmp = Math.floor(remainder / (60 * 60));
        wheel.push([ 'hour', tmp, 24 ]);
        remainder -= tmp * (60 * 60);

        // minutes
        tmp = Math.floor(remainder / 60);
        wheel.push([ 'minute', tmp, 60 ]);
        remainder -= tmp * 60;

        // seconds
        wheel.push([ 'second', Math.floor(remainder), 60 ]);
        return wheel;
    }

    function approximate(precision, wheel) {
        // now nudge the wheels into an acceptably (im)precise configuration
        FIX: do {
            // constraints for leaving this block:
            //  1) number of nonzero wheels must be <= precision
            //  2) no wheels can be improperly expressed (like having "60" for mins)

            var nonzero_count = 0;
            var improperly_expressed = -1;

            for (var i = 0; i < wheel.length; i++) {
                var tmp = wheel[i];
                if (tmp[1] == 0) {
                    continue;
                }
                ++nonzero_count;
                if (i == 0) {
                    // the years wheel is never improper or over any limit; skip
                    continue;
                }

                if (nonzero_count > precision) {
                    // this is one nonzero wheel too many

                    // incr previous wheel if we're big enough
                    if (tmp[1] >= (tmp[tmp.length - 1] / 2)) {
                        ++wheel[i - 1][1];
                    }

                    // reset this and subsequent wheels to 0
                    for (var j = i; j < wheel.length; j++) {
                        wheel[j][1] = 0;
                    }

                    // start over
                    continue FIX;

                } else if (tmp[1] >= tmp[tmp.length - 1]) {
                    // it's an improperly expressed wheel (like "60" on the mins wheel)
                    improperly_expressed = i;
                }
            }

            if (improperly_expressed != -1) {
                // only fix the least-significant improperly expressed wheel (at a time)
                ++wheel[improperly_expressed - 1][1];
                wheel[improperly_expressed][1] = 0;

                // start over
                continue FIX;
            }

            // otherwise there's not too many nonzero wheels, and there's no
            //improperly expressed wheels, so fall thru...
        } while(0);

        return wheel;
    }

    function render(wheel) {
        var parts = [];
        wheel.forEach(function(element, index) {
            if (element[1] > 0) {
                parts.push(element[1] + ' ' + element[0]);
                if (element[1] != 1) {
                    parts[parts.length - 1] += 's';
                }
            }
        });

        if (parts.length == 0) {
            return "just now";
        }
        parts[parts.length - 1] += ' ago';
        if (parts.length == 1) {
            return parts[0];
        }
        if (parts.length == 2) {
            return parts[0] + ' and ' + parts[1];
        }
        parts[parts.length - 1] = 'and ' + parts[parts.length - 1];
        return parts.join(', ');
    }

    window.setInterval(function() {
        var now = Math.floor(new Date().getTime() / 1000);
        $('.rel-time').each(function() {
            $(this).text(render(approximate(2, separate(now - $(this).data('time')))));
        });
        $('.rel-time-title').each(function() {
            $(this).attr('title', render(approximate(2, separate(now - $(this).data('time')))));
        });
    }, 60000);
});