Files
wehub-resource-sync f4ed7901c3
Module Market Check / Validate modules/ (push) Failing after 1s
Android CI Build / check (push) Failing after 1s
Module Market Publish / Generate submissions.json (push) Failing after 1s
Android CI Build / package-apks (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:29:44 +08:00

190 lines
6.5 KiB
JavaScript

// Selection Actions — floating action bar for text selections.
// Search, copy, open as URL, translate, share.
// Replaces the former Floating Search module with configurable actions.
(function () {
var SEARCH_ENGINES = {
google: 'https://www.google.com/search?q=',
duckduckgo: 'https://duckduckgo.com/?q=',
bing: 'https://www.bing.com/search?q=',
baidu: 'https://www.baidu.com/s?wd=',
wikipedia: 'https://en.wikipedia.org/wiki/Special:Search?search='
};
var TRANSLATE_ENGINES = {
google: 'https://translate.google.com/?sl=auto&tl=auto&text=',
deepl: 'https://www.deepl.com/translator#auto/en/',
mymemory: 'https://mymemory.translated.net/en/TranslationAPI/ajax?langpair=auto|en&q='
};
function getOpts() {
return {
engine: getConfig('engine', 'google'),
translateEngine: getConfig('translateEngine', 'google'),
showSearch: String(getConfig('showSearch', 'true')) === 'true',
showCopy: String(getConfig('showCopy', 'true')) === 'true',
showOpen: String(getConfig('showOpen', 'true')) === 'true',
showTranslate: String(getConfig('showTranslate', 'true')) === 'true',
showShare: String(getConfig('showShare', 'false')) === 'true',
minLength: parseInt(getConfig('minLength', '2'), 10) || 2
};
}
var bar = null;
function buildButtons() {
var opts = getOpts();
var parts = [];
if (opts.showSearch) {
parts.push(makeBtn('search', '\uD83D\uDD0D', 'Search'));
}
if (opts.showCopy) {
parts.push(makeDivider());
parts.push(makeBtn('copy', '\uD83D\uDCCB', 'Copy'));
}
if (opts.showOpen) {
parts.push(makeDivider());
parts.push(makeBtn('open', '\uD83D\uDD17', 'Open'));
}
if (opts.showTranslate) {
parts.push(makeDivider());
parts.push(makeBtn('translate', '\uD835\uDC02', 'Translate'));
}
if (opts.showShare) {
parts.push(makeDivider());
parts.push(makeBtn('share', '\uD83D\uDD17', 'Share'));
}
return parts;
}
function makeBtn(action, icon, label) {
return '<button data-action="' + action + '" type="button">' +
'<span class="wta-sa-icon">' + icon + '</span><span>' + label + '</span></button>';
}
function makeDivider() {
return '<div class="wta-sa-divider"></div>';
}
function ensureBar() {
if (bar) return bar;
bar = document.createElement('div');
bar.id = 'wta-sa-bar';
bar.innerHTML = buildButtons().join('');
bar.addEventListener('mousedown', function (e) { e.preventDefault(); });
bar.addEventListener('click', onBarClick);
document.body.appendChild(bar);
return bar;
}
function show(text, px, py) {
var b = ensureBar();
b.dataset.selection = text;
b.style.left = px + 'px';
b.style.top = py + 'px';
b.classList.add('wta-sa-visible');
}
function hide() {
if (bar) bar.classList.remove('wta-sa-visible');
}
function onBarClick(e) {
var btn = e.target.closest('button[data-action]');
if (!btn || !bar) return;
var text = bar.dataset.selection || '';
if (!text) return;
var opts = getOpts();
var action = btn.dataset.action;
if (action === 'search') {
var base = SEARCH_ENGINES[opts.engine] || SEARCH_ENGINES.google;
navigate(base + encodeURIComponent(text));
} else if (action === 'copy') {
copyToClipboard(text);
} else if (action === 'open') {
if (looksLikeUrl(text)) navigate(normaliseUrl(text));
} else if (action === 'translate') {
var tBase = TRANSLATE_ENGINES[opts.translateEngine] || TRANSLATE_ENGINES.google;
navigate(tBase + encodeURIComponent(text));
} else if (action === 'share') {
if (navigator.share) {
navigator.share({ text: text }).catch(function () {});
} else {
copyToClipboard(text);
}
}
hide();
}
function navigate(url) {
var w = window.open(url, '_blank');
if (!w) location.href = url;
}
function copyToClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).catch(function () { legacyCopy(text); });
} else {
legacyCopy(text);
}
}
function legacyCopy(text) {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;left:-9999px;';
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); } catch (_) {}
document.body.removeChild(ta);
}
function looksLikeUrl(s) {
if (!s) return false;
if (/^https?:\/\//i.test(s)) return true;
return /^[a-z0-9-]+(\.[a-z0-9-]+)+(\/.*)?$/i.test(s);
}
function normaliseUrl(s) {
return /^https?:\/\//i.test(s) ? s : 'https://' + s;
}
var debounceTimer = null;
function onSelectionChange() {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(handleSelection, 120);
}
function handleSelection() {
var sel = window.getSelection && window.getSelection();
if (!sel || sel.isCollapsed) { hide(); return; }
var text = sel.toString().trim();
if (text.length < getOpts().minLength) { hide(); return; }
var rect = sel.getRangeAt(0).getBoundingClientRect();
if (!rect.width && !rect.height) { hide(); return; }
show(text, rect.left + rect.width / 2 + window.scrollX, rect.top + window.scrollY);
}
document.addEventListener('selectionchange', onSelectionChange);
document.addEventListener('scroll', hide, { passive: true });
window.addEventListener('resize', hide);
document.addEventListener('pointerdown', function (e) {
if (bar && !bar.contains(e.target)) hide();
}, { capture: true });
if (typeof __WTA_MODULE_UI__ !== 'undefined' && __WTA_MODULE_UI__.register) {
__WTA_MODULE_UI__.register({
id: __MODULE_INFO__.id,
name: __MODULE_INFO__.name,
icon: __MODULE_INFO__.icon,
uiConfig: __MODULE_UI_CONFIG__,
runMode: __MODULE_RUN_MODE__,
onClick: function () {}
});
}
})();