7ecfd0fa80
* handle style action buttons on the site pages * popup find-styles links with icons * auto-updater
213 lines
5.2 KiB
JavaScript
213 lines
5.2 KiB
JavaScript
'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]);
|
|
}
|
|
}
|
|
}
|
|
})();
|