Only one "find styles" link. Hides result after installing.
* "Find more styles for this site" renamed to "Search on userstyles.org". * Only appears below search results after "Find more styles" is clicked. * When style is installed from serach results, it no longer appears in the results. * "Install" uses already-fetched .JSON data. Avoiding useless call to download().
This commit is contained in:
parent
1daa12b59f
commit
b16ee42deb
|
@ -333,9 +333,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"loadStylesForSite": {
|
||||
"message": "Load Styles",
|
||||
"description": "Text for a link that loads styles within the popup"
|
||||
"openSearchWebsite": {
|
||||
"message": "Search on userstyles.org",
|
||||
"description": "Text for a link that searches for styles for this current site on userstyles.org"
|
||||
},
|
||||
"searchResultInstall": {
|
||||
"message": "Install",
|
||||
|
|
12
popup.html
12
popup.html
|
@ -143,14 +143,14 @@
|
|||
</div>
|
||||
<div class="left-gutter"></div>
|
||||
<div class="main-controls">
|
||||
<div id="load-search-results">
|
||||
<a id="load-search-results-link" href="#"
|
||||
i18n-text="loadStylesForSite"></a>
|
||||
<div id="find-styles">
|
||||
<a id="find-styles-link" href="#"
|
||||
i18n-text="findStylesForSite"></a>
|
||||
</div>
|
||||
<div id="searchResults-error" class="hidden"></div>
|
||||
<div id="find-styles">
|
||||
<a id="find-styles-link" href="https://userstyles.org/styles/browse/"
|
||||
i18n-text="findStylesForSite"></a>
|
||||
<div id="open-search">
|
||||
<a id="open-search-link" href="https://userstyles.org/styles/browse/"
|
||||
i18n-text="openSearchWebsite"></a>
|
||||
</div>
|
||||
<div id="write-style">
|
||||
<span id="write-style-for" i18n-text="writeStyleFor"></span>
|
||||
|
|
|
@ -106,8 +106,8 @@ function initPopup(url) {
|
|||
installed);
|
||||
}
|
||||
|
||||
$('#find-styles-link').onclick = handleEvent.openURLandHide;
|
||||
$('#find-styles-link').href +=
|
||||
$('#open-search-link').onclick = handleEvent.openURLandHide;
|
||||
$('#open-search-link').href +=
|
||||
url.startsWith(location.protocol) ?
|
||||
'?search_terms=Stylus' :
|
||||
'all/' + encodeURIComponent(url.startsWith('file:') ? 'file:' : url);
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
}
|
||||
|
||||
#searchResults,
|
||||
#load-search-results {
|
||||
#find-styles {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#searchResults.hidden,
|
||||
#searchResults-error.hidden,
|
||||
#load-search-results.hidden {
|
||||
#find-styles.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
* @returns {Object} Exposed methods representing the search results on userstyles.org
|
||||
*/
|
||||
function SearchUserstyles() {
|
||||
let totalPages, totalResults;
|
||||
let totalPages;
|
||||
let currentPage = 1;
|
||||
let exhausted = false;
|
||||
|
||||
return {getCurrentPage, getTotalPages, getTotalResults, getCategory, search, fetchStyleJson};
|
||||
return {getCurrentPage, getTotalPages, getCategory, isExhausted, search, fetchStyleJson};
|
||||
|
||||
function getCurrentPage() {
|
||||
return currentPage;
|
||||
|
@ -19,8 +20,8 @@ function SearchUserstyles() {
|
|||
return totalPages;
|
||||
}
|
||||
|
||||
function getTotalResults() {
|
||||
return totalResults;
|
||||
function isExhausted() {
|
||||
return exhausted;
|
||||
}
|
||||
|
||||
function getCategory(url) {
|
||||
|
@ -63,12 +64,13 @@ function SearchUserstyles() {
|
|||
|
||||
/**
|
||||
* Fetches (and JSON-parses) search results from a userstyles.org search API.
|
||||
* Automatically sets currentPage, totalPages, and totalResults.
|
||||
* Automatically sets currentPage and totalPages.
|
||||
* @param {string} category The usrestyles.org "category" (subcategory) OR a any search string.
|
||||
* @return {Object} Response object from userstyles.org
|
||||
*/
|
||||
function search(category) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log('search(' + category + ') currentPage:' + currentPage + ' totalPages:' + totalPages);
|
||||
if (totalPages !== undefined && currentPage > totalPages) {
|
||||
resolve({'data':[]});
|
||||
}
|
||||
|
@ -91,9 +93,10 @@ function SearchUserstyles() {
|
|||
const responseJson = tryJSONparse(xhr.responseText);
|
||||
currentPage = responseJson.current_page + 1;
|
||||
totalPages = responseJson.total_pages;
|
||||
totalResults = responseJson.total_entries;
|
||||
exhausted = (currentPage > totalPages);
|
||||
resolve(responseJson);
|
||||
} else {
|
||||
exhausted = true;
|
||||
reject(xhr.status);
|
||||
}
|
||||
};
|
||||
|
@ -113,11 +116,13 @@ function SearchUserstyles() {
|
|||
*/
|
||||
const SearchResults = (() => {
|
||||
const DISPLAYED_RESULTS_PER_PAGE = 3; // Number of results to display in popup.html
|
||||
const DELAY_BETWEEN_RESULTS_MS = 500; // Millisecs to wait before fetching next batch of search results.
|
||||
const DELAY_BETWEEN_FETCHING_STYLES = 0; // Millisecs to wait before fetching .JSON for next search result.
|
||||
const DELAY_AFTER_FETCHING_STYLES = 500; // Millisecs to wait before fetching next batch of search results.
|
||||
const DELAY_BEFORE_SEARCHING_STYLES = 0; // Millisecs to wait before fetching .JSON for next search result.
|
||||
const searchAPI = SearchUserstyles();
|
||||
const unprocessedResults = []; // Search results not yet processed.
|
||||
const processedResults = []; // Search results that are not installed and apply ot the page (includes 'json' field with full style).
|
||||
const BLANK_PIXEL_DATA = 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAA' +
|
||||
'C1HAwCAAAAC0lEQVR42mOcXQ8AAbsBHLLDr5MAAAAASUVORK5CYII=';
|
||||
let loading = false;
|
||||
let tabURL; // The active tab's URL.
|
||||
let currentDisplayedPage = 1; // Current page number in popup.html
|
||||
|
@ -143,8 +148,11 @@ const SearchResults = (() => {
|
|||
}
|
||||
$('#searchResultsNav-currentPage').textContent = currentDisplayedPage;
|
||||
|
||||
// Hack: Add 1 page if there's results left to process.
|
||||
const totalResultsCount = processedResults.length + (unprocessedResults.length ? DISPLAYED_RESULTS_PER_PAGE : 0);
|
||||
let totalResultsCount = processedResults.length;
|
||||
if (unprocessedResults.length > 0) {
|
||||
// Add 1 page if there's results left to process.
|
||||
totalResultsCount += DISPLAYED_RESULTS_PER_PAGE;
|
||||
}
|
||||
const totalPageCount = Math.ceil(Math.max(1, totalResultsCount / DISPLAYED_RESULTS_PER_PAGE));
|
||||
if (currentDisplayedPage >= totalPageCount || loading) {
|
||||
$('#searchResultsNav-next').setAttribute('disabled', 'disabled');
|
||||
|
@ -162,9 +170,9 @@ const SearchResults = (() => {
|
|||
}
|
||||
|
||||
function shouldLoadMore() {
|
||||
const result = (processedResults.length < currentDisplayedPage * DISPLAYED_RESULTS_PER_PAGE)
|
||||
const result = (processedResults.length < currentDisplayedPage * DISPLAYED_RESULTS_PER_PAGE);
|
||||
console.log('shouldLoadMore:',
|
||||
result ? 'YES' : 'NO',
|
||||
result === true ? 'YES' : 'NO',
|
||||
' processedResults.length(' + processedResults.length + ')',
|
||||
'< currentDisplayedPage(' + currentDisplayedPage + ')',
|
||||
'* DISPLAYED_RESULTS_PER_PAGE(' + DISPLAYED_RESULTS_PER_PAGE + ')');
|
||||
|
@ -176,7 +184,7 @@ const SearchResults = (() => {
|
|||
console.log('loadMoreIfNeeded: YES.');
|
||||
loading = true;
|
||||
render();
|
||||
setTimeout(load, 1000);
|
||||
setTimeout(load, DELAY_BEFORE_SEARCHING_STYLES);
|
||||
} else {
|
||||
console.log('loadMoreIfNeeded: NO.');
|
||||
loading = false;
|
||||
|
@ -238,7 +246,14 @@ const SearchResults = (() => {
|
|||
return true;
|
||||
}
|
||||
|
||||
$('#load-search-results').classList.add('hidden');
|
||||
if (searchAPI.isExhausted()) {
|
||||
console.log('searchAPI is exhausted');
|
||||
loading = false;
|
||||
render();
|
||||
return true;
|
||||
}
|
||||
|
||||
$('#find-styles').classList.add('hidden');
|
||||
$('#searchResults').classList.remove('hidden');
|
||||
$('#searchResults-error').classList.add('hidden');
|
||||
|
||||
|
@ -283,70 +298,77 @@ const SearchResults = (() => {
|
|||
|
||||
// Process the next result in the queue.
|
||||
const nextResult = unprocessedResults.shift();
|
||||
const matchingStyles = getMatchingInstalledStyles(nextResult);
|
||||
if (matchingStyles.length > 0) {
|
||||
// Style already installed, skip it.
|
||||
// TODO: Include the style anyway with option to "Uninstall" (?)
|
||||
console.log('[' + unprocessedResults.length + '] style "' + nextResult.name + '" already installed: CONTINUING');
|
||||
alreadyInstalledResults += 1;
|
||||
setTimeout(processNextResult, DELAY_BETWEEN_FETCHING_STYLES); // Keep processing
|
||||
} else if (nextResult.category !== 'site') {
|
||||
// Style is not for a website, skip it.
|
||||
console.log('[' + unprocessedResults.length + '] style "' + nextResult.name + '" category is for "' + nextResult.category + '", not "site": CONTINUING');
|
||||
nonApplicableResults += 1;
|
||||
setTimeout(processNextResult, 0); // Keep processing
|
||||
} else {
|
||||
// Style not installed, fetch full style to see if it applies to this site.
|
||||
console.log('[' + unprocessedResults.length + '] fetching "' + nextResult.name + '": CONTINUING');
|
||||
searchAPI.fetchStyleJson(nextResult.id)
|
||||
.then(userstyleJson => {
|
||||
// Extract applicable sections (i.e. styles that apply to the current site)
|
||||
const applicableSections = BG.getApplicableSections({
|
||||
style: userstyleJson,
|
||||
matchUrl: tabURL,
|
||||
stopOnFirst: true
|
||||
});
|
||||
if (applicableSections.length === 0) {
|
||||
// Style is invalid (does not apply to this site).
|
||||
nonApplicableResults += 1;
|
||||
} else {
|
||||
// Style is valid (can apply to this site).
|
||||
nextResult.json = userstyleJson; // Store Style JSON for easy installing later.
|
||||
processedResults.push(nextResult);
|
||||
render();
|
||||
}
|
||||
console.log('[' + unprocessedResults.length + '] Processed "' + nextResult.name + '"',
|
||||
'processedResults=' + processedResults.length,
|
||||
'skipped-installed=' + alreadyInstalledResults,
|
||||
'skipped-irrelevant=' + nonApplicableResults,
|
||||
'CONTINUING @ sleep=' + DELAY_BETWEEN_RESULTS_MS);
|
||||
setTimeout(processNextResult, DELAY_BETWEEN_RESULTS_MS); // Keep processing
|
||||
})
|
||||
.catch(reason => {
|
||||
console.log('[' + unprocessedResults.length + '] Error while loading style ID ' + nextResult.id + ': ' + reason);
|
||||
setTimeout(processNextResult, DELAY_BETWEEN_RESULTS_MS); // Keep processing
|
||||
});
|
||||
}
|
||||
isStyleInstalled(nextResult)
|
||||
.then(isInstalled => {
|
||||
if (isInstalled) {
|
||||
// Style already installed, skip it.
|
||||
// TODO: Include the style anyway with option to "Uninstall" (?)
|
||||
console.log('[' + unprocessedResults.length + '] style "' + nextResult.name +
|
||||
'" already installed: CONTINUING');
|
||||
alreadyInstalledResults += 1;
|
||||
setTimeout(processNextResult, DELAY_AFTER_FETCHING_STYLES); // Keep processing
|
||||
} else if (nextResult.category !== 'site') {
|
||||
// Style is not for a website, skip it.
|
||||
console.log('[' + unprocessedResults.length + '] style "' + nextResult.name +
|
||||
'" category is for "' + nextResult.category + '", not "site": CONTINUING');
|
||||
nonApplicableResults += 1;
|
||||
setTimeout(processNextResult, 0); // Keep processing
|
||||
} else {
|
||||
// Style not installed, fetch full style to see if it applies to this site.
|
||||
console.log('[' + unprocessedResults.length + '] fetching "' + nextResult.name + '": CONTINUING');
|
||||
searchAPI.fetchStyleJson(nextResult.id)
|
||||
.then(userstyleJson => {
|
||||
// Extract applicable sections (i.e. styles that apply to the current site)
|
||||
const applicableSections = BG.getApplicableSections({
|
||||
style: userstyleJson,
|
||||
matchUrl: tabURL,
|
||||
stopOnFirst: true
|
||||
});
|
||||
if (applicableSections.length === 0) {
|
||||
// Style is invalid (does not apply to this site).
|
||||
nonApplicableResults += 1;
|
||||
} else {
|
||||
// Style is valid (can apply to this site).
|
||||
nextResult.json = userstyleJson; // Store Style JSON for easy installing later.
|
||||
processedResults.push(nextResult);
|
||||
render();
|
||||
}
|
||||
console.log('[' + unprocessedResults.length + '] Processed "' + nextResult.name + '"',
|
||||
'processedResults=' + processedResults.length,
|
||||
'skipped-installed=' + alreadyInstalledResults,
|
||||
'skipped-irrelevant=' + nonApplicableResults,
|
||||
'CONTINUING @ sleep=' + DELAY_AFTER_FETCHING_STYLES);
|
||||
setTimeout(processNextResult, DELAY_AFTER_FETCHING_STYLES); // Keep processing
|
||||
})
|
||||
.catch(reason => {
|
||||
console.log('[' + unprocessedResults.length + '] Error while loading style ID ' +
|
||||
nextResult.id + ': ' + reason);
|
||||
setTimeout(processNextResult, DELAY_AFTER_FETCHING_STYLES); // Keep processing
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Promises a list of installed styles that match the provided search result.
|
||||
* Promises if the given searchResult matches an already-installed style.
|
||||
* @param {Object} userstyleSearchResult Search result object from userstyles.org
|
||||
* @returns {Promise<boolean>} Resolves if the style is installed.
|
||||
*/
|
||||
function getMatchingInstalledStyles(userstyleSearchResult) {
|
||||
function isStyleInstalled(userstyleSearchResult) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
getStylesSafe()
|
||||
.then(installedStyles => {
|
||||
console.log('Seeing if searchResult(', userstyleSearchResult, ') is in matchingStyles');
|
||||
const matchingStyles = installedStyles.filter(installedStyle => {
|
||||
// Compare installed name to search result name.
|
||||
let isMatch = installedStyle.name === userstyleSearchResult.name;
|
||||
// Also compare if search result ID (userstyles ID) is mentioned in the installed updateUrl.
|
||||
// Compare if search result ID (userstyles ID) is mentioned in the installed updateUrl.
|
||||
if (installedStyle.updateUrl) {
|
||||
isMatch &= installedStyle.updateUrl.indexOf('/' + userstyleSearchResult.id + '.json') >= 0;
|
||||
}
|
||||
return isMatch;
|
||||
});
|
||||
resolve(matchingStyles);
|
||||
resolve(matchingStyles.length > 0);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
|
@ -371,6 +393,10 @@ const SearchResults = (() => {
|
|||
*/
|
||||
console.log('createSearchResultNode(', userstyleSearchResult, ')');
|
||||
|
||||
if (userstyleSearchResult.installed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = template.searchResult.cloneNode(true);
|
||||
Object.assign(entry, {
|
||||
id: 'searchResult-' + userstyleSearchResult.id
|
||||
|
@ -389,7 +415,7 @@ const SearchResults = (() => {
|
|||
const screenshot = $('.searchResult-screenshot', entry);
|
||||
let screenshotUrl = userstyleSearchResult.screenshot_url;
|
||||
if (screenshotUrl === null) {
|
||||
screenshotUrl = 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mOcXQ8AAbsBHLLDr5MAAAAASUVORK5CYII=';
|
||||
screenshotUrl = BLANK_PIXEL_DATA;
|
||||
} else if (RegExp(/^[0-9]*_after.(jpe?g|png|gif)$/i).test(screenshotUrl)) {
|
||||
screenshotUrl = 'https://userstyles.org/style_screenshot_thumbnails/' + screenshotUrl;
|
||||
screenshot.classList.remove('no-screenshot');
|
||||
|
@ -429,13 +455,24 @@ const SearchResults = (() => {
|
|||
// TODO on Install: Promise.all([fetchJSON, fetchHTML]) -> popup if customization is present, install otheriwse.
|
||||
const styleId = userstyleSearchResult.id;
|
||||
const url = 'https://userstyles.org/styles/chrome/' + styleId + '.json';
|
||||
download(url)
|
||||
.then(responseText => {
|
||||
saveStyleSafe(tryJSONparse(responseText))
|
||||
.then(() => {
|
||||
// Hide search result after installing
|
||||
entry.parentNode.removeChild(entry);
|
||||
});
|
||||
saveStyleSafe(userstyleSearchResult.json)
|
||||
.then(() => {
|
||||
// Remove search result after installing
|
||||
let matchingIndex = -1;
|
||||
processedResults.forEach((processedResult, index) => {
|
||||
console.log('processedResult[' + index + '].id =', processedResult.id,
|
||||
'userstyleSearchResult.id =', userstyleSearchResult.id);
|
||||
if (processedResult.id === userstyleSearchResult.id) {
|
||||
matchingIndex = index;
|
||||
}
|
||||
});
|
||||
console.log('matchingIndex =', matchingIndex);
|
||||
if (matchingIndex >= 0) {
|
||||
console.log('processedResults.length before', processedResults.length);
|
||||
processedResults.splice(matchingIndex, 1);
|
||||
console.log('processedResults.length after', processedResults.length);
|
||||
}
|
||||
processNextResult();
|
||||
})
|
||||
.catch(reason => {
|
||||
console.log('install:download(', url, ') => [ERROR]: ', reason);
|
||||
|
@ -447,7 +484,7 @@ const SearchResults = (() => {
|
|||
})();
|
||||
|
||||
onDOMready().then(() => {
|
||||
$('#load-search-results-link').onclick = SearchResults.load;
|
||||
$('#find-styles-link').onclick = SearchResults.load;
|
||||
$('#searchResultsNav-prev').onclick = SearchResults.prev;
|
||||
$('#searchResultsNav-next').onclick = SearchResults.next;
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user