allow live-reload without reinstalling
This commit is contained in:
parent
c9e60dc19b
commit
a339b50e27
|
@ -588,6 +588,10 @@
|
||||||
"message": "Live reload",
|
"message": "Live reload",
|
||||||
"description": "The label of live-reload feature"
|
"description": "The label of live-reload feature"
|
||||||
},
|
},
|
||||||
|
"liveReloadInstallHint": {
|
||||||
|
"message": "Live reload is enabled so the installed style will be auto-updated on external changes while both this tab and the source file tab are open.",
|
||||||
|
"description": "The label of live-reload feature"
|
||||||
|
},
|
||||||
"liveReloadError": {
|
"liveReloadError": {
|
||||||
"message": "An error occurred while watching the file",
|
"message": "An error occurred while watching the file",
|
||||||
"description": "The label of live-reload error"
|
"description": "The label of live-reload error"
|
||||||
|
|
|
@ -1,7 +1,64 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function createSourceLoader() {
|
(() => {
|
||||||
let source;
|
// some weird bug in new Chrome: the content script gets injected multiple times
|
||||||
|
if (typeof window.initUsercssInstall === 'function') return;
|
||||||
|
if (!/text\/(css|plain)/.test(document.contentType) ||
|
||||||
|
!/==userstyle==/i.test(document.body.textContent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.initUsercssInstall = () => {};
|
||||||
|
|
||||||
|
orphanCheck();
|
||||||
|
|
||||||
|
const DELAY = 500;
|
||||||
|
const url = location.href;
|
||||||
|
let sourceCode, port, timer;
|
||||||
|
|
||||||
|
chrome.runtime.onConnect.addListener(onConnected);
|
||||||
|
chrome.runtime.sendMessage({method: 'installUsercss', url}, r =>
|
||||||
|
r && r.__ERROR__ && alert(r.__ERROR__));
|
||||||
|
|
||||||
|
function onConnected(newPort) {
|
||||||
|
port = newPort;
|
||||||
|
port.onDisconnect.addListener(stop);
|
||||||
|
port.onMessage.addListener(onMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMessage(msg, port) {
|
||||||
|
switch (msg.method) {
|
||||||
|
case 'getSourceCode':
|
||||||
|
fetchText(url)
|
||||||
|
.then(text => {
|
||||||
|
sourceCode = sourceCode || text;
|
||||||
|
port.postMessage({
|
||||||
|
method: msg.method + 'Response',
|
||||||
|
sourceCode,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => port.postMessage({
|
||||||
|
method: msg.method + 'Response',
|
||||||
|
error: err.message || String(err),
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'liveReloadStart':
|
||||||
|
start();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'liveReloadStop':
|
||||||
|
stop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'closeTab':
|
||||||
|
if (history.length > 1) {
|
||||||
|
history.back();
|
||||||
|
} else {
|
||||||
|
chrome.runtime.sendMessage({method: 'closeTab'});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fetchText(url) {
|
function fetchText(url) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -14,22 +71,8 @@ function createSourceLoader() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function load() {
|
|
||||||
return fetchText(location.href).then(newSource => {
|
|
||||||
source = newSource;
|
|
||||||
return source;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function watch(cb) {
|
|
||||||
let timer;
|
|
||||||
const DELAY = 1000;
|
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
if (timer) {
|
timer = timer || setTimeout(check, DELAY);
|
||||||
return;
|
|
||||||
}
|
|
||||||
timer = setTimeout(check, DELAY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stop() {
|
function stop() {
|
||||||
|
@ -38,80 +81,30 @@ function createSourceLoader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function check() {
|
function check() {
|
||||||
fetchText(location.href)
|
fetchText(url)
|
||||||
.then(newSource => {
|
.then(text => {
|
||||||
if (source !== newSource) {
|
if (sourceCode === text) return;
|
||||||
source = newSource;
|
sourceCode = text;
|
||||||
return cb(source);
|
port.postMessage({method: 'sourceCodeChanged', sourceCode});
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log(chrome.i18n.getMessage('liveReloadError', error));
|
console.log(chrome.i18n.getMessage('liveReloadError', error));
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
timer = setTimeout(check, DELAY);
|
timer = null;
|
||||||
|
start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {start, stop};
|
function orphanCheck() {
|
||||||
|
const eventName = chrome.runtime.id + '-install-hook-usercss';
|
||||||
|
const orphanCheckRequest = () => {
|
||||||
|
if (chrome.i18n && chrome.i18n.getUILanguage()) return true;
|
||||||
|
// In Chrome content script is orphaned on an extension update/reload
|
||||||
|
// so we need to detach event listeners
|
||||||
|
removeEventListener(eventName, orphanCheckRequest, true);
|
||||||
|
};
|
||||||
|
dispatchEvent(new Event(eventName));
|
||||||
|
addEventListener(eventName, orphanCheckRequest, true);
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
return {load, watch, source: () => source};
|
|
||||||
}
|
|
||||||
|
|
||||||
function initUsercssInstall() {
|
|
||||||
const sourceLoader = createSourceLoader();
|
|
||||||
const pendingSource = sourceLoader.load();
|
|
||||||
let watcher;
|
|
||||||
|
|
||||||
chrome.runtime.onConnect.addListener(port => {
|
|
||||||
port.onMessage.addListener(msg => {
|
|
||||||
switch (msg.method) {
|
|
||||||
case 'getSourceCode':
|
|
||||||
pendingSource
|
|
||||||
.then(sourceCode => port.postMessage({method: msg.method + 'Response', sourceCode}))
|
|
||||||
.catch(err => port.postMessage({method: msg.method + 'Response', error: err.message || String(err)}));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'liveReloadStart':
|
|
||||||
if (!watcher) {
|
|
||||||
watcher = sourceLoader.watch(sourceCode => {
|
|
||||||
port.postMessage({method: 'sourceCodeChanged', sourceCode});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
watcher.start();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'liveReloadStop':
|
|
||||||
watcher.stop();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'closeTab':
|
|
||||||
if (history.length > 1) {
|
|
||||||
history.back();
|
|
||||||
} else {
|
|
||||||
chrome.runtime.sendMessage({method: 'closeTab'});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
chrome.runtime.sendMessage({
|
|
||||||
method: 'installUsercss',
|
|
||||||
url: location.href,
|
|
||||||
}, r => r && r.__ERROR__ && alert(r.__ERROR__));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUsercss() {
|
|
||||||
if (!/text\/(css|plain)/.test(document.contentType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!/==userstyle==/i.test(document.body.textContent)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUsercss()) {
|
|
||||||
initUsercssInstall();
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,50 +5,37 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
<title>Loading...</title>
|
<title>Loading...</title>
|
||||||
<link rel="stylesheet" href="global.css">
|
|
||||||
<link rel="stylesheet" href="/install-usercss/install-usercss.css">
|
<link href="global.css" rel="stylesheet">
|
||||||
<script src="/js/messaging.js"></script>
|
<link href="install-usercss/install-usercss.css" rel="stylesheet">
|
||||||
<script src="/js/prefs.js"></script>
|
|
||||||
<script src="/js/dom.js"></script>
|
<script src="js/messaging.js"></script>
|
||||||
<script src="/js/localization.js"></script>
|
<script src="js/prefs.js"></script>
|
||||||
<script src="/content/apply.js"></script>
|
<script src="js/dom.js"></script>
|
||||||
<script src="/vendor/node-semver/semver.js"></script>
|
<script src="js/localization.js"></script>
|
||||||
<script src="js/script-loader.js"></script>
|
<script src="js/script-loader.js"></script>
|
||||||
|
<script src="content/apply.js"></script>
|
||||||
|
<script src="vendor/node-semver/semver.js"></script>
|
||||||
|
|
||||||
<script src="/msgbox/msgbox.js"></script>
|
<link href="msgbox/msgbox.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="/msgbox/msgbox.css">
|
<script src="msgbox/msgbox.js"></script>
|
||||||
|
|
||||||
<script src="/vendor/codemirror/lib/codemirror.js"></script>
|
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">
|
||||||
<script src="/vendor/codemirror/keymap/sublime.js"></script>
|
<script src="vendor/codemirror/lib/codemirror.js"></script>
|
||||||
<script src="/vendor/codemirror/keymap/emacs.js"></script>
|
<script src="vendor/codemirror/keymap/sublime.js"></script>
|
||||||
<script src="/vendor/codemirror/keymap/vim.js"></script>
|
<script src="vendor/codemirror/keymap/emacs.js"></script>
|
||||||
|
<script src="vendor/codemirror/keymap/vim.js"></script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/vendor/codemirror/lib/codemirror.css">
|
<script src="vendor/codemirror/mode/css/css.js"></script>
|
||||||
<script src="/vendor/codemirror/mode/css/css.js"></script>
|
<script src="vendor/codemirror/addon/search/searchcursor.js"></script>
|
||||||
<link rel="stylesheet" href="/vendor/codemirror/addon/dialog/dialog.css">
|
|
||||||
<link rel="stylesheet" href="/vendor/codemirror/addon/search/matchesonscrollbar.css">
|
|
||||||
<script src="/vendor/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/search/matchesonscrollbar.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/search/match-highlighter.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/dialog/dialog.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/search/searchcursor.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/search/search.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/comment/comment.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/selection/active-line.js"></script>
|
|
||||||
<link rel="stylesheet" href="/vendor/codemirror/addon/fold/foldgutter.css" />
|
|
||||||
<script src="/vendor/codemirror/addon/fold/foldcode.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/fold/foldgutter.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/fold/brace-fold.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/fold/comment-fold.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/edit/matchbrackets.js"></script>
|
|
||||||
<link rel="stylesheet" href="/vendor/codemirror/addon/lint/lint.css" />
|
|
||||||
<link rel="stylesheet" href="/vendor/codemirror/addon/hint/show-hint.css" />
|
|
||||||
<script src="/vendor/codemirror/addon/hint/show-hint.js"></script>
|
|
||||||
<script src="/vendor/codemirror/addon/hint/css-hint.js"></script>
|
|
||||||
|
|
||||||
<script src="/edit/match-highlighter-helper.js"></script>
|
<link href="vendor/codemirror/addon/fold/foldgutter.css" rel="stylesheet" />
|
||||||
<script src="/edit/codemirror-default.js"></script>
|
<script src="vendor/codemirror/addon/fold/foldcode.js"></script>
|
||||||
<link rel="stylesheet" href="/edit/codemirror-default.css">
|
<script src="vendor/codemirror/addon/fold/foldgutter.js"></script>
|
||||||
|
<script src="vendor/codemirror/addon/fold/brace-fold.js"></script>
|
||||||
|
|
||||||
|
<script src="edit/codemirror-default.js"></script>
|
||||||
|
<link rel="stylesheet" href="edit/codemirror-default.css">
|
||||||
</head>
|
</head>
|
||||||
<body id="stylus-install-usercss">
|
<body id="stylus-install-usercss">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -60,6 +47,7 @@
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<h2 class="installed" i18n-text="installButtonInstalled"></h2>
|
<h2 class="installed" i18n-text="installButtonInstalled"></h2>
|
||||||
<button class="install" i18n-text="installButton"></button>
|
<button class="install" i18n-text="installButton"></button>
|
||||||
|
<p id="live-reload-install-hint" i18n-text="liveReloadInstallHint" class="hidden"></p>
|
||||||
<label class="set-update-url">
|
<label class="set-update-url">
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||||
|
@ -91,7 +79,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/install-usercss/install-usercss.js"></script>
|
<script src="install-usercss/install-usercss.js"></script>
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none !important;">
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none !important;">
|
||||||
<symbol id="svg-icon-checked" viewBox="0 0 1000 1000">
|
<symbol id="svg-icon-checked" viewBox="0 0 1000 1000">
|
||||||
|
|
|
@ -156,12 +156,18 @@ h1 small {
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.install:hover {
|
.install:hover:not(:disabled) {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
color: #eee;
|
color: #eee;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.install:disabled {
|
||||||
|
opacity: .25;
|
||||||
|
color: white;
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.install.reinstall:after {
|
.install.reinstall:after {
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
|
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
|
||||||
const params = new URLSearchParams(location.search.replace(/^\?/, ''));
|
const params = new URLSearchParams(location.search.replace(/^\?/, ''));
|
||||||
let liveReload = false;
|
let liveReload = false;
|
||||||
let installed = false;
|
let installed = null;
|
||||||
|
let installedDup = null;
|
||||||
|
|
||||||
const tabId = Number(params.get('tabId'));
|
const tabId = Number(params.get('tabId'));
|
||||||
let tabUrl;
|
let tabUrl;
|
||||||
|
@ -65,7 +66,7 @@
|
||||||
cm.scrollTo(scrollInfo.left, scrollInfo.top);
|
cm.scrollTo(scrollInfo.left, scrollInfo.top);
|
||||||
|
|
||||||
return sendMessage({
|
return sendMessage({
|
||||||
id: installed.id,
|
id: (installed || installedDup).id,
|
||||||
method: 'saveUsercss',
|
method: 'saveUsercss',
|
||||||
reason: 'update',
|
reason: 'update',
|
||||||
sourceCode
|
sourceCode
|
||||||
|
@ -74,19 +75,27 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateMeta(style, dup) {
|
function updateMeta(style, dup = installedDup) {
|
||||||
|
installedDup = dup;
|
||||||
const data = style.usercssData;
|
const data = style.usercssData;
|
||||||
const dupData = dup && dup.usercssData;
|
const dupData = dup && dup.usercssData;
|
||||||
const versionTest = dup && semverCompare(data.version, dupData.version);
|
const versionTest = dup && semverCompare(data.version, dupData.version);
|
||||||
|
|
||||||
// update editor
|
|
||||||
cm.setPreprocessor(data.preprocessor);
|
cm.setPreprocessor(data.preprocessor);
|
||||||
|
|
||||||
// update metas
|
const installButtonLabel = t(
|
||||||
document.title = `${installButtonLabel()} ${data.name}`;
|
installed ? 'installButtonInstalled' :
|
||||||
|
!dup ? 'installButton' :
|
||||||
|
versionTest > 0 ? 'installButtonUpdate' : 'installButtonReinstall'
|
||||||
|
);
|
||||||
|
document.title = `${installButtonLabel} ${data.name}`;
|
||||||
|
|
||||||
$('.install').textContent = installButtonLabel();
|
$('.install').textContent = installButtonLabel;
|
||||||
$('.install').classList.add(installButtonClass());
|
$('.install').classList.add(
|
||||||
|
installed ? 'installed' :
|
||||||
|
!dup ? 'install' :
|
||||||
|
versionTest > 0 ? 'update' :
|
||||||
|
'reinstall');
|
||||||
$('.set-update-url').title = dup && dup.updateUrl && t('installUpdateFrom', dup.updateUrl) || '';
|
$('.set-update-url').title = dup && dup.updateUrl && t('installUpdateFrom', dup.updateUrl) || '';
|
||||||
$('.meta-name').textContent = data.name;
|
$('.meta-name').textContent = data.name;
|
||||||
$('.meta-version').textContent = data.version;
|
$('.meta-version').textContent = data.version;
|
||||||
|
@ -158,20 +167,6 @@
|
||||||
))
|
))
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function installButtonClass() {
|
|
||||||
return installed ? 'installed' :
|
|
||||||
!dup ? 'install' :
|
|
||||||
versionTest > 0 ? 'update' : 'reinstall';
|
|
||||||
}
|
|
||||||
|
|
||||||
function installButtonLabel() {
|
|
||||||
return t(
|
|
||||||
installed ? 'installButtonInstalled' :
|
|
||||||
!dup ? 'installButton' :
|
|
||||||
versionTest > 0 ? 'installButtonUpdate' : 'installButtonReinstall'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError(err) {
|
function showError(err) {
|
||||||
|
@ -213,7 +208,7 @@
|
||||||
cm.setValue(sourceCode);
|
cm.setValue(sourceCode);
|
||||||
cm.refresh();
|
cm.refresh();
|
||||||
API.buildUsercss({sourceCode, checkDup: true})
|
API.buildUsercss({sourceCode, checkDup: true})
|
||||||
.then(r => init(r instanceof Object ? r : deepCopy(r)))
|
.then(init)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
$('.header').classList.add('meta-init-error');
|
$('.header').classList.add('meta-init-error');
|
||||||
showError(err);
|
showError(err);
|
||||||
|
@ -324,9 +319,11 @@
|
||||||
} else {
|
} else {
|
||||||
setLiveReload.addEventListener('change', () => {
|
setLiveReload.addEventListener('change', () => {
|
||||||
liveReload = setLiveReload.checked;
|
liveReload = setLiveReload.checked;
|
||||||
if (installed) {
|
if (installed || installedDup) {
|
||||||
const method = 'liveReload' + (liveReload ? 'Start' : 'Stop');
|
const method = 'liveReload' + (liveReload ? 'Start' : 'Stop');
|
||||||
port.postMessage({method});
|
port.postMessage({method});
|
||||||
|
$('.install').disabled = liveReload;
|
||||||
|
$('#live-reload-install-hint').classList.toggle('hidden', !liveReload);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window.addEventListener('installed', () => {
|
window.addEventListener('installed', () => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user