allow live-reload without reinstalling

This commit is contained in:
tophf 2018-01-04 13:36:27 +03:00
parent c9e60dc19b
commit a339b50e27
5 changed files with 150 additions and 162 deletions

View File

@ -588,6 +588,10 @@
"message": "Live reload",
"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": {
"message": "An error occurred while watching the file",
"description": "The label of live-reload error"

View File

@ -1,7 +1,64 @@
'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) {
return new Promise((resolve, reject) => {
@ -14,104 +71,40 @@ function createSourceLoader() {
});
}
function load() {
return fetchText(location.href).then(newSource => {
source = newSource;
return source;
});
function start() {
timer = timer || setTimeout(check, DELAY);
}
function watch(cb) {
let timer;
const DELAY = 1000;
function start() {
if (timer) {
return;
}
timer = setTimeout(check, DELAY);
}
function stop() {
clearTimeout(timer);
timer = null;
}
function check() {
fetchText(location.href)
.then(newSource => {
if (source !== newSource) {
source = newSource;
return cb(source);
}
})
.catch(error => {
console.log(chrome.i18n.getMessage('liveReloadError', error));
})
.then(() => {
timer = setTimeout(check, DELAY);
});
}
return {start, stop};
function stop() {
clearTimeout(timer);
timer = null;
}
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;
function check() {
fetchText(url)
.then(text => {
if (sourceCode === text) return;
sourceCode = text;
port.postMessage({method: 'sourceCodeChanged', sourceCode});
})
.catch(error => {
console.log(chrome.i18n.getMessage('liveReloadError', error));
})
.then(() => {
timer = null;
start();
});
}
if (!/==userstyle==/i.test(document.body.textContent)) {
return false;
}
return true;
}
if (isUsercss()) {
initUsercssInstall();
}
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);
}
})();

View File

@ -5,50 +5,37 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Loading...</title>
<link rel="stylesheet" href="global.css">
<link rel="stylesheet" href="/install-usercss/install-usercss.css">
<script src="/js/messaging.js"></script>
<script src="/js/prefs.js"></script>
<script src="/js/dom.js"></script>
<script src="/js/localization.js"></script>
<script src="/content/apply.js"></script>
<script src="/vendor/node-semver/semver.js"></script>
<link href="global.css" rel="stylesheet">
<link href="install-usercss/install-usercss.css" rel="stylesheet">
<script src="js/messaging.js"></script>
<script src="js/prefs.js"></script>
<script src="js/dom.js"></script>
<script src="js/localization.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 rel="stylesheet" href="/msgbox/msgbox.css">
<link href="msgbox/msgbox.css" rel="stylesheet">
<script src="msgbox/msgbox.js"></script>
<script src="/vendor/codemirror/lib/codemirror.js"></script>
<script src="/vendor/codemirror/keymap/sublime.js"></script>
<script src="/vendor/codemirror/keymap/emacs.js"></script>
<script src="/vendor/codemirror/keymap/vim.js"></script>
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">
<script src="vendor/codemirror/lib/codemirror.js"></script>
<script src="vendor/codemirror/keymap/sublime.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>
<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="vendor/codemirror/mode/css/css.js"></script>
<script src="vendor/codemirror/addon/search/searchcursor.js"></script>
<script src="/edit/match-highlighter-helper.js"></script>
<script src="/edit/codemirror-default.js"></script>
<link rel="stylesheet" href="/edit/codemirror-default.css">
<link href="vendor/codemirror/addon/fold/foldgutter.css" rel="stylesheet" />
<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="edit/codemirror-default.js"></script>
<link rel="stylesheet" href="edit/codemirror-default.css">
</head>
<body id="stylus-install-usercss">
<div class="container">
@ -60,6 +47,7 @@
<div class="actions">
<h2 class="installed" i18n-text="installButtonInstalled"></h2>
<button class="install" i18n-text="installButton"></button>
<p id="live-reload-install-hint" i18n-text="liveReloadInstallHint" class="hidden"></p>
<label class="set-update-url">
<input type="checkbox">
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
@ -91,7 +79,7 @@
</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;">
<symbol id="svg-icon-checked" viewBox="0 0 1000 1000">

View File

@ -156,12 +156,18 @@ h1 small {
background-position: center center;
}
.install:hover {
.install:hover:not(:disabled) {
filter: brightness(1.1);
color: #eee;
text-shadow: none;
}
.install:disabled {
opacity: .25;
color: white;
cursor: auto;
}
.install.reinstall:after {
background-color: #333;
filter: grayscale(100%);

View File

@ -6,7 +6,8 @@
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
const params = new URLSearchParams(location.search.replace(/^\?/, ''));
let liveReload = false;
let installed = false;
let installed = null;
let installedDup = null;
const tabId = Number(params.get('tabId'));
let tabUrl;
@ -65,7 +66,7 @@
cm.scrollTo(scrollInfo.left, scrollInfo.top);
return sendMessage({
id: installed.id,
id: (installed || installedDup).id,
method: 'saveUsercss',
reason: 'update',
sourceCode
@ -74,19 +75,27 @@
});
}
function updateMeta(style, dup) {
function updateMeta(style, dup = installedDup) {
installedDup = dup;
const data = style.usercssData;
const dupData = dup && dup.usercssData;
const versionTest = dup && semverCompare(data.version, dupData.version);
// update editor
cm.setPreprocessor(data.preprocessor);
// update metas
document.title = `${installButtonLabel()} ${data.name}`;
const installButtonLabel = t(
installed ? 'installButtonInstalled' :
!dup ? 'installButton' :
versionTest > 0 ? 'installButtonUpdate' : 'installButtonReinstall'
);
document.title = `${installButtonLabel} ${data.name}`;
$('.install').textContent = installButtonLabel();
$('.install').classList.add(installButtonClass());
$('.install').textContent = installButtonLabel;
$('.install').classList.add(
installed ? 'installed' :
!dup ? 'install' :
versionTest > 0 ? 'update' :
'reinstall');
$('.set-update-url').title = dup && dup.updateUrl && t('installUpdateFrom', dup.updateUrl) || '';
$('.meta-name').textContent = data.name;
$('.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) {
@ -213,7 +208,7 @@
cm.setValue(sourceCode);
cm.refresh();
API.buildUsercss({sourceCode, checkDup: true})
.then(r => init(r instanceof Object ? r : deepCopy(r)))
.then(init)
.catch(err => {
$('.header').classList.add('meta-init-error');
showError(err);
@ -324,9 +319,11 @@
} else {
setLiveReload.addEventListener('change', () => {
liveReload = setLiveReload.checked;
if (installed) {
if (installed || installedDup) {
const method = 'liveReload' + (liveReload ? 'Start' : 'Stop');
port.postMessage({method});
$('.install').disabled = liveReload;
$('#live-reload-install-hint').classList.toggle('hidden', !liveReload);
}
});
window.addEventListener('installed', () => {