-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathflagtracker.user.js
More file actions
107 lines (98 loc) · 4.56 KB
/
flagtracker.user.js
File metadata and controls
107 lines (98 loc) · 4.56 KB
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
// ==UserScript==
// @name Stack Exchange Flag Tracker
// @namespace https://so.floern.com/
// @version 1.2.1
// @description Tracks flagged posts on Stack Exchange.
// @author Floern
// @contributor double-beep
// @include /^https?:\/\/(?:[^/.]+\.)*(?:stackexchange\.com|stackoverflow\.com|serverfault\.com|superuser\.com|askubuntu\.com|stackapps\.com|mathoverflow\.net)\/(?:q(?:uestions)?)/
// @exclude *://chat.stackoverflow.com/*
// @exclude *://chat.stackexchange.com/*
// @exclude *://chat.*.stackexchange.com/*
// @exclude *://api.*.stackexchange.com/*
// @exclude *://data.stackexchange.com/*
// @connect so.floern.com
// @grant GM_xmlhttpRequest
// @run-at document-end
// @updateURL https://github.com/SOBotics/Userscripts/raw/master/GenericBot/flagtracker.user.js
// @downloadURL https://github.com/SOBotics/Userscripts/raw/master/GenericBot/flagtracker.user.js
// ==/UserScript==
/* globals StackExchange */
(function() {
if (!StackExchange.options.user.isRegistered) return; // user is not logged in
const key = 'Cm45BSrt51FR3ju';
const myProfileElement = document.querySelector('.my-profile .gravatar-wrapper-24');
const flaggername = myProfileElement ? myProfileElement.title : null;
const sitename = window.location.hostname;
const flagTrackerButtonHtml = '<div class="flex--item">'
+ ' <button class="flag-tracker-link s-btn s-btn__link" title="Register this post to be tracked">Track</button>'
+ '</div>';
function computeContentHash(postContent) {
if (!postContent) return 0;
var hash = 0;
for (var i = 0; i < postContent.length; ++i) {
hash = ((hash << 5) - hash) + postContent.charCodeAt(i);
hash = hash & hash;
}
return hash;
}
function addXHRListener(callback) {
let open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener('load', callback.bind(null, this), false);
open.apply(this, arguments);
};
}
function sendTrackRequest(postId, contentHash, flagTrackerButtonElement) {
if (!flaggername || !postId || !contentHash) return; // one of these doesn't exist for whatever reason; return
GM_xmlhttpRequest({
method: 'POST',
url: 'https://so.floern.com/api/trackpost.php',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: 'key=' + key
+ '&postId=' + postId
+ '&site=' + sitename
+ '&contentHash=' + contentHash
+ '&flagger=' + encodeURIComponent(flaggername),
onload: function (response) {
if (response.status !== 200) {
StackExchange.helpers.showToast('Flag Tracker Error: Status ' + response.status + '. See console for details', { type: 'danger' });
console.error(response.responseText);
return;
}
flagTrackerButtonElement.classList.add('flag-tracked');
flagTrackerButtonElement.innerHTML = 'Track ✓';
},
onerror: function (response) {
StackExchange.helpers.showToast('Flag Tracker Error. See console for details.', { type: 'danger' });
console.error(response.responseText);
}
});
}
function trackFlag(element) {
const postId = element.querySelector('.js-share-link').href.split('/')[4];
const postContent = element.closest('.post-layout--right').querySelector('.s-prose').innerHTML.trim();
const contentHash = computeContentHash(postContent);
sendTrackRequest(postId, contentHash, element.querySelector('.flag-tracker-link'));
}
function handlePosts() {
[...document.querySelectorAll('.post-layout .js-post-menu')].forEach(element => {
if (element.innerText.match('track')) return; // element already exists
element.children[0].insertAdjacentHTML('beforeend', flagTrackerButtonHtml);
element.children[0].querySelector('.flag-tracker-link').addEventListener('click', () => trackFlag(element));
});
}
addXHRListener(xhr => {
if (/ajax-load-realtime/.test(xhr.responseURL)) handlePosts();
});
addXHRListener(function(xhr) {
let matches = /flags\/posts\/(\d+)\/add\//.exec(xhr.responseURL);
if (matches !== null && xhr.status === 200) {
const postId = matches[1];
const postIsQuestion = document.querySelector('.question').getAttribute('data-questionid') == postId;
const element = postIsQuestion ? document.querySelector('.question .js-post-menu') : document.querySelector(`#answer-${postId} .js-post-menu`);
trackFlag(element);
}
});
handlePosts();
})();