Skip to content

Commit

Permalink
v2.5.0 merge
Browse files Browse the repository at this point in the history
* Only inject into Github issues if you are on the SOX repo
* Fix bugs in various features
* Deprecate the 'hide HNQ' feauter (now implemented natively)
* Improve SOX's performance with many behind-the-scenes changes:
  * Reduce jQuery usage
  * Reduce number of API requests by caching them for a short period
  * Improve SOX's use of MutationObservers by specifying specific
  targets to observe in all uses
* Improve SOX's Github Community Profile
* Remove EOL RawGit dependency
  • Loading branch information
shu8 committed Jun 4, 2019
2 parents 3122edf + 6652482 commit 26488ec
Show file tree
Hide file tree
Showing 10 changed files with 812 additions and 600 deletions.
5 changes: 3 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"no-trailing-spaces":"error",
"prefer-const": "warn",
"one-var": ["warn", "never"],
"no-multiple-empty-lines": ["warn", { "max": 1 }]
"no-multiple-empty-lines": ["warn", { "max": 1 }],
"arrow-parens": ["warn", "as-needed"]
}
}
}
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Join the chat at https://gitter.im/soscripted/sox](https://badges.gitter.im/soscripted/sox.svg)](https://gitter.im/soscripted/sox?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

### SOX v2.4.0
### SOX v2.5.0

