Last active
September 13, 2020 04:22
-
-
Save barneycarroll/70107eaa98378aab0da1d99998782854 to your computer and use it in GitHub Desktop.
A browser plugin script to identify previous / next pagination links in forums and expose them to generic keyboard navigation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const options = { | |
duration: 600, | |
easing: 'cubic-bezier(0, 0.55, 0.45, 1)', | |
fill: 'both', | |
} | |
function feedback(message = 'error'){ | |
const $circle = render(circle) | |
$circle.animate({ | |
transform: ['scale(0%)', 'scale(120%)'], | |
}, options) | |
.onfinish = () => { | |
$circle.animate({ | |
opacity: [1, 0], | |
}, options) | |
} | |
const $symbol = render( | |
message === 'error' ? x : arrow | |
) | |
if(message === 'next') | |
$symbol.firstElementChild.style.transform = 'rotate(.5turns)' | |
$symbol.animate({ | |
transform: ['scale(200%)', 'scale(90%)'], | |
opacity: [0, 1], | |
}, { | |
...options, | |
duration: 200, | |
}) | |
.onfinish = () => { | |
$symbol.animate({ | |
opacity: [1, 0] | |
}, options) | |
} | |
} | |
function render(source){ | |
const $ = document.createElement('div') | |
$.innerHTML = ` | |
<div style=" | |
transform-origin: 50% 50%; | |
position: fixed; | |
height: 100vh; | |
width: 100vw; | |
left: 0; | |
top: 0; | |
"> | |
<object | |
type=image/svg+xml | |
data="data:image/svg+xml;base64,${ btoa(source) }" | |
style="object-fit: contain"> | |
</object> | |
</div> | |
` | |
return document.body.appendChild( | |
$.firstElementChild | |
) | |
} | |
const arrow = `\ | |
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 640 640"> | |
<defs/> | |
<defs> | |
<path id="a" d="M311 0L0 320l311 320V437h329V202H311V0z"/> | |
</defs> | |
<use fill="#492d67" xlink:href="#a"/> | |
</svg> | |
` | |
const circle = `\ | |
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 640 640"> | |
<defs/> | |
<defs> | |
<path id="a" d="M640 320a320 320 0 11-640 0 320 320 0 01640 0z"/> | |
</defs> | |
<use fill="#f22ce6" xlink:href="#a"/> | |
</svg> | |
` | |
const x = `\ | |
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 640 640"> | |
<defs/> | |
<defs> | |
<path id="a" d="M435 320l205 205-115 115-205-205-205 205L0 525l205-205L0 115 115 0l205 205L525 0l115 115-205 205z"/> | |
</defs> | |
<use fill="#492d67" xlink:href="#a"/> | |
</svg> | |
` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"manifest_version": 2, | |
"name": "Traverse", | |
"version": "1.0", | |
"description": "Forum navigation assistance script. Finds common previous / next page link patterns and binds their actions to Ctrl + ⟵ / Ctrl + ⟶ keyboard commands.", | |
"icons": { | |
"64": "icons/traverse.svg" | |
}, | |
"content_scripts": [ | |
{ | |
"matches": ["<all_urls>"], | |
"js": ["DOM.js"], | |
"run_at": "document_end" | |
} | |
], | |
"commands": { | |
"previous": { | |
"suggested_key": { | |
"default": "Ctrl+Left", | |
"mac": "MacCtrl+Left" | |
}, | |
"description": "Navigate to previous page in thread" | |
}, | |
"next": { | |
"suggested_key": { | |
"default": "Ctrl+Right", | |
"mac": "MacCtrl+Right" | |
}, | |
"description": "Navigate to next page in thread" | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Commands interface | |
browser.commands.onCommand.addListener(direction => | |
act(controls[direction]) | |
) | |
// Controls are searched for on request... | |
const controls = new Proxy({}, { | |
get: (_, command) => _[command] || findControls()[command] | |
}) | |
// ...up until the document is ready, at which point they're fixed | |
document.addEventListener('DOMContentLoaded', () => { | |
Object.assign(controls, findControls()) | |
}) | |
// Text patterns used by pagination controls, | |
// in order of most to least likely / ambiguous | |
const patterns = [ | |
['previous', 'next'], | |
['<', '>' ], | |
['prev', 'next'], | |
['‹', '›' ], | |
['«', '»' ], | |
['<<', '>>' ], | |
].map(pair => | |
pair.map(string => | |
new RegExp('\\s*' + string + '\\s*', 'i') | |
) | |
) | |
// Element properties to test those patterns on | |
const properties = ['textContent', 'rel', 'title'] | |
function findControls() { | |
const controls = {} | |
const $all = Array.from(document.all) | |
// The 2 commands, whose index maps to that of the pattern pair (0 , 1) | |
void ['previous', 'next'].forEach((command, index) => | |
// Iterate through the patterns | |
patterns.find(pair => | |
// And for every element on the page | |
$all.find($ => | |
// If any of the relevant element properties match the pattern... | |
properties.some(property => | |
pair[index].test($[property]) | |
) | |
&& | |
// The containing actionable element is the pertinent control | |
(controls[command] = $.closest('a,button,input')) | |
) | |
) | |
) | |
return controls | |
} | |
// Activate pagination controls | |
function act($) { | |
if ($.onclick) | |
$.onclick() | |
if ($.matches('a')) | |
window.location.assign($.href) | |
else if ($.matches('input[type=submit]')) | |
$.form.submit() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment