Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ jobs:
# Clone the repo and checkout the 'current' tag
git clone --depth 1 --branch current https://github.com/lovasoa/ophirofox.git ophirofox
- name: Copy generate-userscript.sh inside ophirofox dir
run: cp ./generate-userscript.sh ./ophirofox/generate-userscript.sh
- name: Copy generate-userscript.sh and adapt-special-case.py inside ophirofox dir
run: |
cp ./generate-userscript.sh ./ophirofox/generate-userscript.sh
cp ./adapt-special-case.py ./ophirofox/adapt-special-case.py
- name: Grant execution rights to generate-userscript.sh
working-directory: ophirofox
Expand Down
158 changes: 158 additions & 0 deletions adapt-special-case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env python3
"""
Transform ophirofox content_script JS → userscript-compatible JS.
Replaces chrome.storage.sync (unavailable in userscripts) with GM.* equivalents
and configurationsSpecifiques() with getOphirofoxConfig() + property check.

Usage: python3 adapt-special-case.py <site_type> < input.js > output.js
site_type: mediapart | arretsurimages | alternativeseconomiques
"""
import sys, re

SITE = sys.argv[1] if len(sys.argv) > 1 else ""

code = sys.stdin.read()

# ── Helper: extract quoted keys from ["k1", 'k2'] strings ────────────────────
def _extract_keys(s):
return re.findall(r"""['"]([^'"]+)['"]""", s)

# ── 1. configurationsSpecifiques([...]) → getOphirofoxConfig() ──────────────
code = re.sub(
r"const config = await configurationsSpecifiques\(\[.*?\]\);?",
"const config = await getOphirofoxConfig();",
code,
)

# ── 2. if (!config) → property check (site-specific) ────────────────────────
PROPERTY_MAP = {
"mediapart": "AUTH_URL_MEDIAPART",
"arretsurimages": "AUTH_URL_ARRETSURIMAGES",
"alternativeseconomiques": "AUTH_URL_ALTERNATIVESECONOMIQUES",
"pressreader": "AUTH_URL_PRESSREADER",
}
prop = PROPERTY_MAP.get(SITE)
if prop:
code = re.sub(r"if\s*\(!config\)\s*return;", f"if (!config.{prop}) return;", code)
code = re.sub(r"if\s*\(!config\)\s*\{", f"if (!config.{prop}) {{", code)

# ── 3. chrome.storage.sync.remove(["k1","k2"]) → GM.deleteValue(...) ────────
def _replace_remove(m):
indent = m.group(1)
has_await = bool(m.group(2))
keys = _extract_keys(m.group(3))
prefix = "await " if has_await else ""
return "\n".join(f"{indent}{prefix}GM.deleteValue(\"{k}\");" for k in keys)

code = re.sub(
r"(\s*)(await\s+)?chrome\.storage\.sync\.remove\(\s*\[(.*?)\]\s*\);?",

_replace_remove,
code,
flags=re.DOTALL,
)

# ── 4. chrome.storage.sync.set({...}) → GM.setValue(...) ────────────────────
def _replace_set(m):
indent = m.group(1)
has_await = bool(m.group(2))
inner = m.group(3)
pairs = re.findall(r"""['"]([^'"]+)['"]\s*:\s*(.+?)\s*(?:,|$)""", inner, re.DOTALL)
prefix = "await " if has_await else ""
lines = []
for k, v in pairs:
v = v.strip().rstrip(",")
lines.append(f"{indent}{prefix}GM.setValue(\"{k}\", {v});")
return "\n".join(lines)

code = re.sub(
r"(\s*)(await\s+)?chrome\.storage\.sync\.set\(\s*\{(.*?)\}\s*\);?",

_replace_set,
code,
flags=re.DOTALL,
)

# ── 5. chrome.storage.sync.get(...).then(...) → GM.getValue(...) ───────────
# Handles: let articlePath;\n await chrome.storage.sync.get(...).then(...)
def _replace_get_then(m):
let_indent = m.group(1) or "" # indent of optional "let x;" line
let_var = m.group(2) or "" # variable name from "let x;"
await_indent = m.group(3) or "" # indent of "await chrome..." line
keys_str = m.group(4)
body = m.group(5)
indent = let_indent or await_indent
keys = _extract_keys(keys_str)
lines = []
for k in keys:
# Find variable assigned from result.KEY
vm = re.search(r"(\w+)\s*=\s*result\." + re.escape(k), body)
if vm:
varname = vm.group(1)
lines.append(f"{indent}const {varname} = await GM.getValue(\"{k}\", null);")
return "\n".join(lines)

