Skip to content
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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added src/browser_polyfill.js
Copy link
Owner

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?

Empty file.
1 change: 1 addition & 0 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"content_scripts": [
{
"js": [
"userscript-polyfill.js",
"browser-polyfill.js",
"mousetrap.js",
"mousetrap-global-bind.js",
Expand Down
54 changes: 54 additions & 0 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is localStorage defined? Is it from the userscript manager?

}

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,
Expand Down
4 changes: 4 additions & 0 deletions src/options_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ const OPTIONAL_PERMISSIONS_URLS = {
'custom-gitlab': ['https://*/*'],
};

globalThis._browser_userscript_polyfill.permissions.getAll = () => ({
origins: Object.values(OPTIONAL_PERMISSIONS_URLS).flat(),
})

const KEYBINDING_TO_DIV = {
nextKey: 'next-key',
previousKey: 'previous-key',
Expand Down
100 changes: 100 additions & 0 deletions src/userscript-options.js
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);
});
}
34 changes: 34 additions & 0 deletions src/userscript-polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
globalThis.IS_USERSCRIPT = true;

globalThis._localStorage_browser_polyfill = {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_browser_userscript_polyfill and _localStorage_browser_polyfill have inconsistent names (camel case mixed in, different order).

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});
46 changes: 46 additions & 0 deletions tools/make-userscript.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
mkdir -p build/userscript
Copy link
Owner

Choose a reason for hiding this comment

The 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}")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use echo here and below

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"