Compare commits
3 Commits
master
...
freestyler
Author | SHA1 | Date | |
---|---|---|---|
|
7ecfd0fa80 | ||
|
291c3a3ec5 | ||
|
83dcc488dd |
|
@ -329,6 +329,10 @@
|
||||||
"message": "Find more styles for this site",
|
"message": "Find more styles for this site",
|
||||||
"description": "Text for a link that gets a list of styles for the current 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": {
|
"helpAlt": {
|
||||||
"message": "Help",
|
"message": "Help",
|
||||||
"description": "Alternate text for help buttons"
|
"description": "Alternate text for help buttons"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* global dbExec, getStyles, saveStyle */
|
/* global dbExec, getStyles, saveStyle, deleteStyle, calcStyleDigest */
|
||||||
/* global handleCssTransitionBug */
|
/* global handleCssTransitionBug */
|
||||||
/* global usercssHelper openEditor */
|
/* global usercssHelper openEditor */
|
||||||
/* global styleViaAPI */
|
/* global styleViaAPI */
|
||||||
|
@ -339,6 +339,29 @@ function onRuntimeMessage(request, sender, sendResponse) {
|
||||||
case 'openEditor':
|
case 'openEditor':
|
||||||
openEditor(request.id);
|
openEditor(request.id);
|
||||||
return;
|
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.
|
'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))
|
return (ignoreDigest ? Promise.resolve() : calcStyleDigest(style))
|
||||||
.then(checkIfEdited)
|
.then(checkIfEdited)
|
||||||
.then(maybeUpdate)
|
.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) {
|
function maybeValidate(json) {
|
||||||
if (json.usercssData) {
|
if (json.usercssData) {
|
||||||
// usercss is already validated while building
|
// 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();
|
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();
|
resolve();
|
||||||
});
|
});
|
||||||
|
if (element.classList.contains(className)) {
|
||||||
|
element.classList.remove(className);
|
||||||
|
setTimeout(() => element.classList.add(className));
|
||||||
|
} else {
|
||||||
element.classList.add(className);
|
element.classList.add(className);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@ function activateTab(tab) {
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
chrome.windows.update(tab.windowId, {focused: true}, 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.enabledFirst': true, // display enabled styles before disabled styles
|
||||||
'popup.stylesFirst': 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.borders': false, // add white borders on the sides
|
||||||
|
'popup.findStylesSource': 'userstyles',
|
||||||
|
|
||||||
'manage.onlyEnabled': false, // display only enabled styles
|
'manage.onlyEnabled': false, // display only enabled styles
|
||||||
'manage.onlyLocal': false, // display only styles created locally
|
'manage.onlyLocal': false, // display only styles created locally
|
||||||
|
@ -104,8 +105,12 @@ var prefs = new function Prefs() {
|
||||||
return deepCopy(values);
|
return deepCopy(values);
|
||||||
},
|
},
|
||||||
|
|
||||||
set(key, value, {broadcast = true, sync = true, fromBroadcast} = {}) {
|
set(key, value, {
|
||||||
const oldValue = values[key];
|
broadcast = true,
|
||||||
|
sync = true,
|
||||||
|
onlyIfChanged = false,
|
||||||
|
fromBroadcast,
|
||||||
|
} = {}) {
|
||||||
switch (typeof defaults[key]) {
|
switch (typeof defaults[key]) {
|
||||||
case typeof value:
|
case typeof value:
|
||||||
break;
|
break;
|
||||||
|
@ -119,9 +124,13 @@ var prefs = new function Prefs() {
|
||||||
value = value === true || value === 'true';
|
value = value === true || value === 'true';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
const oldValue = values[key];
|
||||||
|
const hasChanged = !equal(value, oldValue);
|
||||||
|
if (!hasChanged && onlyIfChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
values[key] = value;
|
values[key] = value;
|
||||||
defineReadonlyProperty(this.readOnlyValues, key, value);
|
defineReadonlyProperty(this.readOnlyValues, key, value);
|
||||||
const hasChanged = !equal(value, oldValue);
|
|
||||||
if (!fromBroadcast) {
|
if (!fromBroadcast) {
|
||||||
if (BG && BG !== window) {
|
if (BG && BG !== window) {
|
||||||
BG.prefs.set(key, BG.deepCopy(value), {broadcast, sync});
|
BG.prefs.set(key, BG.deepCopy(value), {broadcast, sync});
|
||||||
|
|
|
@ -48,6 +48,11 @@ function onRuntimeMessage(msg) {
|
||||||
case 'styleDeleted':
|
case 'styleDeleted':
|
||||||
handleDelete(msg.id);
|
handleDelete(msg.id);
|
||||||
break;
|
break;
|
||||||
|
case 'highlightStyle':
|
||||||
|
if (!highlightEntry(msg) && showStyles.inProgress) {
|
||||||
|
once(window, 'showStyles:done', () => highlightEntry(msg));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +107,7 @@ function initGlobalEvents() {
|
||||||
|
|
||||||
|
|
||||||
function showStyles(styles = []) {
|
function showStyles(styles = []) {
|
||||||
|
showStyles.inProgress = true;
|
||||||
const sorted = styles
|
const sorted = styles
|
||||||
.map(style => ({name: style.name.toLocaleLowerCase(), style}))
|
.map(style => ({name: style.name.toLocaleLowerCase(), style}))
|
||||||
.sort((a, b) => (a.name < b.name ? -1 : a.name === b.name ? 0 : 1));
|
.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);
|
debounce(handleEvent.loadFavicons, 16);
|
||||||
}
|
}
|
||||||
if (sessionStorage.justEditedStyleId) {
|
if (sessionStorage.justEditedStyleId) {
|
||||||
const entry = $(ENTRY_ID_PREFIX + sessionStorage.justEditedStyleId);
|
highlightEntry({id: sessionStorage.justEditedStyleId});
|
||||||
delete 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});
|
filterAndAppend({entry});
|
||||||
if (!entry.matches('.hidden') && reason !== 'import') {
|
if (!entry.matches('.hidden') && reason !== 'import') {
|
||||||
animateElement(entry);
|
highlightEntry({entry});
|
||||||
scrollElementIntoView(entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToggledOrCodeOnly() {
|
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
|
// TODO: remove when these bugs are fixed in FF
|
||||||
function dieOnNullBackground() {
|
function dieOnNullBackground() {
|
||||||
if (!FIREFOX || BG) {
|
if (!FIREFOX || BG) {
|
||||||
|
|
|
@ -54,7 +54,13 @@
|
||||||
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"],
|
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"all_frames": false,
|
"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>"],
|
"matches": ["<all_urls>"],
|
||||||
|
|
12
popup.html
12
popup.html
|
@ -116,6 +116,18 @@
|
||||||
<div id="find-styles">
|
<div id="find-styles">
|
||||||
<a id="find-styles-link" href="https://userstyles.org/styles/browse/"
|
<a id="find-styles-link" href="https://userstyles.org/styles/browse/"
|
||||||
i18n-text="findStylesForSite"></a>
|
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>
|
||||||
<div id="write-style">
|
<div id="write-style">
|
||||||
<span id="write-style-for" i18n-text="writeStyleFor"></span>
|
<span id="write-style-for" i18n-text="writeStyleFor"></span>
|
||||||
|
|
|
@ -239,6 +239,48 @@ body.blocked .actions > .left-gutter {
|
||||||
display: none;
|
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 */
|
/* Never shown, but can be enabled with a style */
|
||||||
|
|
||||||
.enable,
|
.enable,
|
||||||
|
@ -429,6 +471,10 @@ body.blocked .actions > .left-gutter {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes lights-off {
|
@keyframes lights-off {
|
||||||
from {
|
from {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
|
@ -18,9 +18,7 @@ getActiveTab().then(tab =>
|
||||||
tabURL = URLS.supported(url) ? url : '';
|
tabURL = URLS.supported(url) ? url : '';
|
||||||
Promise.all([
|
Promise.all([
|
||||||
tabURL && getStylesSafe({matchUrl: tabURL}),
|
tabURL && getStylesSafe({matchUrl: tabURL}),
|
||||||
onDOMready().then(() => {
|
onDOMready().then(initPopup),
|
||||||
initPopup(tabURL);
|
|
||||||
}),
|
|
||||||
]).then(([styles]) => {
|
]).then(([styles]) => {
|
||||||
showStyles(styles);
|
showStyles(styles);
|
||||||
});
|
});
|
||||||
|
@ -74,7 +72,7 @@ function toggleSideBorders(state = prefs.get('popup.borders')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function initPopup(url) {
|
function initPopup() {
|
||||||
installed = $('#installed');
|
installed = $('#installed');
|
||||||
|
|
||||||
setPopupWidth();
|
setPopupWidth();
|
||||||
|
@ -106,18 +104,55 @@ function initPopup(url) {
|
||||||
installed);
|
installed);
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#find-styles-link').onclick = handleEvent.openURLandHide;
|
$$('[data-toggle-on-click]').forEach(el => {
|
||||||
$('#find-styles-link').href +=
|
// dataset on SVG doesn't work in Chrome 49-??, works in 57+
|
||||||
url.startsWith(location.protocol) ?
|
const target = $(el.getAttribute('data-toggle-on-click'));
|
||||||
'?search_terms=Stylus' :
|
el.onclick = () => target.classList.toggle('hidden');
|
||||||
'all/' + encodeURIComponent(url.startsWith('file:') ? 'file:' : url);
|
});
|
||||||
|
|
||||||
if (!url) {
|
if (!tabURL) {
|
||||||
document.body.classList.add('blocked');
|
document.body.classList.add('blocked');
|
||||||
document.body.insertBefore(template.unavailableInfo, document.body.firstChild);
|
document.body.insertBefore(template.unavailableInfo, document.body.firstChild);
|
||||||
return;
|
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) {
|
getActiveTab().then(function ping(tab, retryCountdown = 10) {
|
||||||
chrome.tabs.sendMessage(tab.id, {method: 'ping'}, {frameId: 0}, pong => {
|
chrome.tabs.sendMessage(tab.id, {method: 'ping'}, {frameId: 0}, pong => {
|
||||||
if (pong) {
|
if (pong) {
|
||||||
|
@ -150,10 +185,10 @@ function initPopup(url) {
|
||||||
// For this URL
|
// For this URL
|
||||||
const urlLink = template.writeStyle.cloneNode(true);
|
const urlLink = template.writeStyle.cloneNode(true);
|
||||||
Object.assign(urlLink, {
|
Object.assign(urlLink, {
|
||||||
href: 'edit.html?url-prefix=' + encodeURIComponent(url),
|
href: 'edit.html?url-prefix=' + encodeURIComponent(tabURL),
|
||||||
title: `url-prefix("${url}")`,
|
title: `url-prefix("${tabURL}")`,
|
||||||
textContent: prefs.get('popup.breadcrumbs.usePath')
|
textContent: prefs.get('popup.breadcrumbs.usePath')
|
||||||
? new URL(url).pathname.slice(1)
|
? new URL(tabURL).pathname.slice(1)
|
||||||
// this URL
|
// this URL
|
||||||
: t('writeStyleForURL').replace(/ /g, '\u00a0'),
|
: t('writeStyleForURL').replace(/ /g, '\u00a0'),
|
||||||
onclick: handleEvent.openLink,
|
onclick: handleEvent.openLink,
|
||||||
|
@ -167,7 +202,7 @@ function initPopup(url) {
|
||||||
matchTargets.appendChild(urlLink);
|
matchTargets.appendChild(urlLink);
|
||||||
|
|
||||||
// For domain
|
// For domain
|
||||||
const domains = BG.getDomains(url);
|
const domains = BG.getDomains(tabURL);
|
||||||
for (const domain of domains) {
|
for (const domain of domains) {
|
||||||
const numParts = domain.length - domain.replace(/\./g, '').length + 1;
|
const numParts = domain.length - domain.replace(/\./g, '').length + 1;
|
||||||
// Don't include TLD
|
// Don't include TLD
|
||||||
|
|
Loading…
Reference in New Issue
Block a user