Compare commits
3 Commits
master
...
freestyler
Author | SHA1 | Date | |
---|---|---|---|
|
7ecfd0fa80 | ||
|
291c3a3ec5 | ||
|
83dcc488dd |
|
@ -329,6 +329,10 @@
|
|||
"message": "Find more styles for this site",
|
||||
"description": "Text for a link that gets a list of styles for the current site"
|
||||
},
|
||||
"findStylesSiteSelectorTooltip": {
|
||||
"message": "Choose where to search for the styles.\n'Find more styles' will also use the choice.",
|
||||
"description": "Short text for a link that gets a list of styles for the current site"
|
||||
},
|
||||
"helpAlt": {
|
||||
"message": "Help",
|
||||
"description": "Alternate text for help buttons"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global dbExec, getStyles, saveStyle */
|
||||
/* global dbExec, getStyles, saveStyle, deleteStyle, calcStyleDigest */
|
||||
/* global handleCssTransitionBug */
|
||||
/* global usercssHelper openEditor */
|
||||
/* global styleViaAPI */
|
||||
|
@ -339,6 +339,29 @@ function onRuntimeMessage(request, sender, sendResponse) {
|
|||
case 'openEditor':
|
||||
openEditor(request.id);
|
||||
return;
|
||||
|
||||
case 'openManager':
|
||||
openURL({url: 'manage.html'}).then(function onReady(tab) {
|
||||
if (tab && tab.status === 'complete') {
|
||||
chrome.tabs.sendMessage(tab.id, {
|
||||
method: 'highlightStyle',
|
||||
id: request.styleId,
|
||||
});
|
||||
} else if (tab) {
|
||||
setTimeout(() => chrome.tabs.get(tab.id, onReady), 100);
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
||||
case 'deleteStyle':
|
||||
deleteStyle(request);
|
||||
return;
|
||||
|
||||
case 'calcStyleDigest':
|
||||
getStyles({id: request.id})
|
||||
.then(([style]) => style && calcStyleDigest(style))
|
||||
.then(sendResponse);
|
||||
return KEEP_CHANNEL_OPEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,9 @@ var updater = {
|
|||
|
||||
'ignoreDigest' option is set on the second manual individual update check on the manage page.
|
||||
*/
|
||||
const maybeUpdate = style.usercssData ? maybeUpdateUsercss : maybeUpdateUSO;
|
||||
const maybeUpdate = style.usercssData ? maybeUpdateUsercss :
|
||||
style.freestylerData ? maybeUpdateFWS :
|
||||
maybeUpdateUSO;
|
||||
return (ignoreDigest ? Promise.resolve() : calcStyleDigest(style))
|
||||
.then(checkIfEdited)
|
||||
.then(maybeUpdate)
|
||||
|
@ -114,6 +116,29 @@ var updater = {
|
|||
});
|
||||
}
|
||||
|
||||
function maybeUpdateFWS() {
|
||||
return updater.invokeFreestylerAPI('check_updates', {
|
||||
json: [style.freestylerData]
|
||||
}).then(data => (
|
||||
!data || !data[0] ? Promise.reject(updater.ERROR_JSON) :
|
||||
!data[0].isUpdated ? Promise.reject(updater.SAME_MD5) :
|
||||
true
|
||||
)).then(() => updater.invokeFreestylerAPI('get_updates', {
|
||||
json: [style.freestylerData]
|
||||
})).then(data => {
|
||||
data = data && data[0] || {};
|
||||
const newStyle = tryJSONparse(data.newJson);
|
||||
if (newStyle) {
|
||||
newStyle.freestylerData = {
|
||||
id: data.id,
|
||||
hash: data.newHash,
|
||||
params: data.newParams,
|
||||
};
|
||||
}
|
||||
return newStyle;
|
||||
});
|
||||
}
|
||||
|
||||
function maybeValidate(json) {
|
||||
if (json.usercssData) {
|
||||
// usercss is already validated while building
|
||||
|
@ -196,6 +221,18 @@ var updater = {
|
|||
});
|
||||
}
|
||||
})(),
|
||||
|
||||
invokeFreestylerAPI(method, params) {
|
||||
return new Promise(resolve => {
|
||||
const encodeParam = k =>
|
||||
encodeURIComponent(k === 'json' ? JSON.stringify(params[k]) : params[k]);
|
||||
const query = Object.keys(params)
|
||||
.map(k => k + '=' + encodeParam(k))
|
||||
.join('&');
|
||||
download(`https://freestyler.ws/api/v2/${method}.php?${query}`)
|
||||
.then(text => resolve(params.json ? tryJSONparse(text) : text));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updater.schedule();
|
||||
|
|
212
content/freestyler.js
Normal file
212
content/freestyler.js
Normal file
|
@ -0,0 +1,212 @@
|
|||
'use strict';
|
||||
|
||||
// IIFE simplifies garbage-collection on orphaning or non-style pages
|
||||
(() => {
|
||||
if (!getPageData().id) {
|
||||
return;
|
||||
}
|
||||
getInstalledStyle().then(setPageDataAndNotify);
|
||||
notifyPage(chrome.runtime.id);
|
||||
|
||||
const pageListeners = {
|
||||
install: onUpdate,
|
||||
update: onUpdate,
|
||||
applyParams: onUpdate,
|
||||
uninstall: onUninstall,
|
||||
stylesManager: onStylesManager,
|
||||
[chrome.runtime.id]: orphanCheck,
|
||||
};
|
||||
|
||||
for (const name of Object.keys(pageListeners)) {
|
||||
window.addEventListener(name, pageListeners[name]);
|
||||
}
|
||||
|
||||
|
||||
function onUpdate(event) {
|
||||
const pageData = getPageData();
|
||||
let installedStyle;
|
||||
getInstalledStyle()
|
||||
.then(checkIfEdited)
|
||||
.then(makeSiteRequest)
|
||||
.then(maybeSaveStyle)
|
||||
.then(setPageDataAndNotify)
|
||||
.catch(() => notifyPage(
|
||||
event.type === 'install' ? 'installFailed' :
|
||||
event.type === 'update' ? 'updateFailed' :
|
||||
event.type === 'applyParams' ? 'applyFailed' : ''
|
||||
));
|
||||
|
||||
function checkIfEdited(style) {
|
||||
return style && invokeBG('calcStyleDigest', {id: style.id})
|
||||
.then(digest => {
|
||||
if (digest === style.originalDigest ||
|
||||
confirm(chrome.i18n.getMessage('updateCheckManualUpdateForce'))) {
|
||||
return style;
|
||||
} else {
|
||||
setPageDataAndNotify(style);
|
||||
return Promise.reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function makeSiteRequest(style) {
|
||||
installedStyle = style;
|
||||
return invokeFreestylerAPI('get_styles_json', {
|
||||
json: [Object.assign(
|
||||
pickProps(pageData, [
|
||||
'id',
|
||||
'params'
|
||||
]), installedStyle && {
|
||||
'installed_params': pickProps(installedStyle.freestylerData, [
|
||||
'params',
|
||||
'hash',
|
||||
]),
|
||||
'installed_hash': installedStyle.freestylerData.hash,
|
||||
}
|
||||
)]
|
||||
});
|
||||
}
|
||||
|
||||
function maybeSaveStyle(data) {
|
||||
data = data && data[0] || {};
|
||||
const style = tryJSONparse(data.json);
|
||||
if (!styleJSONseemsValid(style)) {
|
||||
return Promise.reject();
|
||||
}
|
||||
return invokeBG('saveStyle', {
|
||||
reason: 'update',
|
||||
url: getStyleUrl(),
|
||||
id: installedStyle && installedStyle.id,
|
||||
name: !installedStyle && style.name,
|
||||
sections: style.sections,
|
||||
freestylerData: {
|
||||
id: data.id,
|
||||
hash: data.jsonHash,
|
||||
params: pageData.params,
|
||||
},
|
||||
// use a dummmy URL to make this style updatable
|
||||
updateUrl: location.origin,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onUninstall() {
|
||||
getInstalledStyle().then(style => {
|
||||
if (style && confirm(chrome.i18n.getMessage('deleteStyleConfirm'))) {
|
||||
invokeBG('deleteStyle', style);
|
||||
style = null;
|
||||
}
|
||||
setPageDataAndNotify(style);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function onStylesManager() {
|
||||
getInstalledStyle().then(style => {
|
||||
invokeBG('openManager', {
|
||||
styleId: (style || {}).id,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getInstalledStyle() {
|
||||
return invokeBG('getStyles', {
|
||||
url: getStyleUrl(),
|
||||
}).then(styles => styles[0]);
|
||||
}
|
||||
|
||||
|
||||
function styleJSONseemsValid(style) {
|
||||
return (
|
||||
style &&
|
||||
style.name && typeof style.name === 'string' &&
|
||||
style.sections && typeof style.sections.splice === 'function' &&
|
||||
typeof (style.sections[0] || {}).code === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function setPageDataAndNotify(style) {
|
||||
$id('plugin-data-container').value = JSON.stringify(style ? [style.freestylerData] : []);
|
||||
$id('plugin-info-container').value = JSON.stringify({version: '2.4.1.3'});
|
||||
notifyPage('pluginReady');
|
||||
}
|
||||
|
||||
|
||||
function invokeFreestylerAPI(method, params) {
|
||||
return new Promise(resolve => {
|
||||
const encodeParam = k =>
|
||||
encodeURIComponent(k === 'json' ? JSON.stringify(params[k]) : params[k]);
|
||||
const query = Object.keys(params)
|
||||
.map(k => k + '=' + encodeParam(k))
|
||||
.join('&');
|
||||
invokeBG('download', {
|
||||
url: `https://${location.hostname}/api/v2/${method}.php?${query}`,
|
||||
}).then(text => {
|
||||
resolve(params.json ? tryJSONparse(text) : text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function notifyPage(message) {
|
||||
if (message) {
|
||||
window.dispatchEvent(new CustomEvent(message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getPageData() {
|
||||
// the data may change during page lifetime
|
||||
return tryJSONparse($id('site-data-container').value) || '';
|
||||
}
|
||||
|
||||
|
||||
function getStyleUrl() {
|
||||
return location.href.replace(/#.*/, '');
|
||||
}
|
||||
|
||||
|
||||
function $id(id) {
|
||||
return document.getElementById(id) || '';
|
||||
}
|
||||
|
||||
|
||||
function tryJSONparse(jsonString) {
|
||||
try {
|
||||
return JSON.parse(jsonString);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
|
||||
function pickProps(obj, names) {
|
||||
const result = {};
|
||||
for (const name of names) {
|
||||
result[name] = obj[name];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function invokeBG(method, params) {
|
||||
return new Promise(resolve => {
|
||||
params.method = method;
|
||||
chrome.runtime.sendMessage(params, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function orphanCheck() {
|
||||
const port = chrome.runtime.connect();
|
||||
if (port) {
|
||||
port.disconnect();
|
||||
} else {
|
||||
// we're orphaned due to an extension update
|
||||
for (const name of Object.keys(pageListeners)) {
|
||||
window.removeEventListener(name, pageListeners[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
BIN
images/world-freestyler.png
Normal file
BIN
images/world-freestyler.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
images/world-userstyles.png
Normal file
BIN
images/world-userstyles.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
|
@ -119,7 +119,12 @@ function animateElement(
|
|||
}
|
||||
resolve();
|
||||
});
|
||||
element.classList.add(className);
|
||||
if (element.classList.contains(className)) {
|
||||
element.classList.remove(className);
|
||||
setTimeout(() => element.classList.add(className));
|
||||
} else {
|
||||
element.classList.add(className);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@ function activateTab(tab) {
|
|||
new Promise(resolve => {
|
||||
chrome.windows.update(tab.windowId, {focused: true}, resolve);
|
||||
}),
|
||||
]);
|
||||
]).then(([tab]) => tab);
|
||||
}
|
||||
|
||||
|
||||
|
|
15
js/prefs.js
15
js/prefs.js
|
@ -16,6 +16,7 @@ var prefs = new function Prefs() {
|
|||
'popup.enabledFirst': true, // display enabled styles before disabled styles
|
||||
'popup.stylesFirst': true, // display enabled styles before disabled styles
|
||||
'popup.borders': false, // add white borders on the sides
|
||||
'popup.findStylesSource': 'userstyles',
|
||||
|
||||
'manage.onlyEnabled': false, // display only enabled styles
|
||||
'manage.onlyLocal': false, // display only styles created locally
|
||||
|
@ -104,8 +105,12 @@ var prefs = new function Prefs() {
|
|||
return deepCopy(values);
|
||||
},
|
||||
|
||||
set(key, value, {broadcast = true, sync = true, fromBroadcast} = {}) {
|
||||
const oldValue = values[key];
|
||||
set(key, value, {
|
||||
broadcast = true,
|
||||
sync = true,
|
||||
onlyIfChanged = false,
|
||||
fromBroadcast,
|
||||
} = {}) {
|
||||
switch (typeof defaults[key]) {
|
||||
case typeof value:
|
||||
break;
|
||||
|
@ -119,9 +124,13 @@ var prefs = new function Prefs() {
|
|||
value = value === true || value === 'true';
|
||||
break;
|
||||
}
|
||||
const oldValue = values[key];
|
||||
const hasChanged = !equal(value, oldValue);
|
||||
if (!hasChanged && onlyIfChanged) {
|
||||
return;
|
||||
}
|
||||
values[key] = value;
|
||||
defineReadonlyProperty(this.readOnlyValues, key, value);
|
||||
const hasChanged = !equal(value, oldValue);
|
||||
if (!fromBroadcast) {
|
||||
if (BG && BG !== window) {
|
||||
BG.prefs.set(key, BG.deepCopy(value), {broadcast, sync});
|
||||
|
|
|
@ -48,6 +48,11 @@ function onRuntimeMessage(msg) {
|
|||
case 'styleDeleted':
|
||||
handleDelete(msg.id);
|
||||
break;
|
||||
case 'highlightStyle':
|
||||
if (!highlightEntry(msg) && showStyles.inProgress) {
|
||||
once(window, 'showStyles:done', () => highlightEntry(msg));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +107,7 @@ function initGlobalEvents() {
|
|||
|
||||
|
||||
function showStyles(styles = []) {
|
||||
showStyles.inProgress = true;
|
||||
const sorted = styles
|
||||
.map(style => ({name: style.name.toLocaleLowerCase(), style}))
|
||||
.sort((a, b) => (a.name < b.name ? -1 : a.name === b.name ? 0 : 1));
|
||||
|
@ -137,13 +143,11 @@ function showStyles(styles = []) {
|
|||
debounce(handleEvent.loadFavicons, 16);
|
||||
}
|
||||
if (sessionStorage.justEditedStyleId) {
|
||||
const entry = $(ENTRY_ID_PREFIX + sessionStorage.justEditedStyleId);
|
||||
highlightEntry({id: sessionStorage.justEditedStyleId});
|
||||
delete sessionStorage.justEditedStyleId;
|
||||
if (entry) {
|
||||
animateElement(entry);
|
||||
scrollElementIntoView(entry);
|
||||
}
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('showStyles:done'));
|
||||
showStyles.inProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -428,8 +432,7 @@ function handleUpdate(style, {reason, method} = {}) {
|
|||
}
|
||||
filterAndAppend({entry});
|
||||
if (!entry.matches('.hidden') && reason !== 'import') {
|
||||
animateElement(entry);
|
||||
scrollElementIntoView(entry);
|
||||
highlightEntry({entry});
|
||||
}
|
||||
|
||||
function handleToggledOrCodeOnly() {
|
||||
|
@ -575,6 +578,28 @@ function usePrefsDuringPageLoad() {
|
|||
}
|
||||
|
||||
|
||||
// TODO: move to dom.js and use where applicable
|
||||
function once(element, event, callback, options) {
|
||||
element.addEventListener(event, onEvent, options);
|
||||
function onEvent(...args) {
|
||||
element.removeEventListener(event, onEvent);
|
||||
callback.call(element, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function highlightEntry({
|
||||
id,
|
||||
entry = $(ENTRY_ID_PREFIX + id),
|
||||
}) {
|
||||
if (entry) {
|
||||
animateElement(entry);
|
||||
scrollElementIntoView(entry);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove when these bugs are fixed in FF
|
||||
function dieOnNullBackground() {
|
||||
if (!FIREFOX || BG) {
|
||||
|
|
|
@ -54,7 +54,13 @@
|
|||
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": false,
|
||||
"js": ["content/install.js"]
|
||||
"js": ["content/userstyles.js"]
|
||||
},
|
||||
{
|
||||
"matches": ["http://freestyler.ws/*", "https://freestyler.ws/*"],
|
||||
"run_at": "document_end",
|
||||
"all_frames": false,
|
||||
"js": ["content/freestyler.js"]
|
||||
},
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
|
|
12
popup.html
12
popup.html
|
@ -116,6 +116,18 @@
|
|||
<div id="find-styles">
|
||||
<a id="find-styles-link" href="https://userstyles.org/styles/browse/"
|
||||
i18n-text="findStylesForSite"></a>
|
||||
<span i18n-title="findStylesSiteSelectorTooltip"
|
||||
data-toggle-on-click="#find-styles-sources">
|
||||
<svg class="svg-icon info" viewBox="0 0 14 16" i18n-alt="helpAlt">
|
||||
<path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<div id="find-styles-sources" class="hidden">
|
||||
<a href="https://userstyles.org/styles/browse/all/"
|
||||
data-pref-value="userstyles"><img src="images/world-userstyles.png"><span>userstyles.org</span></a>
|
||||
<a href="http://freestyler.ws/search?q="
|
||||
data-pref-value="freestyler"><img src="images/world-freestyler.png"><span>freestyler.ws</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="write-style">
|
||||
<span id="write-style-for" i18n-text="writeStyleFor"></span>
|
||||
|
|
|
@ -239,6 +239,48 @@ body.blocked .actions > .left-gutter {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#find-styles svg {
|
||||
vertical-align: sub;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#find-styles-sources {
|
||||
display: flex;
|
||||
transition: opacity .5s .1s cubic-bezier(.25,.02,1,.21);
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
box-shadow: 3px 3px 10px rgba(0, 0, 0, .5);
|
||||
border: 1px solid darkgray;
|
||||
bottom: .75em;
|
||||
right: .75em;
|
||||
padding: .5em 0;
|
||||
}
|
||||
|
||||
#find-styles-sources > * {
|
||||
padding: .5em 1em;
|
||||
}
|
||||
|
||||
#find-styles-sources > *:hover {
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
#find-styles img {
|
||||
max-height: 18px;
|
||||
-webkit-filter: grayscale(1) brightness(1.15);
|
||||
filter: grayscale(1) brightness(1.15);
|
||||
transition: -webkit-filter .5s;
|
||||
transition: filter .5s;
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#find-styles a:hover img {
|
||||
-webkit-filter: none;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
/* Never shown, but can be enabled with a style */
|
||||
|
||||
.enable,
|
||||
|
@ -429,6 +471,10 @@ body.blocked .actions > .left-gutter {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@keyframes lights-off {
|
||||
from {
|
||||
background-color: transparent;
|
||||
|
|
|
@ -18,9 +18,7 @@ getActiveTab().then(tab =>
|
|||
tabURL = URLS.supported(url) ? url : '';
|
||||
Promise.all([
|
||||
tabURL && getStylesSafe({matchUrl: tabURL}),
|
||||
onDOMready().then(() => {
|
||||
initPopup(tabURL);
|
||||
}),
|
||||
onDOMready().then(initPopup),
|
||||
]).then(([styles]) => {
|
||||
showStyles(styles);
|
||||
});
|
||||
|
@ -74,7 +72,7 @@ function toggleSideBorders(state = prefs.get('popup.borders')) {
|
|||
}
|
||||
|
||||
|
||||
function initPopup(url) {
|
||||
function initPopup() {
|
||||
installed = $('#installed');
|
||||
|
||||
setPopupWidth();
|
||||
|
@ -106,18 +104,55 @@ function initPopup(url) {
|
|||
installed);
|
||||
}
|
||||
|
||||
$('#find-styles-link').onclick = handleEvent.openURLandHide;
|
||||
$('#find-styles-link').href +=
|
||||
url.startsWith(location.protocol) ?
|
||||
'?search_terms=Stylus' :
|
||||
'all/' + encodeURIComponent(url.startsWith('file:') ? 'file:' : url);
|
||||
$$('[data-toggle-on-click]').forEach(el => {
|
||||
// dataset on SVG doesn't work in Chrome 49-??, works in 57+
|
||||
const target = $(el.getAttribute('data-toggle-on-click'));
|
||||
el.onclick = () => target.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
if (!url) {
|
||||
if (!tabURL) {
|
||||
document.body.classList.add('blocked');
|
||||
document.body.insertBefore(template.unavailableInfo, document.body.firstChild);
|
||||
return;
|
||||
}
|
||||
|
||||
const findStylesElement = $('#find-styles-link');
|
||||
findStylesElement.onclick = handleEvent.openURLandHide;
|
||||
function openAndRememberSource(event) {
|
||||
prefs.set('popup.findStylesSource', this.dataset.prefValue, {onlyIfChanged: true});
|
||||
handleEvent.openURLandHide.call(this, event);
|
||||
}
|
||||
$$('#find-styles-sources a').forEach(a => (a.onclick = openAndRememberSource));
|
||||
// touch devices don't have onHover events so the element we'll be toggled via clicking (touching)
|
||||
if ('ontouchstart' in document.body) {
|
||||
const menu = $('#find-styles-sources');
|
||||
const menuData = menu.dataset;
|
||||
const closeOnOutsideTouch = event => {
|
||||
if (!menu.contains(event.target)) {
|
||||
delete menuData.show;
|
||||
window.removeEventListener('touchstart', closeOnOutsideTouch);
|
||||
}
|
||||
};
|
||||
findStylesElement.onclick = event => {
|
||||
if (menuData.show) {
|
||||
closeOnOutsideTouch(event);
|
||||
} else {
|
||||
menuData.show = true;
|
||||
window.addEventListener('touchstart', closeOnOutsideTouch);
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
}
|
||||
// freestyler: strip 'www.' when hostname has 3+ parts
|
||||
$('#find-styles a[href*="freestyler"]').href +=
|
||||
encodeURIComponent(new URL(tabURL).hostname.replace(/^www\.(?=.+?\.)/, ''));
|
||||
// userstyles: send just 'file:' for file:// links
|
||||
$('#find-styles a[href*="userstyles"]').href +=
|
||||
encodeURIComponent(tabURL.startsWith('file:') ? 'file:' : tabURL);
|
||||
// set the default link to the last used one
|
||||
$$(`#find-styles a[data-pref-value="${(prefs.get('popup.findStylesSource') || 'userstyles')}"]`)
|
||||
.forEach(a => (findStylesElement.href = a.href));
|
||||
|
||||
getActiveTab().then(function ping(tab, retryCountdown = 10) {
|
||||
chrome.tabs.sendMessage(tab.id, {method: 'ping'}, {frameId: 0}, pong => {
|
||||
if (pong) {
|
||||
|
@ -150,10 +185,10 @@ function initPopup(url) {
|
|||
// For this URL
|
||||
const urlLink = template.writeStyle.cloneNode(true);
|
||||
Object.assign(urlLink, {
|
||||
href: 'edit.html?url-prefix=' + encodeURIComponent(url),
|
||||
title: `url-prefix("${url}")`,
|
||||
href: 'edit.html?url-prefix=' + encodeURIComponent(tabURL),
|
||||
title: `url-prefix("${tabURL}")`,
|
||||
textContent: prefs.get('popup.breadcrumbs.usePath')
|
||||
? new URL(url).pathname.slice(1)
|
||||
? new URL(tabURL).pathname.slice(1)
|
||||
// this URL
|
||||
: t('writeStyleForURL').replace(/ /g, '\u00a0'),
|
||||
onclick: handleEvent.openLink,
|
||||
|
@ -167,7 +202,7 @@ function initPopup(url) {
|
|||
matchTargets.appendChild(urlLink);
|
||||
|
||||
// For domain
|
||||
const domains = BG.getDomains(url);
|
||||
const domains = BG.getDomains(tabURL);
|
||||
for (const domain of domains) {
|
||||
const numParts = domain.length - domain.replace(/\./g, '').length + 1;
|
||||
// Don't include TLD
|
||||
|
|
Loading…
Reference in New Issue
Block a user