blob: 78adfd08622cdd4403d78d60a813f1cab11dd62c (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
/*
 * The client side script that cgit serves with every page. Its main job is
 * to refresh the relative ages that cgit_print_age renders in
 * source/ui-shared.c, so the thresholds, suffixes and class names tabulated
 * below mirror the constants there and the two have to move together. One
 * smaller piece follows, the highlight laid over the source line a URL
 * fragment names. Each piece is wrapped in a function of its own so that
 * nothing is left behind in the global scope.
 */

(function () {

// Written the way SECONDS_PER_* are derived in source/cgit.h, so that the
// bucket a browser picks is the bucket cgit_print_age already picked on the
// server. A month is a twelfth of a year rather than thirty days.
var MINUTE = 60;
var HOUR   = 60 * MINUTE;
var DAY    = 24 * HOUR;
var WEEK   = 7 * DAY;
var YEAR   = 365 * DAY;
var MONTH  = YEAR / 12;

// The five arrays are indexed together by the bucket an age falls into.
// The search in render_age can stop one place past the last class, so
// age_suffix and age_unit repeat their final entry. Each limit is twice the
// next unit up, matching the thresholds cgit_print_age compares against.
var age_classes = [ "age-mins", "age-hours", "age-days", "age-weeks", "age-months", "age-years" ];
var age_suffix = [ "min.", "hours", "days", "weeks", "months", "years", "years" ];
var age_unit = [ MINUTE, HOUR, DAY, WEEK, MONTH, YEAR, YEAR ];
var age_limit = [ 2 * HOUR, 2 * DAY, 2 * WEEK, 2 * MONTH, 2 * YEAR, 2 * YEAR ];
var update_delay = [ 10, 5 * MINUTE, 30 * MINUTE, DAY, DAY, DAY ];

function render_age(element, age) {
  var text, bucket;

  for (bucket = 0; bucket < age_classes.length; bucket++)
    if (age < age_limit[bucket])
      break;

  text = Math.round(age / age_unit[bucket]) + " " + age_suffix[bucket];

  // Every age on the page is measured again on each pass, so most of them
  // are already reading correctly and writing to them would cost a repaint
  // for nothing.
  if (element.textContent != text) {
    element.textContent = text;
    // An age past the last limit is still counted in years, but there
    // is no class beyond age-years to put on it.
    if (bucket == age_classes.length)
      bucket--;
    if (element.className != age_classes[bucket])
      element.className = age_classes[bucket];
  }
}

/*
 * Measures every age on the page against the clock and books the next pass.
 * There is no point coming back before the finest unit on the page could
 * change, so a page already counted in hours is left alone for minutes and
 * one counted in years for a day.
 */
function refresh_ages() {
  var bucket, elements, i, age;
  var delay = 24 * 3600;
  var now = Math.round(new Date().getTime() / 1000);

  for (bucket = 0; bucket < age_classes.length; bucket++) {
    elements = document.getElementsByClassName(age_classes[bucket]);

    if (elements.length && update_delay[bucket] < delay)
      delay = update_delay[bucket];

    for (i = 0; i < elements.length; i++) {
      age = now - elements[i].getAttribute("data-ut");

      // A commit dated ahead of the viewer's clock would otherwise show
      // a negative age, and ui-shared.c clamps it the same way.
      if (age < 0)
        age = 0;

      render_age(elements[i], age);
    }
  }

  window.setTimeout(refresh_ages, delay * 1000);
}

document.addEventListener("DOMContentLoaded", refresh_ages, false);

})();

/*
 * Washes a faded highlight over the source line a URL fragment names, either
 * a single line as in #n231 or a range as in #n5-n12. The line anchors live
 * in the number gutter, so the anchor is measured and a bar of the same
 * height is laid across the full width of the code. Without scripting the
 * CSS target rule still tints the line number itself.
 */

(function () {

var bar = null;

function place_bar() {
  var match, first, last, table;
  var table_box, first_box, last_box, top, bottom;

  if (bar) {
    bar.remove();
    bar = null;
  }
  match = /^#n(\d+)(?:-n?(\d+))?$/.exec(location.hash);
  if (!match)
    return;
  first = document.getElementById("n" + match[1]);
  if (!first)
    return;
  last = (match[2] && document.getElementById("n" + match[2])) || first;
  table = first.closest("table");
  if (!table)
    return;
  table_box = table.getBoundingClientRect();
  first_box = first.getBoundingClientRect();
  last_box = last.getBoundingClientRect();
  top = Math.min(first_box.top, last_box.top) - table_box.top;
  bottom = Math.max(first_box.bottom, last_box.bottom) - table_box.top;
  bar = document.createElement("div");
  bar.className = "line-hl";
  bar.style.top = top + "px";
  bar.style.height = (bottom - top) + "px";
  table.appendChild(bar);

  // A browser only scrolls to a fragment that names a real id, which a range
  // never does, so bring the start of one into view here.
  if (match[2])
    first.scrollIntoView({ block: "center" });
}

document.addEventListener("DOMContentLoaded", place_bar, false);
window.addEventListener("hashchange", place_bar, false);

})();