blob: 2c0a8281058c830aa9aab11fefa2bc36ea72bbcc (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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * 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 returns how many
 * seconds may pass before one of them could read differently. 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 update_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);
    }
  }

  return delay;
}

function refresh_ages() {
  window.setTimeout(refresh_ages, update_ages() * 1000);
}

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

// A site script that adds rows of its own can ask for one pass over them.
window.cgit = window.cgit || {};
window.cgit.updateAges = update_ages;

})();

/*
 * 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);

// A site script that moves the lines about can ask for the bar to be laid
// again.
window.cgit = window.cgit || {};
window.cgit.highlightLines = place_bar;

})();