diff --git a/background/style-manager.js b/background/style-manager.js index 35ac165b..3946cd56 100644 --- a/background/style-manager.js +++ b/background/style-manager.js @@ -1,10 +1,16 @@ /* eslint no-eq-null: 0, eqeqeq: [2, "smart"] */ -/* - global createCache db calcStyleDigest normalizeStyleSections db promisify - getStyleWithNoCode msg -*/ +/* global createCache db calcStyleDigest normalizeStyleSections db promisify + getStyleWithNoCode msg */ 'use strict'; +/* +This style manager is a layer between content script and the DB. When a style +is added/updated, it broadcast a message to content script and the content +script would try to fetch the new code. + +The live preview feature relies on `runtime.connect` and `port.onDisconnect` +to cleanup the temporary code. See /edit/live-preview.js. +*/ const styleManager = (() => { const preparing = prepare(); const styles = new Map(); @@ -13,29 +19,7 @@ const styleManager = (() => { const compiledExclusion = createCache(); const BAD_MATCHER = {test: () => false}; - // setup live preview - chrome.runtime.onConnect(port => { - if (port.name !== 'livePreview') { - return; - } - let id; - port.onMessage.addListener(data => { - if (!id) { - id = data.id; - } - const style = styles.get(id); - style.preview = data; - broadcastStyleUpdated(data, 'editPreview'); - }); - port.onDisconnect.addListener(() => { - port = null; - if (id) { - const style = styles.get(id); - style.preview = null; - broadcastStyleUpdated(style.data, 'editPreview'); - } - }); - }); + handleLivePreviewConnections(); return ensurePrepared({ get, @@ -52,6 +36,31 @@ const styleManager = (() => { countStylesByUrl, // used by icon badge }); + function handleLivePreviewConnections() { + chrome.runtime.onConnect(port => { + if (port.name !== 'livePreview') { + return; + } + let id; + port.onMessage.addListener(data => { + if (!id) { + id = data.id; + } + const style = styles.get(id); + style.preview = data; + broadcastStyleUpdated(style.preview, 'editPreview'); + }); + port.onDisconnect.addListener(() => { + port = null; + if (id) { + const style = styles.get(id); + style.preview = null; + broadcastStyleUpdated(style.data, 'editPreview'); + } + }); + }); + } + function get(id) { return styles.get(id).data; } diff --git a/edit/live-preview.js b/edit/live-preview.js index 7b53b52a..8cb3e7d8 100644 --- a/edit/live-preview.js +++ b/edit/live-preview.js @@ -1,16 +1,13 @@ +/* global editors messageBox */ 'use strict'; -function createLivePreview() { +function createLivePreview(preprocess) { let data; let previewer; - let hidden; - let node; - document.addEventListener('DOMContentLoaded', () => { - node = $('#preview-label'); - if (hidden !== undefined) { - node.classList.toggle('hidden', hidden); - } - }, {once: true}); + let enabled = prefs.get('editor.livePreview'); + const label = $('#preview-label'); + const errorContainer = $('#preview-errors'); + prefs.subscribe(['editor.livePreview'], (key, value) => { if (value && data && data.id && data.enabled) { previewer = createPreviewer; @@ -20,23 +17,18 @@ function createLivePreview() { previewer.disconnect(); previewer = null; } + enabled = value; }); return {update, show}; function show(state) { - if (hidden === !state) { - return; - } - hidden = !state; - if (node) { - node.classList.toggle('hidden', hidden); - } + label.classList.toggle('hidden', !state); } function update(_data) { data = _data; if (!previewer) { - if (!data.id || !data.enabled) { + if (!data.id || !data.enabled || !enabled) { return; } previewer = createPreviewer(); @@ -54,7 +46,23 @@ function createLivePreview() { return {update, disconnect}; function update(data) { - port.postMessage(data); + Promise.resolve() + .then(() => preprocess ? preprocess(data) : data) + .then(data => port.postMessage(data)) + .then( + () => errorContainer.classList.add('hidden'), + err => { + if (Array.isArray(err)) { + err = err.join('\n'); + } else if (err && err.index !== undefined) { + // FIXME: this would fail if editors[0].getValue() !== data.sourceCode + const pos = editors[0].posFromIndex(err.index); + err.message = `${pos.line}:${pos.ch} ${err.message || String(err)}`; + } + errorContainer.classList.remove('hidden'); + errorContainer.onclick = () => messageBox.alert(err.message || String(err), 'pre'); + } + ); } function disconnect() { diff --git a/edit/source-editor.js b/edit/source-editor.js index 75bca459..d876d5a4 100644 --- a/edit/source-editor.js +++ b/edit/source-editor.js @@ -37,7 +37,7 @@ function createSourceEditor(style) { editors.push(cm); - const livePreview = createLivePreview(); + const livePreview = createLivePreview(preprocess); livePreview.show(Boolean(style.id)); $('#enabled').onchange = function () { @@ -92,20 +92,24 @@ function createSourceEditor(style) { }); }); - function updateLivePreview() { - if (!style.id) { - return; - } - API.buildUsercss({ - sourceCode: cm.getValue(), + function preprocess(style) { + return API.buildUsercss({ + sourceCode: style.sourceCode, vars: style.usercssData.vars }) .then(newStyle => { delete newStyle.enabled; - livePreview.update(Object.assign({}, style, newStyle)); + return Object.assign(style, newStyle); }); } + function updateLivePreview() { + if (!style.id) { + return; + } + livePreview.update(Object.assign({}, style, {sourceCode: cm.getValue()})); + } + function initAppliesToLineWidget() { const PREF_NAME = 'editor.appliesToLineWidget'; const widget = createAppliesToLineWidget(cm);