2021-07-30 12:44:06 +00:00
|
|
|
/* global $ $$ $create $remove showSpinner */// dom.js
|
2021-01-01 14:27:58 +00:00
|
|
|
/* global $entry tabURL */// popup.js
|
|
|
|
/* global API */// msg.js
|
|
|
|
/* global Events */
|
|
|
|
/* global FIREFOX URLS debounce download tryCatch */// toolbox.js
|
|
|
|
/* global prefs */
|
|
|
|
/* global t */// localization.js
|
2017-12-09 21:03:17 +00:00
|
|
|
'use strict';
|
|
|
|
|
2021-01-01 14:27:58 +00:00
|
|
|
(() => {
|
|
|
|
require(['/popup/search.css']);
|
|
|
|
|
2017-12-11 02:20:59 +00:00
|
|
|
const RESULT_ID_PREFIX = 'search-result-';
|
2020-10-04 05:30:02 +00:00
|
|
|
const INDEX_URL = URLS.usoArchiveRaw + 'search-index.json';
|
2021-04-20 17:40:04 +00:00
|
|
|
const USW_INDEX_URL = URLS.usw + 'api/index/uso-format';
|
|
|
|
const USW_ICON = $create('img', {
|
|
|
|
src: `${URLS.usw}favicon.ico`,
|
|
|
|
title: URLS.usw,
|
|
|
|
});
|
2020-01-28 02:36:43 +00:00
|
|
|
const STYLUS_CATEGORY = 'chrome-extension';
|
2020-10-04 05:30:02 +00:00
|
|
|
const PAGE_LENGTH = 10;
|
|
|
|
// update USO style install counter if the style isn't uninstalled immediately
|
|
|
|
const PINGBACK_DELAY = 5e3;
|
|
|
|
const BUSY_DELAY = .5e3;
|
|
|
|
const USO_AUTO_PIC_SUFFIX = '-after.png';
|
|
|
|
const BLANK_PIXEL = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
|
|
|
const dom = {};
|
|
|
|
/**
|
|
|
|
* @typedef IndexEntry
|
|
|
|
* @prop {'uso' | 'uso-android'} f - format
|
|
|
|
* @prop {Number} i - id
|
|
|
|
* @prop {string} n - name
|
|
|
|
* @prop {string} c - category
|
|
|
|
* @prop {Number} u - updatedTime
|
|
|
|
* @prop {Number} t - totalInstalls
|
|
|
|
* @prop {Number} w - weeklyInstalls
|
|
|
|
* @prop {Number} r - rating
|
|
|
|
* @prop {Number} ai - authorId
|
|
|
|
* @prop {string} an - authorName
|
|
|
|
* @prop {string} sn - screenshotName
|
|
|
|
* @prop {boolean} sa - screenshotArchived
|
|
|
|
*/
|
|
|
|
/** @type IndexEntry[] */
|
|
|
|
let results;
|
|
|
|
/** @type IndexEntry[] */
|
|
|
|
let index;
|
|
|
|
let category = '';
|
2020-10-10 11:25:43 +00:00
|
|
|
let searchGlobals = $('#search-globals').checked;
|
2020-10-04 05:30:02 +00:00
|
|
|
/** @type string[] */
|
|
|
|
let query = [];
|
2021-04-20 17:40:04 +00:00
|
|
|
let order = prefs.get('popup.findSort');
|
2017-12-11 02:20:59 +00:00
|
|
|
let scrollToFirstResult = true;
|
|
|
|
let displayedPage = 1;
|
|
|
|
let totalPages = 1;
|
2020-10-04 05:30:02 +00:00
|
|
|
let ready;
|
2017-12-11 02:20:59 +00:00
|
|
|
|
2021-08-12 13:02:48 +00:00
|
|
|
let imgType = '.jpg';
|
|
|
|
// detect WebP support
|
|
|
|
$create('img', {
|
|
|
|
src: 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=',
|
|
|
|
onload: () => (imgType = '.webp'),
|
|
|
|
});
|
|
|
|
|
2020-10-10 11:25:43 +00:00
|
|
|
const $class = sel => (sel instanceof Node ? sel : $(sel)).classList;
|
|
|
|
const show = sel => $class(sel).remove('hidden');
|
|
|
|
const hide = sel => $class(sel).add('hidden');
|
2017-12-11 02:20:59 +00:00
|
|
|
|
2021-01-01 14:27:58 +00:00
|
|
|
Object.assign(Events, {
|
|
|
|
/**
|
|
|
|
* @param {HTMLAnchorElement} a
|
|
|
|
* @param {Event} event
|
|
|
|
*/
|
|
|
|
searchOnClick(a, event) {
|
2017-12-11 02:20:59 +00:00
|
|
|
if (!prefs.get('popup.findStylesInline') || dom.container) {
|
2020-11-05 19:45:22 +00:00
|
|
|
// use a less specific category if the inline search wasn't used yet
|
|
|
|
if (!category) calcCategory({retry: 1});
|
2021-01-01 14:27:58 +00:00
|
|
|
a.search = new URLSearchParams({category, search: $('#search-query').value});
|
|
|
|
Events.openURLandHide.call(a, event);
|
2017-12-10 01:03:04 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-01-01 14:27:58 +00:00
|
|
|
a.textContent = a.title;
|
|
|
|
a.title = '';
|
2017-12-11 02:20:59 +00:00
|
|
|
init();
|
2020-11-05 19:45:22 +00:00
|
|
|
calcCategory();
|
2020-10-04 05:30:02 +00:00
|
|
|
ready = start();
|
2017-12-10 01:03:04 +00:00
|
|
|
},
|
|
|
|
});
|
2017-12-09 21:03:17 +00:00
|
|
|
|
2017-12-11 02:20:59 +00:00
|
|
|
function init() {
|
2020-10-04 05:30:02 +00:00
|
|
|
setTimeout(() => document.body.classList.add('search-results-shown'));
|
|
|
|
hide('#find-styles-inline-group');
|
2020-10-10 11:25:43 +00:00
|
|
|
$('#search-globals').onchange = function () {
|
|
|
|
searchGlobals = this.checked;
|
|
|
|
ready = ready.then(start);
|
|
|
|
};
|
2020-10-04 05:30:02 +00:00
|
|
|
$('#search-query').oninput = function () {
|
|
|
|
query = [];
|
|
|
|
const text = this.value.trim().toLocaleLowerCase();
|
|
|
|
const thisYear = new Date().getFullYear();
|
|
|
|
for (let re = /"(.+?)"|(\S+)/g, m; (m = re.exec(text));) {
|
|
|
|
const n = Number(m[2]);
|
|
|
|
query.push(n >= 2000 && n <= thisYear ? n : m[1] || m[2]);
|
|
|
|
}
|
2020-10-13 18:14:54 +00:00
|
|
|
if (category === STYLUS_CATEGORY && !query.includes('stylus')) {
|
|
|
|
query.push('stylus');
|
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
ready = ready.then(start);
|
|
|
|
};
|
|
|
|
$('#search-order').value = order;
|
|
|
|
$('#search-order').onchange = function () {
|
|
|
|
order = this.value;
|
2021-04-20 17:40:04 +00:00
|
|
|
prefs.set('popup.findSort', order);
|
2020-10-04 05:30:02 +00:00
|
|
|
results.sort(comparator);
|
|
|
|
render();
|
|
|
|
};
|
|
|
|
dom.list = $('#search-results-list');
|
2017-12-11 02:20:59 +00:00
|
|
|
dom.container = $('#search-results');
|
2017-12-11 20:25:41 +00:00
|
|
|
dom.container.dataset.empty = '';
|
2017-12-11 02:20:59 +00:00
|
|
|
dom.error = $('#search-results-error');
|
|
|
|
dom.nav = {};
|
|
|
|
const navOnClick = {prev, next};
|
|
|
|
for (const place of ['top', 'bottom']) {
|
2017-12-11 10:03:03 +00:00
|
|
|
const nav = $(`.search-results-nav[data-type="${place}"]`);
|
2020-11-18 11:17:15 +00:00
|
|
|
nav.appendChild(t.template.searchNav.cloneNode(true));
|
2017-12-11 02:20:59 +00:00
|
|
|
dom.nav[place] = nav;
|
2017-12-11 10:03:03 +00:00
|
|
|
for (const child of $$('[data-type]', nav)) {
|
|
|
|
const type = child.dataset.type;
|
|
|
|
child.onclick = navOnClick[type];
|
|
|
|
nav['_' + type] = child;
|
2017-12-09 21:03:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 20:13:35 +00:00
|
|
|
if (FIREFOX) {
|
2017-12-19 04:05:07 +00:00
|
|
|
let lastShift;
|
2021-01-01 14:27:58 +00:00
|
|
|
window.on('resize', () => {
|
2017-12-13 20:13:35 +00:00
|
|
|
const scrollbarWidth = window.innerWidth - document.scrollingElement.clientWidth;
|
2017-12-19 04:05:07 +00:00
|
|
|
const shift = document.body.getBoundingClientRect().left;
|
|
|
|
if (!scrollbarWidth || shift === lastShift) return;
|
|
|
|
lastShift = shift;
|
|
|
|
document.body.style.setProperty('padding',
|
|
|
|
`0 ${scrollbarWidth + shift}px 0 ${-shift}px`, 'important');
|
|
|
|
}, {passive: true});
|
2017-12-13 20:13:35 +00:00
|
|
|
}
|
2017-12-11 20:25:41 +00:00
|
|
|
|
2021-01-01 14:27:58 +00:00
|
|
|
window.on('styleDeleted', ({detail: {style: {id}}}) => {
|
2020-10-04 05:30:02 +00:00
|
|
|
restoreScrollPosition();
|
|
|
|
const result = results.find(r => r.installedStyleId === id);
|
2017-12-13 21:49:11 +00:00
|
|
|
if (result) {
|
2020-10-04 05:30:02 +00:00
|
|
|
clearTimeout(result.pingbackTimer);
|
|
|
|
renderActionButtons(result.i, -1);
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
|
|
|
});
|
2017-12-10 01:03:04 +00:00
|
|
|
|
2021-01-01 14:27:58 +00:00
|
|
|
window.on('styleAdded', async ({detail: {style}}) => {
|
2020-10-04 05:30:02 +00:00
|
|
|
restoreScrollPosition();
|
2021-04-20 17:40:04 +00:00
|
|
|
const id = calcId(style) || calcId(await API.styles.get(style.id));
|
|
|
|
if (id && results.find(r => r.i === id)) {
|
|
|
|
renderActionButtons(id, style.id);
|
2017-12-10 01:03:04 +00:00
|
|
|
}
|
2017-12-11 02:20:59 +00:00
|
|
|
});
|
|
|
|
}
|
2017-12-10 01:03:04 +00:00
|
|
|
|
2017-12-11 02:20:59 +00:00
|
|
|
function next() {
|
2020-10-04 05:30:02 +00:00
|
|
|
displayedPage = Math.min(totalPages, displayedPage + 1);
|
2017-12-11 02:20:59 +00:00
|
|
|
scrollToFirstResult = true;
|
|
|
|
render();
|
|
|
|
}
|
2017-12-09 21:03:17 +00:00
|
|
|
|
2017-12-11 02:20:59 +00:00
|
|
|
function prev() {
|
|
|
|
displayedPage = Math.max(1, displayedPage - 1);
|
|
|
|
scrollToFirstResult = true;
|
|
|
|
render();
|
|
|
|
}
|
2017-12-09 21:03:17 +00:00
|
|
|
|
2017-12-11 02:20:59 +00:00
|
|
|
function error(reason) {
|
2020-10-04 05:30:02 +00:00
|
|
|
dom.error.textContent = reason;
|
|
|
|
show(dom.error);
|
2020-10-10 11:25:43 +00:00
|
|
|
hide(dom.list);
|
2017-12-19 02:29:21 +00:00
|
|
|
if (dom.error.getBoundingClientRect().bottom < 0) {
|
|
|
|
dom.error.scrollIntoView({behavior: 'smooth', block: 'start'});
|
2017-12-11 19:26:33 +00:00
|
|
|
}
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
2017-12-09 21:03:17 +00:00
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
async function start() {
|
|
|
|
show(dom.container);
|
2020-10-10 11:25:43 +00:00
|
|
|
show(dom.list);
|
2020-10-04 05:30:02 +00:00
|
|
|
hide(dom.error);
|
|
|
|
try {
|
2021-04-20 17:40:04 +00:00
|
|
|
results = [];
|
2020-10-04 05:30:02 +00:00
|
|
|
for (let retry = 0; !results.length && retry <= 2; retry++) {
|
|
|
|
results = await search({retry});
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
if (results.length) {
|
2021-01-01 14:27:58 +00:00
|
|
|
const installedStyles = await API.styles.getAll();
|
2021-04-20 17:40:04 +00:00
|
|
|
const allSupportedIds = new Set(installedStyles.map(calcId));
|
|
|
|
results = results.filter(r => !allSupportedIds.has(r.i));
|
2017-12-11 20:25:41 +00:00
|
|
|
}
|
2020-10-10 11:25:43 +00:00
|
|
|
render();
|
|
|
|
(results.length ? show : hide)(dom.list);
|
|
|
|
if (!results.length && !$('#search-query').value) {
|
2020-10-04 05:30:02 +00:00
|
|
|
error(t('searchResultNoneFound'));
|
2017-12-09 21:03:17 +00:00
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
} catch (reason) {
|
|
|
|
error(reason);
|
|
|
|
}
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function render() {
|
2020-10-04 05:30:02 +00:00
|
|
|
totalPages = Math.ceil(results.length / PAGE_LENGTH);
|
|
|
|
displayedPage = Math.min(displayedPage, totalPages) || 1;
|
|
|
|
let start = (displayedPage - 1) * PAGE_LENGTH;
|
|
|
|
const end = displayedPage * PAGE_LENGTH;
|
2017-12-11 02:20:59 +00:00
|
|
|
let plantAt = 0;
|
|
|
|
let slot = dom.list.children[0];
|
|
|
|
// keep rendered elements with ids in the range of interest
|
|
|
|
while (
|
2020-10-04 05:30:02 +00:00
|
|
|
plantAt < PAGE_LENGTH &&
|
|
|
|
slot && slot.id === 'search-result-' + (results[start] || {}).i
|
2017-12-11 02:20:59 +00:00
|
|
|
) {
|
|
|
|
slot = slot.nextElementSibling;
|
|
|
|
plantAt++;
|
|
|
|
start++;
|
2017-12-09 21:03:17 +00:00
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
// add new elements
|
|
|
|
while (start < Math.min(end, results.length)) {
|
|
|
|
const entry = createSearchResultNode(results[start++]);
|
2017-12-11 02:20:59 +00:00
|
|
|
if (slot) {
|
|
|
|
dom.list.replaceChild(entry, slot);
|
|
|
|
slot = entry.nextElementSibling;
|
2017-12-09 21:03:17 +00:00
|
|
|
} else {
|
2017-12-11 02:20:59 +00:00
|
|
|
dom.list.appendChild(entry);
|
2017-12-09 21:03:17 +00:00
|
|
|
}
|
2017-12-11 02:20:59 +00:00
|
|
|
plantAt++;
|
2017-12-09 21:03:17 +00:00
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
// remove extraneous elements
|
|
|
|
const pageLen = end > results.length &&
|
|
|
|
results.length % PAGE_LENGTH ||
|
|
|
|
Math.min(results.length, PAGE_LENGTH);
|
|
|
|
while (dom.list.children.length > pageLen) {
|
2017-12-11 02:20:59 +00:00
|
|
|
dom.list.lastElementChild.remove();
|
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
if (results.length && 'empty' in dom.container.dataset) {
|
2017-12-11 20:25:41 +00:00
|
|
|
delete dom.container.dataset.empty;
|
|
|
|
}
|
|
|
|
if (scrollToFirstResult && (!FIREFOX || FIREFOX >= 55)) {
|
|
|
|
debounce(doScrollToFirstResult);
|
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
// navigation
|
|
|
|
for (const place in dom.nav) {
|
|
|
|
const nav = dom.nav[place];
|
|
|
|
nav._prev.disabled = displayedPage <= 1;
|
|
|
|
nav._next.disabled = displayedPage >= totalPages;
|
|
|
|
nav._page.textContent = displayedPage;
|
|
|
|
nav._total.textContent = totalPages;
|
|
|
|
}
|
2017-12-11 20:25:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function doScrollToFirstResult() {
|
|
|
|
if (dom.container.scrollHeight > window.innerHeight * 2) {
|
2017-12-11 02:20:59 +00:00
|
|
|
scrollToFirstResult = false;
|
2017-12-11 20:25:41 +00:00
|
|
|
dom.container.scrollIntoView({behavior: 'smooth', block: 'start'});
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-10 09:00:40 +00:00
|
|
|
|
2017-12-11 02:20:59 +00:00
|
|
|
/**
|
2020-10-04 05:30:02 +00:00
|
|
|
* @param {IndexEntry} result
|
|
|
|
* @returns {Node}
|
2017-12-11 02:20:59 +00:00
|
|
|
*/
|
|
|
|
function createSearchResultNode(result) {
|
2020-11-18 11:17:15 +00:00
|
|
|
const entry = t.template.searchResult.cloneNode(true);
|
2020-10-04 05:30:02 +00:00
|
|
|
const {
|
|
|
|
i: id,
|
|
|
|
n: name,
|
|
|
|
r: rating,
|
|
|
|
u: updateTime,
|
|
|
|
w: weeklyInstalls,
|
|
|
|
t: totalInstalls,
|
|
|
|
an: author,
|
|
|
|
sa: shotArchived,
|
|
|
|
sn: shotName,
|
2021-04-20 17:40:04 +00:00
|
|
|
isUsw,
|
2020-10-04 05:30:02 +00:00
|
|
|
} = entry._result = result;
|
|
|
|
entry.id = RESULT_ID_PREFIX + id;
|
|
|
|
// title
|
2017-12-11 02:20:59 +00:00
|
|
|
Object.assign($('.search-result-title', entry), {
|
2021-01-01 14:27:58 +00:00
|
|
|
onclick: Events.openURLandHide,
|
2021-04-20 17:40:04 +00:00
|
|
|
href: isUsw ? `${URLS.usw}style/${id}` :
|
|
|
|
`${URLS.usoArchive}?category=${category}&style=${id}`,
|
2017-12-11 02:20:59 +00:00
|
|
|
});
|
2021-04-20 17:40:04 +00:00
|
|
|
if (isUsw) $('.search-result-title', entry).prepend(USW_ICON.cloneNode(true));
|
2020-10-04 05:30:02 +00:00
|
|
|
$('.search-result-title span', entry).textContent =
|
2020-11-18 11:17:15 +00:00
|
|
|
t.breakWord(name.length < 300 ? name : name.slice(0, 300) + '...');
|
2020-10-04 05:30:02 +00:00
|
|
|
// screenshot
|
2021-04-20 17:40:04 +00:00
|
|
|
const elShot = $('.search-result-screenshot', entry);
|
|
|
|
if (isUsw) {
|
2021-08-12 13:02:48 +00:00
|
|
|
elShot.src = !/^https?:/i.test(shotName) ? BLANK_PIXEL :
|
|
|
|
imgType !== '.jpg' ? shotName.replace(/\.jpg$/, imgType) :
|
|
|
|
shotName;
|
2021-04-20 17:40:04 +00:00
|
|
|
} else {
|
|
|
|
const auto = URLS.uso + `auto_style_screenshots/${id}${USO_AUTO_PIC_SUFFIX}`;
|
|
|
|
Object.assign(elShot, {
|
|
|
|
src: shotName && !shotName.endsWith(USO_AUTO_PIC_SUFFIX)
|
|
|
|
? `${shotArchived ? URLS.usoArchiveRaw : URLS.uso + 'style_'}screenshots/${shotName}`
|
|
|
|
: auto,
|
|
|
|
_src: auto,
|
|
|
|
onerror: fixScreenshot,
|
|
|
|
});
|
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
// author
|
2021-04-20 17:40:04 +00:00
|
|
|
const eAuthor = encodeURIComponent(author);
|
2017-12-11 10:03:03 +00:00
|
|
|
Object.assign($('[data-type="author"] a', entry), {
|
2020-10-04 05:30:02 +00:00
|
|
|
textContent: author,
|
|
|
|
title: author,
|
2021-04-20 17:40:04 +00:00
|
|
|
href: isUsw ? `${URLS.usw}user/${eAuthor}` :
|
|
|
|
`${URLS.usoArchive}?author=${eAuthor.replace(/%20/g, '+')}`,
|
2021-01-01 14:27:58 +00:00
|
|
|
onclick: Events.openURLandHide,
|
2017-12-11 02:20:59 +00:00
|
|
|
});
|
2020-10-04 05:30:02 +00:00
|
|
|
// rating
|
|
|
|
$('[data-type="rating"]', entry).dataset.class =
|
|
|
|
!rating ? 'none' :
|
|
|
|
rating >= 2.5 ? 'good' :
|
|
|
|
rating >= 1.5 ? 'okay' :
|
|
|
|
'bad';
|
|
|
|
$('[data-type="rating"] dd', entry).textContent = rating && rating.toFixed(1) || '';
|
|
|
|
// time
|
2017-12-11 10:03:03 +00:00
|
|
|
Object.assign($('[data-type="updated"] time', entry), {
|
2020-10-04 05:30:02 +00:00
|
|
|
dateTime: updateTime * 1000,
|
2020-11-18 11:17:15 +00:00
|
|
|
textContent: t.formatDate(updateTime * 1000),
|
2017-12-11 04:35:23 +00:00
|
|
|
});
|
2020-10-04 05:30:02 +00:00
|
|
|
// totals
|
|
|
|
$('[data-type="weekly"] dd', entry).textContent = formatNumber(weeklyInstalls);
|
|
|
|
$('[data-type="total"] dd', entry).textContent = formatNumber(totalInstalls);
|
2017-12-11 02:20:59 +00:00
|
|
|
renderActionButtons(entry);
|
|
|
|
return entry;
|
|
|
|
}
|
2017-12-10 01:03:04 +00:00
|
|
|
|
2017-12-11 10:03:03 +00:00
|
|
|
function formatNumber(num) {
|
|
|
|
return (
|
|
|
|
num > 1e9 ? (num / 1e9).toFixed(1) + 'B' :
|
|
|
|
num > 10e6 ? (num / 1e6).toFixed(0) + 'M' :
|
|
|
|
num > 1e6 ? (num / 1e6).toFixed(1) + 'M' :
|
|
|
|
num > 10e3 ? (num / 1e3).toFixed(0) + 'k' :
|
|
|
|
num > 1e3 ? (num / 1e3).toFixed(1) + 'k' :
|
|
|
|
num
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
function fixScreenshot() {
|
|
|
|
const {_src} = this;
|
|
|
|
if (_src && _src !== this.src) {
|
|
|
|
this.src = _src;
|
|
|
|
delete this._src;
|
|
|
|
} else {
|
|
|
|
this.src = BLANK_PIXEL;
|
|
|
|
this.onerror = null;
|
2017-12-13 21:49:11 +00:00
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
}
|
2017-12-11 19:26:33 +00:00
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
function renderActionButtons(entry, installedId) {
|
|
|
|
if (Number(entry)) {
|
|
|
|
entry = $('#' + RESULT_ID_PREFIX + entry);
|
|
|
|
}
|
|
|
|
if (!entry) return;
|
|
|
|
const result = entry._result;
|
|
|
|
if (typeof installedId === 'number') {
|
|
|
|
result.installed = installedId > 0;
|
|
|
|
result.installedStyleId = installedId;
|
|
|
|
}
|
|
|
|
const isInstalled = result.installed;
|
|
|
|
if (isInstalled && !('installed' in entry.dataset)) {
|
2017-12-11 19:26:33 +00:00
|
|
|
entry.dataset.installed = '';
|
2017-12-19 02:30:40 +00:00
|
|
|
$('.search-result-status', entry).textContent = t('clickToUninstall');
|
2020-10-04 05:30:02 +00:00
|
|
|
} else if (!isInstalled && 'installed' in entry.dataset) {
|
2017-12-11 19:26:33 +00:00
|
|
|
delete entry.dataset.installed;
|
|
|
|
$('.search-result-status', entry).textContent = '';
|
2020-10-04 05:30:02 +00:00
|
|
|
hide('.search-result-customize', entry);
|
2017-12-11 19:26:33 +00:00
|
|
|
}
|
2021-01-01 14:27:58 +00:00
|
|
|
const notMatching = installedId > 0 && !$entry(installedId);
|
2020-11-23 12:45:00 +00:00
|
|
|
if (notMatching !== entry.classList.contains('not-matching')) {
|
|
|
|
entry.classList.toggle('not-matching');
|
|
|
|
if (notMatching) {
|
|
|
|
entry.prepend(t.template.searchResultNotMatching.cloneNode(true));
|
|
|
|
} else {
|
|
|
|
entry.firstElementChild.remove();
|
|
|
|
}
|
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
Object.assign($('.search-result-screenshot', entry), {
|
|
|
|
onclick: isInstalled ? uninstall : install,
|
|
|
|
title: isInstalled ? '' : t('installButton'),
|
|
|
|
});
|
|
|
|
$('.search-result-uninstall', entry).onclick = uninstall;
|
|
|
|
$('.search-result-install', entry).onclick = install;
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
2017-12-09 21:03:17 +00:00
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
function renderFullInfo(entry, style) {
|
|
|
|
let {description, vars} = style.usercssData;
|
|
|
|
// description
|
|
|
|
description = (description || '')
|
|
|
|
.replace(/<[^>]*>/g, ' ')
|
|
|
|
.replace(/([^.][.。?!]|[\s,].{50,70})\s+/g, '$1\n')
|
|
|
|
.replace(/([\r\n]\s*){3,}/g, '\n\n');
|
|
|
|
Object.assign($('.search-result-description', entry), {
|
|
|
|
textContent: description,
|
|
|
|
title: description,
|
|
|
|
});
|
|
|
|
// config button
|
|
|
|
if (vars) {
|
|
|
|
const btn = $('.search-result-customize', entry);
|
2021-01-01 14:27:58 +00:00
|
|
|
btn.onclick = () => $('.configure', $entry(style)).click();
|
2020-10-04 05:30:02 +00:00
|
|
|
show(btn);
|
|
|
|
}
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
2017-12-09 21:03:17 +00:00
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
async function install() {
|
2017-12-11 02:20:59 +00:00
|
|
|
const entry = this.closest('.search-result');
|
2020-10-04 05:30:02 +00:00
|
|
|
const result = /** @type IndexEntry */ entry._result;
|
2021-04-20 17:40:04 +00:00
|
|
|
const {i: id, isUsw} = result;
|
2017-12-11 02:20:59 +00:00
|
|
|
const installButton = $('.search-result-install', entry);
|
|
|
|
|
|
|
|
showSpinner(entry);
|
2017-12-13 22:13:16 +00:00
|
|
|
saveScrollPosition(entry);
|
2017-12-11 02:20:59 +00:00
|
|
|
installButton.disabled = true;
|
2017-12-11 04:35:23 +00:00
|
|
|
entry.style.setProperty('pointer-events', 'none', 'important');
|
2021-04-20 17:40:04 +00:00
|
|
|
if (!isUsw) {
|
|
|
|
// FIXME: move this to background page and create an API like installUSOStyle
|
|
|
|
result.pingbackTimer = setTimeout(download, PINGBACK_DELAY,
|
|
|
|
`${URLS.uso}styles/install/${id}?source=stylish-ch`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const updateUrl = isUsw ? URLS.makeUswCodeUrl(id) : URLS.makeUsoArchiveCodeUrl(id);
|
2017-12-11 02:20:59 +00:00
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
try {
|
|
|
|
const sourceCode = await download(updateUrl);
|
2021-01-01 14:27:58 +00:00
|
|
|
const style = await API.usercss.install({sourceCode, updateUrl});
|
2020-10-04 05:30:02 +00:00
|
|
|
renderFullInfo(entry, style);
|
|
|
|
} catch (reason) {
|
|
|
|
error(`Error while downloading usoID:${id}\nReason: ${reason}`);
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
2021-01-01 14:27:58 +00:00
|
|
|
$remove('.lds-spinner', entry);
|
2020-10-04 05:30:02 +00:00
|
|
|
installButton.disabled = false;
|
|
|
|
entry.style.pointerEvents = '';
|
2017-12-09 21:03:17 +00:00
|
|
|
}
|
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
function uninstall() {
|
|
|
|
const entry = this.closest('.search-result');
|
|
|
|
saveScrollPosition(entry);
|
2021-01-01 14:27:58 +00:00
|
|
|
API.styles.delete(entry._result.installedStyleId);
|
2017-12-13 22:46:32 +00:00
|
|
|
}
|
|
|
|
|
2017-12-13 22:13:16 +00:00
|
|
|
function saveScrollPosition(entry) {
|
2020-10-04 05:30:02 +00:00
|
|
|
dom.scrollPos = entry.getBoundingClientRect().top;
|
|
|
|
dom.scrollPosElement = entry;
|
2017-12-13 22:13:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function restoreScrollPosition() {
|
2020-10-04 05:30:02 +00:00
|
|
|
window.scrollBy(0, dom.scrollPosElement.getBoundingClientRect().top - dom.scrollPos);
|
2017-12-13 22:13:16 +00:00
|
|
|
}
|
|
|
|
|
2017-12-09 21:03:17 +00:00
|
|
|
/**
|
|
|
|
* Resolves the Userstyles.org "category" for a given URL.
|
2020-10-04 05:30:02 +00:00
|
|
|
* @returns {boolean} true if the category has actually changed
|
2017-12-09 21:03:17 +00:00
|
|
|
*/
|
2020-10-04 05:30:02 +00:00
|
|
|
function calcCategory({retry} = {}) {
|
2017-12-11 02:20:59 +00:00
|
|
|
const u = tryCatch(() => new URL(tabURL));
|
2020-10-04 05:30:02 +00:00
|
|
|
const old = category;
|
2017-12-09 21:03:17 +00:00
|
|
|
if (!u) {
|
2017-12-11 02:20:59 +00:00
|
|
|
// Invalid URL
|
2020-10-04 05:30:02 +00:00
|
|
|
category = '';
|
2017-12-09 21:03:17 +00:00
|
|
|
} else if (u.protocol === 'file:') {
|
2020-10-04 05:30:02 +00:00
|
|
|
category = 'file:';
|
2017-12-09 21:03:17 +00:00
|
|
|
} else if (u.protocol === location.protocol) {
|
2020-10-04 05:30:02 +00:00
|
|
|
category = STYLUS_CATEGORY;
|
2017-12-09 21:03:17 +00:00
|
|
|
} else {
|
2020-01-28 02:36:43 +00:00
|
|
|
const parts = u.hostname.replace(/\.(?:com?|org)(\.\w{2,3})$/, '$1').split('.');
|
|
|
|
const [tld, main = u.hostname, third, fourth] = parts.reverse();
|
2020-10-04 05:30:02 +00:00
|
|
|
const keepTld = retry !== 1 && !(
|
2020-01-28 02:36:43 +00:00
|
|
|
tld === 'com' ||
|
|
|
|
tld === 'org' && main !== 'userstyles'
|
|
|
|
);
|
|
|
|
const keepThird = !retry && (
|
|
|
|
fourth ||
|
|
|
|
third && third !== 'www' && third !== 'm'
|
|
|
|
);
|
2020-10-04 05:30:02 +00:00
|
|
|
category = (keepThird && `${third}.` || '') + main + (keepTld || keepThird ? `.${tld}` : '');
|
2017-12-12 14:35:38 +00:00
|
|
|
}
|
2020-10-04 05:30:02 +00:00
|
|
|
return category !== old;
|
2017-12-10 07:08:39 +00:00
|
|
|
}
|
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
async function fetchIndex() {
|
|
|
|
const timer = setTimeout(showSpinner, BUSY_DELAY, dom.list);
|
2021-04-20 17:40:04 +00:00
|
|
|
index = [];
|
|
|
|
await Promise.all([
|
|
|
|
download(INDEX_URL, {responseType: 'json'}).then(res => {
|
|
|
|
index = index.concat(res.filter(res => res.f === 'uso'));
|
|
|
|
}).catch(() => {}),
|
|
|
|
download(USW_INDEX_URL, {responseType: 'json'}).then(res => {
|
|
|
|
for (const style of res.data) {
|
|
|
|
style.isUsw = true;
|
|
|
|
index.push(style);
|
|
|
|
}
|
|
|
|
}).catch(() => {}),
|
|
|
|
]);
|
2020-10-04 05:30:02 +00:00
|
|
|
clearTimeout(timer);
|
2021-01-01 14:27:58 +00:00
|
|
|
$remove(':scope > .lds-spinner', dom.list);
|
2020-10-04 05:30:02 +00:00
|
|
|
return index;
|
2017-12-10 07:08:39 +00:00
|
|
|
}
|
2017-12-11 02:20:59 +00:00
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
async function search({retry} = {}) {
|
|
|
|
return retry && !calcCategory({retry})
|
|
|
|
? []
|
|
|
|
: (index || await fetchIndex()).filter(isResultMatching).sort(comparator);
|
2017-12-11 02:20:59 +00:00
|
|
|
}
|
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
function isResultMatching(res) {
|
2020-10-13 18:14:54 +00:00
|
|
|
// We're trying to call calcHaystack only when needed, not on all 100K items
|
|
|
|
const {c} = res;
|
2020-10-04 05:30:02 +00:00
|
|
|
return (
|
2020-10-13 18:14:54 +00:00
|
|
|
c === category ||
|
2021-04-20 18:05:49 +00:00
|
|
|
(category === STYLUS_CATEGORY
|
|
|
|
? c === 'stylus' // USW
|
|
|
|
: c === 'global' && searchGlobals &&
|
|
|
|
(query.length || calcHaystack(res)._nLC.includes(category))
|
2020-10-13 18:14:54 +00:00
|
|
|
)
|
2020-10-10 11:25:43 +00:00
|
|
|
) && (
|
2020-10-13 18:14:54 +00:00
|
|
|
!query.length || // to skip calling calcHaystack
|
|
|
|
query.every(isInHaystack, calcHaystack(res))
|
2020-10-04 05:30:02 +00:00
|
|
|
);
|
2018-12-10 13:14:43 +00:00
|
|
|
}
|
2018-07-04 12:03:54 +00:00
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
/** @this {IndexEntry} haystack */
|
|
|
|
function isInHaystack(needle) {
|
2020-10-10 11:25:43 +00:00
|
|
|
return this._year === needle && this.c !== 'global' ||
|
|
|
|
this._nLC.includes(needle);
|
2018-12-10 13:14:43 +00:00
|
|
|
}
|
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
/**
|
|
|
|
* @param {IndexEntry} a
|
|
|
|
* @param {IndexEntry} b
|
|
|
|
*/
|
|
|
|
function comparator(a, b) {
|
|
|
|
return (
|
|
|
|
order === 'n'
|
|
|
|
? a.n < b.n ? -1 : a.n > b.n
|
|
|
|
: b[order] - a[order]
|
|
|
|
) || b.t - a.t;
|
2018-12-10 13:14:43 +00:00
|
|
|
}
|
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
function calcUsoId({md5Url: m, updateUrl}) {
|
2021-02-26 10:01:27 +00:00
|
|
|
return Number(m && m.match(/\d+|$/)[0]) ||
|
2020-10-04 05:30:02 +00:00
|
|
|
URLS.extractUsoArchiveId(updateUrl);
|
2018-12-10 13:14:43 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 17:40:04 +00:00
|
|
|
function calcUswId({updateUrl}) {
|
|
|
|
return URLS.extractUSwId(updateUrl) || 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function calcId(style) {
|
|
|
|
return calcUsoId(style) || calcUswId(style);
|
|
|
|
}
|
|
|
|
|
2020-10-04 05:30:02 +00:00
|
|
|
function calcHaystack(res) {
|
|
|
|
if (!res._nLC) res._nLC = res.n.toLocaleLowerCase();
|
|
|
|
if (!res._year) res._year = new Date(res.u * 1000).getFullYear();
|
|
|
|
return res;
|
2018-07-04 12:03:54 +00:00
|
|
|
}
|
2021-01-01 14:27:58 +00:00
|
|
|
})();
|