-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Userscript (Fixes #564) #565
base: master
Are you sure you want to change the base?
Changes from 6 commits
f1618e0
705a759
60d33e6
d09426f
81bbc2e
b815d69
e95000e
e667ea6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -234,14 +234,68 @@ class BrowserStorage { | |
} | ||
} | ||
|
||
|
||
const STORAGE_KEY = 'webSearchNavigator'; | ||
|
||
class LocalStorage { | ||
constructor(defaultValues) { | ||
this.values = {}; | ||
this.defaultValues = defaultValues; | ||
this.load(); | ||
} | ||
|
||
load() { | ||
const storedData = localStorage.getItem(STORAGE_KEY); | ||
|
||
if (storedData) { | ||
this.values = JSON.parse(storedData); | ||
} else { | ||
this.values = { ...this.defaultValues }; | ||
this.save(); | ||
} | ||
} | ||
|
||
save() { | ||
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.values)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is |
||
} | ||
|
||
get(key) { | ||
const value = this.values[key]; | ||
if (value != null) { | ||
return value; | ||
} | ||
return this.defaultValues[key]; | ||
} | ||
|
||
set(key, value) { | ||
this.values[key] = value; | ||
this.save(); | ||
} | ||
|
||
clear() { | ||
localStorage.removeItem(STORAGE_KEY); | ||
this.values = { ...this.defaultValues }; | ||
} | ||
|
||
getAll() { | ||
// Merge options from storage with defaults. | ||
return { ...this.defaultValues, ...this.values }; | ||
} | ||
} | ||
|
||
const createSyncedOptions = () => { | ||
if (globalThis.IS_USERSCRIPT){return new LocalStorage(DEFAULT_OPTIONS)} | ||
return new BrowserStorage(browser.storage.sync, DEFAULT_OPTIONS); | ||
}; | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
class ExtensionOptions { | ||
constructor() { | ||
this.sync = createSyncedOptions(); | ||
if (globalThis.IS_USERSCRIPT){ | ||
this.local = createSyncedOptions(); | ||
return; | ||
} | ||
this.local = new BrowserStorage(browser.storage.local, { | ||
lastQueryUrl: null, | ||
lastFocusedIndex: 0, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
const OPTIONS_HTML = atob(` | ||
|
||
__OPTIONS_HTML__ | ||
|
||
`.replaceAll('\n', '')); | ||
|
||
const OPTIONS_CSS = atob(` | ||
|
||
__OPTIONS_CSS__ | ||
|
||
`.replaceAll('\n', '')); | ||
|
||
const OPTIONS_JS = atob(` | ||
|
||
__OPTIONS_JS__ | ||
|
||
`.replaceAll('\n', '')); | ||
|
||
const OPTIONS_PAGE_JS = atob(` | ||
|
||
__OPTIONS_PAGE_JS__ | ||
|
||
`.replaceAll('\n', '')); | ||
|
||
const BROWSER_POLYFILL_JS = atob(` | ||
|
||
__BROWSER_POLYFILL_JS__ | ||
|
||
`.replaceAll('\n', '')); | ||
|
||
function showOptions() { | ||
const CONTAINER_ID = "webNavigatorIframe"; | ||
if (document.getElementById(CONTAINER_ID)) { | ||
document.getElementById(CONTAINER_ID).remove(); | ||
} | ||
const iframe = document.createElement("iframe"); | ||
const iframe_container = document.createElement("div"); | ||
|
||
iframe_container.id = CONTAINER_ID; | ||
iframe_container.onclick = () => { | ||
iframe_container?.remove(); | ||
}; | ||
iframe.onclick = (e) => { | ||
e.stopPropagation(); | ||
}; | ||
|
||
const BETTER_STYLES = ` | ||
|
||
body {padding: 30px; max-width: 600px; margin: 0 auto;} | ||
* {box-sizing: border-box; padding: 0; margin: 0; font-family: sans-serif;} | ||
h1, h2, h3 {font-weight: 100;} | ||
|
||
` | ||
const OUT_HTML = OPTIONS_HTML | ||
.replaceAll(`<script src="options.js"></script>`, `<script>\n\n${OPTIONS_JS}\n\n</script>`) | ||
.replaceAll(`<script src="options_page.js"></script>`, `<script>\n\n${OPTIONS_PAGE_JS}\n\n</script>`) | ||
.replaceAll(`<script src="browser-polyfill.js"></script>`, `<script>\n\n${BROWSER_POLYFILL_JS}\n\n</script>`) | ||
.replaceAll(`<link rel="stylesheet" href="options_page.css">`, `<style>\n\n${BETTER_STYLES}\n\n${OPTIONS_CSS}\n\n</style>`); | ||
|
||
console.log({OUT_HTML}); | ||
iframe.srcdoc = OUT_HTML; | ||
Object.assign(iframe_container.style, { | ||
position: "fixed", | ||
display: "grid", | ||
cursor: "pointer", | ||
placeItems: "center", | ||
inset: 0, | ||
backgroundColor: "#0003", | ||
zIndex: 100000, | ||
}); | ||
iframe_container.appendChild(iframe); | ||
Object.assign(iframe.style, { | ||
width: "80vw", | ||
height: "80vh", | ||
border: "none", | ||
borderRadius: "3px", | ||
overflow: "hidden", | ||
background: "#fff", | ||
}); | ||
document.body.appendChild(iframe_container); | ||
return { el: iframe, container: iframe_container }; | ||
} | ||
|
||
// TODO: Make the options page use postMessage to parent and localStorage to utilize settings | ||
|
||
function blobToDataURL(blob) { | ||
return new Promise((resolve, reject) => { | ||
const reader = new FileReader(); | ||
reader.onload = function (e) { | ||
resolve(reader.result); | ||
}; | ||
reader.onerror = function (e) { | ||
reject(reader.error); | ||
}; | ||
reader.onabort = function (e) { | ||
reject(new Error("Read aborted")); | ||
}; | ||
reader.readAsDataURL(blob); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
globalThis.IS_USERSCRIPT = true; | ||
|
||
globalThis._localStorage_browser_polyfill = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
get: async (...args) => { | ||
console.log('[localStorage] Get: ', ...args) | ||
return []; | ||
}, | ||
set: async (...args) => { | ||
console.log('[localStorage] Set: ', ...args) | ||
}, | ||
clear: async () => { | ||
console.log('[localStorage] Clear') | ||
}, | ||
} | ||
|
||
globalThis._browser_userscript_polyfill = { | ||
runtime: { | ||
sendMessage: (msg) => { | ||
if (msg.type === 'tabsCreate'){ | ||
window.open(msg.options.url, '_blank') | ||
} | ||
}, | ||
id: '093889f3-43be-45e3-bc5a-e257e75b466d', | ||
}, | ||
storage: {sync: globalThis._localStorage_browser_polyfill, local: globalThis._localStorage_browser_polyfill}, | ||
permissions: { | ||
remove: () => {}, | ||
add: () => {}, | ||
request: () => {}, | ||
getAll: () => ({}) | ||
}, | ||
} | ||
console.log(globalThis.browser, _browser_userscript_polyfill); | ||
Object.assign(globalThis, {browser: globalThis._browser_userscript_polyfill, chrome: globalThis._browser_userscript_polyfill}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
mkdir -p build/userscript | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a proper shebang line (look at other files and follow their style) |
||
PTH=$(realpath ../src) | ||
|
||
|
||
if ! command -v uglifyjs &> /dev/null | ||
then | ||
echo "uglify-js could not be found, installing" | ||
pnpm i -g uglify-js | ||
fi | ||
|
||
key(){ | ||
cat ../src/manifest.json | jq -r "$1" | ||
} | ||
|
||
ICONPATH="$PTH/$(key '.icons["16"]')" | ||
mimetype=$(file -bN --mime-type $ICONPATH) | ||
content=$(cat $ICONPATH | base64 -w0) | ||
DATAURL="data:$mimetype;base64,$content" | ||
|
||
US=build/userscript/main.user.js | ||
echo "// ==UserScript==" > $US | ||
echo "// @name $(key '.name')" >> $US | ||
echo "// @version $(key '.version')" >> $US | ||
echo "// @description $(key '.description')" >> $US | ||
echo "// @author $(key '.author')" >> $US | ||
echo "// @iconURL $DATAURL" >> $US | ||
key ".content_scripts[0].matches | map(\"// @match \"+.) | .[]" >> $US | ||
echo "// ==/UserScript==" >> $US | ||
|
||
cat $(key ".content_scripts[0].js | map(\"$PTH/\"+.) | .[]") | uglifyjs -c >> $US | ||
|
||
USERSCRIPT_OPTIONS=$(cat "$PTH/userscript-options.js") | ||
|
||
OPTIONS_HTML=$(cat "$PTH/options_page.html" | base64) | ||
OPTIONS_CSS=$(cat "$PTH/options_page.css" | base64) | ||
OPTIONS_PAGE_JS=$(cat "$PTH/options_page.js" | base64) | ||
OPTIONS_JS=$(cat "$PTH/options.js" | base64) | ||
BROWSER_POLYFILL_JS=$(cat "$PTH/userscript-polyfill.js" "$PTH/browser-polyfill.js" | base64) | ||
|
||
FINAL=$(echo "${USERSCRIPT_OPTIONS//__OPTIONS_HTML__/$OPTIONS_HTML}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to use |
||
FINAL=$(echo "${FINAL//__OPTIONS_CSS__/$OPTIONS_CSS}") | ||
FINAL=$(echo "${FINAL//__OPTIONS_JS__/$OPTIONS_JS}") | ||
FINAL=$(echo "${FINAL//__OPTIONS_PAGE_JS__/$OPTIONS_PAGE_JS}") | ||
FINAL=$(echo "${FINAL//__BROWSER_POLYFILL_JS__/$BROWSER_POLYFILL_JS}") | ||
|
||
echo "$FINAL" > "build/userscript/userscript-options.js" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add this empty file?