code = re.sub(
r"(?:(\s*)let\s+(\w+);\s*\n)?" # optional preceding let declaration
r"(\s*)await chrome\.storage\.sync\.get\(\s*\[(.*?)\]\s*\)"
r"\.then\(\s*\(result\)\s*=>\s*\{(.*?)\}\s*\);?",

_replace_get_then,
code,
flags=re.DOTALL,
)

# ── 6. const { key: var } = await chrome.storage.sync.get([...]) ────────────
def _replace_get_destructure(m):
indent = m.group(1)
destructure = m.group(2)
keys_str = m.group(3) # captured but unused — destructured names ARE the keys
parts = re.findall(r"(\w+)\s*:\s*(\w+)", destructure)
lines = []
for storage_key, var_name in parts:
lines.append(f"{indent}const {var_name} = await GM.getValue(\"{storage_key}\", null);")
return "\n".join(lines)

code = re.sub(
r"(\s*)const\s*\{(.*?)\}\s*=\s*await chrome\.storage\.sync\.get\(\s*\[(.*?)\]\s*\);?",
_replace_get_destructure,
code,
)

# ── 7. Cleanup ───────────────────────────────────────────────────────────────
code = re.sub(r"await\s+await\s+", "await ", code)

# ── 8. Apple-platform cog wheel (⚙) on special-case links ───────────────────
COG_WRAPPER = """\
function ophirofoxWrapWithCog(element) {
if (isApplePlatform()) {
const wrapper = document.createElement("span");
wrapper.style.cssText = "display: inline-flex; align-items: center; gap: 6px;";
wrapper.appendChild(element);
const cog = document.createElement("button");
cog.textContent = "\\\\u2699";
cog.title = "Parametres Ophirofox";
cog.style.cssText = "background: white; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; font-size: 16px; padding: 2px 6px; line-height: 1; z-index: 20;";
cog.addEventListener("click", async (evt) => {
evt.preventDefault();
evt.stopPropagation();
const result = await showSettingsPanel();
universityName = result.universityName;
settingsOpenLinksNewTab = result.openLinksNewTab;
settingsAutoOpenLink = result.autoOpenLink;
await getSettings();
});
wrapper.appendChild(cog);
return wrapper;
}
return element;
}
"""

# Inject the wrapper before the onLoad function
code = re.sub(r"(\s*)(async function onLoad)", r"\1" + COG_WRAPPER + r"\n\1\2", code)

# Wrap return value of createLink functions with the cog
code = re.sub(r"(\s+return) (a|div);", r"\1 ophirofoxWrapWithCog(\2);", code)

sys.stdout.write(code)
14 changes: 11 additions & 3 deletions generate-userscript.sh
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ read -r -d '' CONFIG_FUNCTIONS_BLOCK << 'JSBLOCK'
const cog = document.createElement("button");
cog.textContent = "\u2699";
cog.title = "Parametres Ophirofox";
cog.style.cssText = "background: none; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; font-size: 16px; padding: 2px 6px; line-height: 1;";
cog.style.cssText = "background: white; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; font-size: 16px; padding: 2px 6px; line-height: 1; z-index: 20;";
cog.addEventListener('click', async (evt) => {
evt.preventDefault();
evt.stopPropagation();
Expand Down Expand Up @@ -487,7 +487,7 @@ SCRIPT+='//
function pasteStyle(str) {
var node = document.createElement('"'"'style'"'"');
node.type = '"'"'text/css'"'"';
node.appendChild(document.createTextNode(str.replace(/;/g, '"'"' !important;'"'"')));
node.appendChild(document.createTextNode(str.replace(/(?:\s*!important)?;/g, '"'"' !important;'"'"')));
(document.head ?? document.documentElement).appendChild(node);
}

Expand Down Expand Up @@ -575,7 +575,15 @@ while IFS= read -r line; do
while IFS= read -r matches; do
if [[ $matches != *"config.js" ]]; then
while IFS= read -r js_file; do
if [[ $js_file != *"config.js" ]]; then
if [[ $js_file == *"mediapart.js" ]]; then
js_str=$(python3 adapt-special-case.py mediapart < "$OPHIROFOX_DIR/$js_file")
elif [[ $js_file == *"arret-sur-images.js" ]]; then
js_str=$(python3 adapt-special-case.py arretsurimages < "$OPHIROFOX_DIR/$js_file")
elif [[ $js_file == *"alternatives-economiques.js" ]]; then
js_str=$(python3 adapt-special-case.py alternativeseconomiques < "$OPHIROFOX_DIR/$js_file")
elif [[ $js_file == *"pressreader.js" ]]; then
js_str=$(python3 adapt-special-case.py pressreader < "$OPHIROFOX_DIR/$js_file")
elif [[ $js_file != *"config.js" ]]; then
js_str=$(cat "$OPHIROFOX_DIR/$js_file")
fi
done <<< "$js_files"
Expand Down
Loading