Stack Overflow Extras (*SOX*) is a project that stemmed from the [Stack Overflow Optional Features (SOOF)](https://github.com/shu8/Stack-Overflow-Optional-Features) project.

Expand All @@ -10,10 +10,19 @@ Note: This project has no relation to Stack Overflow or Stack Exchange; it is si

## Installation & Requirements

1. Install [Tampermonkey](http://tampermonkey.net/) (for Chrome or Firefox). These are userscript managers that *must* be installed in order for this to work, as the script relies on certain `GM_*` functions in order to save your settings! Tampermonkey is available for many more browsers, and whilst we do not explicitly support them, SOX should work on them. **Note: Greasemonkey 4 and upwards [is not supported with SOX](https://github.com/soscripted/sox/issues/306).**
2. Install the script. Clicking on 'install' below will make your userscript manager prompt you automatically to install it.
1. Install a userscript manager; these are free extensions available for all popular browsers that allow you to manage and install userscripts, along with exposing certain code functions that SOX requires.

- Official Version: <kbd>[install](https://github.com/soscripted/sox/raw/v2.4.0/sox.user.js)</kbd>. <kbd>[view source](https://github.com/soscripted/sox/blob/v2.4.0/sox.user.js)</kbd>
We recommend [Tampermonkey](http://tampermonkey.net/) for Chrome and Firefox.

Whilst SOX only explicitly supports Chrome and Firefox, it should work on any popular browser that can run userscripts.

**Note: Greasemonkey 4 and upwards [is not supported with SOX](https://github.com/soscripted/sox/issues/306).**

**There seems to be [an issue with Tampermonkey on Firefox](https://github.com/Tampermonkey/tampermonkey/issues/477) where userscripts don't seem to run. If this happens, please restart your browser and/or computer before raising an issue on GitHub, as a restart seems to fix this!**

2. Install the script. Clicking on 'install' below will make Tampermonkey prompt you automatically to install it.

- Official Version: <kbd>[install](https://github.com/soscripted/sox/raw/v2.5.0/sox.user.js)</kbd>. <kbd>[view source](https://github.com/soscripted/sox/blob/v2.5.0/sox.user.js)</kbd>
- Development Version: <kbd>[install](https://github.com/soscripted/sox/raw/dev/sox.user.js)</kbd>. <kbd>[view source](https://github.com/soscripted/sox/blob/dev/sox.user.js)</kbd>

3. Go to any site in the Stack Exchange Network (e.g. [Super User](http://superuser.com/) or [Stack Overflow](http://stackoverflow.com/)). You will automatically be asked to choose and save your settings. A toggle button (gears icon) will be added to your topbar where you can change these later on:
Expand Down
26 changes: 13 additions & 13 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!doctype html>
<!DOCTYPE HTML>
<html>

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>sox by soscripted</title>
<title>SOX by soscripted</title>

<link rel="stylesheet" href="/sox/assets/css/style.css?v=a419cba670696de26f5508f589ecdcb9bc1a6377">
<meta name="viewport" content="width=device-width">
Expand All @@ -16,7 +16,7 @@
<body>
<div class="wrapper">
<header>
<h1>sox</h1>
<h1>SOX</h1>
<p>A userscript for the Stack Exchange websites to add a bunch of optional user-selectable features</p>
<p class="view"><a href="https://github.com/soscripted/sox">View the Project on GitHub <small></small></a></p>
</header>
Expand Down Expand Up @@ -46,43 +46,43 @@ <h3><a id="acccess-token" class="anchor" href="#access-token" aria-hidden="true"

<p>The SOX userscript adds a bunch of <strong>optional</strong> features to all sites in the Stack Exchange network. These can be toggled on or off from an easy to use control panel (see screenshot below).</p>

<p>Note: This project has no relation to Stack Overflow or Stack Exchange; it is simply a userscript that enhances the sites!</p>
<p>Note: This project is <strong>not</strong> related to Stack Overflow or Stack Exchange; it is simply a userscript that enhances the sites!</p>

<p>##Installation &amp; Requirements</p>
<h2>Installation &amp; Requirements</h2>

<ol>
<li>Install <a href="http://www.greasespot.net/">Greasemonkey</a> (for Firefox), <a href="http://tampermonkey.net/">Tampermonkey</a> (for Chrome), or <a href="https://github.com/os0x/NinjaKit">NinjaKit</a> for Safari. These are userscript
managers that <em>must</em> be installed in order for this to work, as the script relies on certain <code class="highlighter-rouge">GM_*</code> functions in order to save your settings!</li>
<li>
<p>Install the script. Clicking on install below will make your userscript manager prompt you automatically to install it.</p>
<p>Install the script. Clicking on the 'install' button below will make your userscript manager prompt you automatically to install it.</p>

<ul>
<li>Official Version: <kbd>[install](https://github.com/soscripted/sox/raw/v2.0.1/sox.user.js)</kbd>. <kbd>[view source](https://github.com/soscripted/sox/blob/v2.0.1/sox.user.js)</kbd></li>
<li>Development Version: <kbd>[install](https://github.com/soscripted/sox/raw/dev/sox.user.js)</kbd>. <kbd>[view source](https://github.com/soscripted/sox/blob/dev/sox.user.js)</kbd></li>
<li>Official Version: <kbd><a href="https://github.com/soscripted/sox/raw/master/sox.user.js">install</a></kbd>. <kbd><a href="https://github.com/soscripted/sox/blob/master/sox.user.js">view source</a></kbd></li>
<li>Development Version: <kbd><a href="https://github.com/soscripted/sox/raw/dev/sox.user.js">install</a></kbd>. <kbd><a href="https://github.com/soscripted/sox/blob/dev/sox.user.js">view source</a></kbd></li>
</ul>
</li>
<li>Go to any site in the Stack Exchange Network (eg. <a href="http://superuser.com/">Super User</a> or <a href="http://stackoverflow.com/">Stack Overflow</a>). You will automatically be asked to choose and save your settings. A toggle button
<li>Go to any site in the Stack Exchange Network (e.g. <a href="http://superuser.com/">Super User</a> or <a href="http://stackoverflow.com/">Stack Overflow</a>). You will automatically be asked to choose and save your settings. A toggle button
(gears icon) will be added to your topbar where you can change these later on:</li>
</ol>

<p><img src="https://cloud.githubusercontent.com/assets/12533449/14296194/c732b1b2-fb2d-11e5-9563-1e34b12eada9.png" alt="newdialog" /></p>

<p>##What features are included?</p>
<h2>What features are included?</h2>

<p>A full list of all the features is available on the SOX wiki page <a href="https://github.com/soscripted/sox/wiki/Features">here</a>.</p>

<p>##Bugs and Feature Requests</p>
<h2>Bugs and Feature Requests</h2>

<p>Please post bugs and feature requests as issues on <a href="https://github.com/soscripted/sox">Github</a>, where we can track them easily and push updates quickly. Please <strong>do not</strong> post them as answers on Stack Apps – they are
much harder to manage!</p>

<p>##Changes</p>
<h2>Changes</h2>

<p>Please see the change log <a href="http://stackapps.com/a/6358">at Stack Apps</a>.</p>
</section>
<footer>
<p>This project is maintained by <a href="https://github.com/soscripted">soscripted</a></p>
<p><small>Hosted on GitHub Pages &mdash; Theme by <a href="https://github.com/orderedlist">orderedlist</a></small></p>
<p><small>Hosted on GitHub Pages &mdash; theme by <a href="https://github.com/orderedlist">orderedlist</a></small></p>
</footer>
</div>
<script src="/sox/assets/js/scale.fix.js"></script>
Expand Down
156 changes: 130 additions & 26 deletions sox.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,39 @@
sox.debug = function() {
if (!sox.info.debugging) return;
for (let arg = 0; arg < arguments.length; ++arg) {
console.debug('SOX: ', arguments[arg]);
console.debug('SOX:', arguments[arg]);
}
};

sox.log = function() {
for (let arg = 0; arg < arguments.length; ++arg) {
console.log('SOX: ', arguments[arg]);
console.log('SOX:', arguments[arg]);
}
};

sox.warn = function() {
for (let arg = 0; arg < arguments.length; ++arg) {
console.warn('SOX: ', arguments[arg]);
console.warn('SOX:', arguments[arg]);
}
};

sox.error = function() {
for (let arg = 0; arg < arguments.length; ++arg) {
console.error('SOX: ', arguments[arg]);
console.error('SOX:', arguments[arg]);
}
};

sox.loginfo = function() {
for (let arg = 0; arg < arguments.length; ++arg) {
console.info('SOX: ', arguments[arg]);
console.info('SOX:', arguments[arg]);
}
};

let Chat;
let Stack;
if (location.href.indexOf('github.com') === -1) { //need this so it works on FF -- CSP blocks window.eval() it seems
Chat = (typeof window.CHAT === 'undefined' ? window.eval('typeof CHAT != \'undefined\' ? CHAT : undefined') : CHAT);
sox.debug('CHAT', Chat);
Stack = (typeof Chat === 'undefined' ? (typeof StackExchange === 'undefined' ? window.eval('if (typeof StackExchange != "undefined") StackExchange') : (StackExchange || window.StackExchange)) : undefined);
sox.debug('Stack', Stack);
}

sox.Stack = Stack;
Expand Down Expand Up @@ -104,7 +102,6 @@
}
},
get accessToken() {
sox.debug('SOX Access Token: ' + (GM_getValue('SOX-accessToken', false) === false ? 'NOT SET' : 'SET'));
const accessToken = GM_getValue('SOX-accessToken', false);
return (accessToken == -2 ? false : accessToken); //if the user was already asked once, the value is set to -2, so make sure this is returned as false
},
Expand Down Expand Up @@ -138,16 +135,107 @@
}

sox.helpers = {
getFromAPI: function(type, id, sitename, filter, callback, sortby) {
sox.debug('Getting From API with URL: https://api.stackexchange.com/2.2/' + type + '/' + id + '?order=desc&sort=' + (sortby || 'creation') + '&site=' + sitename + '&key=' + sox.info.apikey + '&access_token=' + sox.settings.accessToken);
getFromAPI: function (details, callback) {
let {
ids,
useCache = true,
} = details;

const {
endpoint,
childEndpoint,
sort = 'creation',
order = 'desc',
sitename,
filter,
limit,
featureId,
cacheDuration = 3, // Minutes to cache data for
} = details;
const baseURL = 'https://api.stackexchange.com/2.2/';
const queryParams = [];

// Cache can only be used if the featureId and IDs (as an array) have been provided
useCache = featureId && useCache && Array.isArray(ids);
const apiCache = JSON.parse(GM_getValue('SOX-apiCache', '{}'));

if (!(featureId in apiCache)) apiCache[featureId] = {};
const featureCache = apiCache[featureId];

if (!(endpoint in featureCache)) featureCache[endpoint] = [];
const endpointCache = featureCache[endpoint];

const endpointToIdFieldNames = {
'questions': 'question_id',
'answers': 'answer_id',
'users': 'user_id',
'comments': 'comment_id',
};

if (filter) queryParams.push(`filter=${filter}`);
if (order) queryParams.push(`order=${order}`);
if (limit) queryParams.push(`pagesize=${limit}`);
queryParams.push(`sort=${sort}`);
queryParams.push(`site=${sitename}`);
queryParams.push(`key=${sox.info.apikey}`);
queryParams.push(`access_token=${sox.settings.accessToken}`);
const queryString = queryParams.join('&');

let finalItems = [];
if (useCache) {
// Count backwards so splicing doesn't change indices
for (let i = ids.length; i >= 0; i--) {
const cachedItemIndex = endpointCache.findIndex(item => {
const idFieldName = endpointToIdFieldNames[endpoint];
return item[idFieldName] === +ids[i];
});

// Cache results for max. cacheDuraction minutes (convert to milliseconds)
const earliestRequestTime = new Date().getTime() - (60 * cacheDuration * 1000);
if (cachedItemIndex !== -1) {
const cachedItem = endpointCache[cachedItemIndex];
if (cachedItem.sox_request_time >= earliestRequestTime) {
// If we have a cached item for this ID, delete it from `ids` so we don't request the API for it
sox.debug(`API: [${featureId}:/${endpoint}/${ids[i]}] Using cached API item`);
finalItems.push(cachedItem);
ids.splice(i, 1);
} else {
// The cached item is now stale (too old); delete it
sox.debug(`API: [${featureId}:/${endpoint}/${ids[i]}] Deleting stale cached item`);
endpointCache.splice(cachedItemIndex, 1);
}
}
}
}

// IDs are optional for endpoints like /questions
if (ids && Array.isArray(ids)) {
if (ids.length) {
ids = ids.join(';');
} else if (useCache) {
// The cache had details for all IDs; no need to request API at all
sox.debug(`API: [${featureId}:/${endpoint}] API Cache had details for all requested IDs, skipping API request`);
GM_setValue('SOX-apiCache', JSON.stringify(apiCache));
sox.debug('API: Saving new cache', apiCache);
callback(finalItems);
return;
}
}

const filterQuery = filter ? '&filter=' + filter : '';
// optional for queries like /questions
const idPath = id ? '/' + id : '';
const idPath = ids ? `/${ids}` : '';
let queryURL;
if (childEndpoint) {
// e.g. /posts/{ids}/revisions
queryURL = `${baseURL}${endpoint}${idPath}/${childEndpoint}?${queryString}`;
} else {
// e.g. /questions/{ids}
queryURL = `${baseURL}${endpoint}${idPath}?${queryString}`;
}
sox.debug(`API: Sending request to URL: '${queryURL}'`);

$.ajax({
type: 'get',
url: 'https://api.stackexchange.com/2.2/' + type + idPath + '?order=desc&sort=' + (sortby || 'creation') + '&site=' + sitename + '&key=' + sox.info.apikey + '&access_token=' + sox.settings.accessToken + filterQuery,
url: queryURL,
success: function(d) {
if (d.backoff) {
sox.error('SOX Error: BACKOFF: ' + d.backoff);
Expand All @@ -158,17 +246,30 @@
window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
alert('Your access token is no longer valid. A window has been opened to request a new one.');
} else {
callback(d);
if (useCache) {
d.items.forEach(item => {
item.sox_request_time = new Date().getTime();
finalItems.push(item);
endpointCache.push(item);
});
GM_setValue('SOX-apiCache', JSON.stringify(apiCache));
sox.debug('API: saving new cache', apiCache);
} else {
finalItems = d.items;
}
callback(finalItems);
}
},
error: function(a, b, c) {
sox.error('SOX Error: ' + b + ' ' + c);
},
});
},
observe: function(elements, callback, toObserve) {
sox.debug('observe: ' + elements);
const observer = new MutationObserver(throttle((mutations) => {
observe: function (targets, elements, callback) {
sox.debug(`OBSERVE: '${elements}' on target(s)`, targets);
if (!targets || (Array.isArray(targets) && !targets.length)) return;

const observer = new MutationObserver(throttle(mutations => {
for (let i = 0; i < mutations.length; i++) {
const mutation = mutations[i];
const target = mutation.target;
Expand All @@ -177,8 +278,8 @@
if (addedNodes) {
for (let n = 0; n < addedNodes.length; n++) {
if ($(addedNodes[n]).find(elements).length) {
callback(target);
sox.debug('fire: node: ', addedNodes[n]);
callback(target);
return;
}
}
Expand All @@ -190,19 +291,22 @@
return;
}
}
}, 250));
}, 1500));

if (Array.isArray(targets)) {
for (let i = 0; i < targets.length; i++) {
const target = targets[i];
if (!target) continue;

if (toObserve) {
for (let i = 0; i < toObserve.length; i++) { //could be multiple elements with querySelectorAll
observer.observe(toObserve[i], {
observer.observe(target, {
attributes: true,
childList: true,
characterData: true,
subtree: true,
});
}
} else {
observer.observe(document.body, {
observer.observe(targets, {
attributes: true,
childList: true,
characterData: true,
Expand Down Expand Up @@ -247,7 +351,7 @@
// answer ID, question ID, user ID, comment ID ("posts/comments/ID" NOT "comment1545_5566")
getIDFromLink: function(link) {
// test cases: https://regex101.com/r/6P9sDX/2
const idMatch = link.match(/\/(\d+)($|\/|\?)/);
const idMatch = link.match(/\/(\d+)/);
return idMatch ? +idMatch[1] : null;
},
getSiteNameFromLink: function(link) {
Expand Down Expand Up @@ -426,4 +530,4 @@
},
};

})(window.sox = window.sox || {}, jQuery);
})(window.sox = window.sox || {}, jQuery);
Loading

0 comments on commit 26488ec

Please sign in to comment.