-
Notifications
You must be signed in to change notification settings - Fork 25
/
content-script.js
151 lines (142 loc) · 5.16 KB
/
content-script.js
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
/* global compareVersions */
(function () {
function throws(f) {
try {
f();
} catch (ex) {
return true;
}
return false;
}
function addScript(code) {
const newScript = document.createElement('script');
newScript.innerHTML = code;
document.head.appendChild(newScript);
}
function request(method, url, cb) {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
cb({
text: xhr.responseText,
json: () => JSON.parse(xhr.responseText),
status: xhr.status,
xhr
});
}
};
xhr.send();
}
function insertVersionTag(parent, after, version, title, muted) {
const versionElement = document.createElement('span');
versionElement.innerText = version;
if (title) {
versionElement.setAttribute('title', title);
}
if (muted) {
versionElement.setAttribute('style', 'font-weight:normal;color:#8a5934;background-color:rgba(251,202,4,0.2);');
}
versionElement.classList.add('Counter');
versionElement.classList.add('GitHub-Version-Tags--Tag');
parent.insertBefore(document.createTextNode(' '), parent.insertBefore(versionElement, after.nextSibling));
}
let requested = false;
let latest = null;
let prerelease = null;
let title = null;
function getVersionTags(cb) {
// Only look on pages that include both an author and repo name in the URL
if (window.location.pathname.split('/').length < 3) {
return cb();
}
const authorElements = document.querySelectorAll('[itemprop="author"]');
const nameElements = document.querySelectorAll('[itemprop="name"]');
const author = authorElements.length > 0 ? authorElements[0].innerText : '';
const name = nameElements.length > 0 ? nameElements[0].innerText : '';
if (!author || !name) {
console.debug('No GitHub author/name itemprop elements found.');
return cb();
}
// Request tags from the GitHub API
request('GET', 'https://api.github.com/repos/' + author + '/' + name + '/tags', r => {
let data;
if (r.status === 404) {
// Fall back to tag list for private repos
// Note: The API took precedence for robustness (to avoid scraping and since this tag list dropdown isn't on every page)
const tagList = document.querySelectorAll('.select-menu-list[data-tab-filter="tags"]')[0];
if (!tagList) {
return cb();
}
data = [...tagList.querySelectorAll('.select-menu-item')].map(x => x.getAttribute('data-name'));
} else {
data = r.json().map(x => x.name);
}
requested = true;
const versions = data
.map(x => x[0] === 'v' ? x.substr(1) : x)
.filter(x => !throws(() => compareVersions('0', x)))
.sort((a, b) => compareVersions(b, a));
if (versions.length === 0) {
return cb();
}
latest = versions.filter(x => !x.includes('-'))[0];
prerelease = versions.filter(x => x.includes('-'))[0];
if (latest && prerelease && compareVersions(prerelease, latest) === -1) {
prerelease = null;
}
title = [
...(latest ? ['Latest version: ' + latest] : []),
...(prerelease ? ['Pre-release: ' + prerelease] : []),
...(versions.length > 0 ? ['All tagged versions (' + versions.length + '): ' + versions.join(', ')] : []),
].join('\n\n');
return cb();
});
}
function addVersionTags() {
// Look for tags on each navigation until they can be found
if (!requested) {
return getVersionTags(() => {
// Try again if found
if (requested) {
addVersionTags();
}
});
}
// Find repo name and insert version tags after
const repoElement = document.querySelectorAll('.repohead .repohead-details-container h1')[0];
if (repoElement.querySelectorAll('.GitHub-Version-Tags--Tag').length > 0) {
return;
}
const nameElement = repoElement.querySelectorAll('[itemprop="name"]')[0];
if (!latest && !prerelease) {
insertVersionTag(repoElement, nameElement, 'no versions tagged');
}
// Note: These are inserted right after the repo name, so insert in reverse order
if (prerelease) {
insertVersionTag(repoElement, nameElement, prerelease, title, true);
}
if (latest) {
insertVersionTag(repoElement, nameElement, latest, title);
}
}
// Add 'xhrRequested' and 'xhrResponded' events
addScript(`
(function () {
function sendEvent(name, arg) { const e = new Event(name); e.arguments = arg; window.dispatchEvent(e); }
const proxied = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.send = function () {
sendEvent('xhrRequested', this);
const intervalId = window.setInterval(() => {
if (this.readyState === 4) {
sendEvent('xhrResponded', this);
clearInterval(intervalId);
}
}, 100);
return proxied.apply(this, [].slice.call(arguments));
};
})();`);
// Request tags and update on soft transition
addVersionTags();
window.addEventListener('xhrResponded', () => addVersionTags(), false);
})();