diff --git a/background.js b/background.js new file mode 100644 index 0000000..de48edb --- /dev/null +++ b/background.js @@ -0,0 +1,105 @@ +/* global chrome, browser */ +const API = typeof chrome !== "undefined" ? chrome : browser; + +function canonicalizeAliExpress(urlString) { + const url = new URL(urlString); + const path = url.pathname; + const canonicalHost = "www.aliexpress.com"; + let productId = null; + const itemMatch = path.match(/\/(item|i)\/(\d+)\.html/i); + if (itemMatch) productId = itemMatch[2]; + if (!productId) { + const xObjectId = url.searchParams.get("x_object_id") || url.searchParams.get("item_id"); + if (xObjectId && /^\d+$/.test(xObjectId)) productId = xObjectId; + } + if (productId) return `https://${canonicalHost}/item/${productId}.html`; + return `https://${canonicalHost}${path}`; +} + +function canonicalizeAlibaba(urlString) { + const url = new URL(urlString); + return `${url.origin}${url.pathname}`; +} + +function canonicalize(urlString) { + const url = new URL(urlString); + const hostname = url.hostname.toLowerCase(); + if (hostname.includes("aliexpress.")) return { vendor: "AliExpress", url: canonicalizeAliExpress(urlString) }; + if (hostname.includes("alibaba.")) return { vendor: "Alibaba", url: canonicalizeAlibaba(urlString) }; + return { vendor: "Unknown", url: `${url.origin}${url.pathname}` }; +} + +async function shortenWithIsGd(longUrl) { + const endpoint = `https://is.gd/create.php?format=simple&url=${encodeURIComponent(longUrl)}`; + const res = await fetch(endpoint, { method: "GET" }); + const text = await res.text(); + if (!res.ok) throw new Error(text || `Shorten failed (${res.status})`); + if (!/^https?:\/\//i.test(text)) throw new Error(text || "Shorten failed"); + return text.trim(); +} + +function createMenus() { + try { API.contextMenus.removeAll(() => {}); } catch (e) {} + API.contextMenus.create({ + id: "ali-clean", + title: "Copy clean Ali link", + contexts: ["page", "selection", "link"], + documentUrlPatterns: [ + "*://*.aliexpress.*/*", + "*://*.alibaba.*/*" + ] + }); + API.contextMenus.create({ + id: "ali-shorten", + title: "Copy short Ali link", + contexts: ["page", "selection", "link"], + documentUrlPatterns: [ + "*://*.aliexpress.*/*", + "*://*.alibaba.*/*" + ] + }); +} + +async function getRelevantUrl(info, tab) { + if (info.linkUrl) return info.linkUrl; + return tab && tab.url ? tab.url : null; +} + +async function copyTextToClipboard(tabId, text) { + // Using scripting to execute in page context for broad compatibility + const code = `navigator.clipboard.writeText(${JSON.stringify(text)}).then(() => true).catch(() => false);`; + try { + const results = await API.scripting.executeScript({ + target: { tabId }, + func: (t) => navigator.clipboard.writeText(t).then(() => true).catch(() => false), + args: [text] + }); + const ok = results && results[0] && results[0].result; + return !!ok; + } catch (e) { + return false; + } +} + +API.runtime.onInstalled && API.runtime.onInstalled.addListener(() => { + createMenus(); +}); + +API.contextMenus.onClicked.addListener(async (info, tab) => { + if (!tab || !tab.id) return; + const rawUrl = await getRelevantUrl(info, tab); + if (!rawUrl) return; + const { url } = canonicalize(rawUrl); + if (info.menuItemId === "ali-clean") { + await copyTextToClipboard(tab.id, url); + } else if (info.menuItemId === "ali-shorten") { + try { + const shortUrl = await shortenWithIsGd(url); + await copyTextToClipboard(tab.id, shortUrl); + } catch (e) { + await copyTextToClipboard(tab.id, url); + } + } +}); + + diff --git a/build.sh b/build.sh index b22cc5a..cb672f1 100644 --- a/build.sh +++ b/build.sh @@ -19,7 +19,7 @@ cleanup() { trap cleanup EXIT # Common files -for f in manifest.json popup.html popup.js; do +for f in manifest.json popup.html popup.js background.js; do cp "$ROOT_DIR/$f" "$WORK_CHROME/" cp "$ROOT_DIR/$f" "$WORK_FIREFOX/" done diff --git a/dist/ali-sharelink-chrome.zip b/dist/ali-sharelink-chrome.zip index 07c1803..c5255cd 100644 Binary files a/dist/ali-sharelink-chrome.zip and b/dist/ali-sharelink-chrome.zip differ diff --git a/dist/ali-sharelink-firefox.zip b/dist/ali-sharelink-firefox.zip index 07c1803..c5255cd 100644 Binary files a/dist/ali-sharelink-firefox.zip and b/dist/ali-sharelink-firefox.zip differ diff --git a/manifest.json b/manifest.json index 488c0ed..b2b248b 100644 --- a/manifest.json +++ b/manifest.json @@ -3,6 +3,9 @@ "name": "Ali Sharelink", "version": "0.1.0", "description": "Get a clean, canonical share link for AliExpress/Alibaba products, with optional shortlink.", + "background": { + "service_worker": "background.js" + }, "action": { "default_title": "Ali Sharelink", "default_popup": "popup.html" @@ -10,7 +13,9 @@ "permissions": [ "activeTab", "tabs", - "clipboardWrite" + "clipboardWrite", + "contextMenus", + "scripting" ], "host_permissions": [ "https://is.gd/*"