extract stuff from edit.js and load on demand
This commit is contained in:
parent
8d639b6140
commit
e74a349a88
141
edit/edit.js
141
edit/edit.js
|
@ -3,19 +3,14 @@
|
||||||
define(require => {
|
define(require => {
|
||||||
const {API, msg} = require('/js/msg');
|
const {API, msg} = require('/js/msg');
|
||||||
const {
|
const {
|
||||||
FIREFOX,
|
|
||||||
closeCurrentTab,
|
closeCurrentTab,
|
||||||
debounce,
|
debounce,
|
||||||
getOwnTab,
|
|
||||||
sessionStore,
|
sessionStore,
|
||||||
} = require('/js/toolbox');
|
} = require('/js/toolbox');
|
||||||
const {
|
const {
|
||||||
$,
|
$,
|
||||||
$$,
|
$$,
|
||||||
$create,
|
$create,
|
||||||
$remove,
|
|
||||||
getEventKeyName,
|
|
||||||
onDOMready,
|
|
||||||
setupLivePrefs,
|
setupLivePrefs,
|
||||||
} = require('/js/dom');
|
} = require('/js/dom');
|
||||||
const t = require('/js/localization');
|
const t = require('/js/localization');
|
||||||
|
@ -26,14 +21,10 @@ define(require => {
|
||||||
const {CodeMirror, initBeautifyButton} = require('./codemirror-factory');
|
const {CodeMirror, initBeautifyButton} = require('./codemirror-factory');
|
||||||
|
|
||||||
let headerHeight;
|
let headerHeight;
|
||||||
let isSimpleWindow;
|
|
||||||
let isWindowed;
|
|
||||||
|
|
||||||
window.on('beforeunload', beforeUnload);
|
window.on('beforeunload', beforeUnload);
|
||||||
msg.onExtension(onRuntimeMessage);
|
msg.onExtension(onRuntimeMessage);
|
||||||
|
|
||||||
lazyInit();
|
|
||||||
|
|
||||||
(async function init() {
|
(async function init() {
|
||||||
await preinit;
|
await preinit;
|
||||||
buildThemeElement();
|
buildThemeElement();
|
||||||
|
@ -205,77 +196,6 @@ define(require => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stuff not needed for the main init so we can let it run at its own tempo */
|
|
||||||
function lazyInit() {
|
|
||||||
let ownTabId;
|
|
||||||
// not using `await` so we don't block the subsequent code
|
|
||||||
getOwnTab().then(patchHistoryBack);
|
|
||||||
// no windows on android
|
|
||||||
if (chrome.windows) {
|
|
||||||
detectWindowedState();
|
|
||||||
chrome.tabs.onAttached.addListener(onAttached);
|
|
||||||
}
|
|
||||||
async function patchHistoryBack(tab) {
|
|
||||||
ownTabId = tab.id;
|
|
||||||
// use browser history back when 'back to manage' is clicked
|
|
||||||
if (sessionStore['manageStylesHistory' + ownTabId] === location.href) {
|
|
||||||
await onDOMready();
|
|
||||||
$('#cancel-button').onclick = event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
history.back();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function detectWindowedState() {
|
|
||||||
isSimpleWindow =
|
|
||||||
(await browser.windows.getCurrent()).type === 'popup';
|
|
||||||
isWindowed = isSimpleWindow || (
|
|
||||||
prefs.get('openEditInWindow') &&
|
|
||||||
history.length === 1 &&
|
|
||||||
(await browser.windows.getAll()).length > 1 &&
|
|
||||||
(await browser.tabs.query({currentWindow: true})).length === 1
|
|
||||||
);
|
|
||||||
if (isSimpleWindow) {
|
|
||||||
await onDOMready();
|
|
||||||
initPopupButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function initPopupButton() {
|
|
||||||
const POPUP_HOTKEY = 'Shift-Ctrl-Alt-S';
|
|
||||||
const btn = $create('img', {
|
|
||||||
id: 'popup-button',
|
|
||||||
title: t('optionsCustomizePopup') + '\n' + POPUP_HOTKEY,
|
|
||||||
onclick: embedPopup,
|
|
||||||
});
|
|
||||||
const onIconsetChanged = (_, val) => {
|
|
||||||
const prefix = `images/icon/${val ? 'light/' : ''}`;
|
|
||||||
btn.srcset = `${prefix}16.png 1x,${prefix}32.png 2x`;
|
|
||||||
};
|
|
||||||
prefs.subscribe('iconset', onIconsetChanged, {runNow: true});
|
|
||||||
document.body.appendChild(btn);
|
|
||||||
window.on('keydown', e => getEventKeyName(e) === POPUP_HOTKEY && embedPopup());
|
|
||||||
CodeMirror.defaults.extraKeys[POPUP_HOTKEY] = 'openStylusPopup'; // adds to keymap help
|
|
||||||
}
|
|
||||||
async function onAttached(tabId, info) {
|
|
||||||
if (tabId !== ownTabId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (info.newPosition !== 0) {
|
|
||||||
prefs.set('openEditInWindow', false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const win = await browser.windows.get(info.newWindowId, {populate: true});
|
|
||||||
// If there's only one tab in this window, it's been dragged to new window
|
|
||||||
const openEditInWindow = win.tabs.length === 1;
|
|
||||||
// FF-only because Chrome retardedly resets the size during dragging
|
|
||||||
if (openEditInWindow && FIREFOX) {
|
|
||||||
chrome.windows.update(info.newWindowId, prefs.get('windowPosition'));
|
|
||||||
}
|
|
||||||
prefs.set('openEditInWindow', openEditInWindow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRuntimeMessage(request) {
|
function onRuntimeMessage(request) {
|
||||||
const {style} = request;
|
const {style} = request;
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
|
@ -322,7 +242,7 @@ define(require => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function canSaveWindowPos() {
|
function canSaveWindowPos() {
|
||||||
return isWindowed &&
|
return editor.isWindowed &&
|
||||||
document.visibilityState === 'visible' &&
|
document.visibilityState === 'visible' &&
|
||||||
prefs.get('openEditInWindow') &&
|
prefs.get('openEditInWindow') &&
|
||||||
!isWindowMaximized();
|
!isWindowMaximized();
|
||||||
|
@ -383,63 +303,4 @@ define(require => {
|
||||||
window.outerHeight < screen.availHeight + 10
|
window.outerHeight < screen.availHeight + 10
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function embedPopup() {
|
|
||||||
const ID = 'popup-iframe';
|
|
||||||
const SEL = '#' + ID;
|
|
||||||
if ($(SEL)) return;
|
|
||||||
const frame = $create('iframe', {
|
|
||||||
id: ID,
|
|
||||||
src: chrome.runtime.getManifest().browser_action.default_popup,
|
|
||||||
height: 600,
|
|
||||||
width: prefs.get('popupWidth'),
|
|
||||||
onload() {
|
|
||||||
frame.onload = null;
|
|
||||||
frame.focus();
|
|
||||||
const pw = frame.contentWindow;
|
|
||||||
const body = pw.document.body;
|
|
||||||
pw.on('keydown', e => getEventKeyName(e) === 'Escape' && embedPopup._close());
|
|
||||||
pw.close = embedPopup._close;
|
|
||||||
if (pw.IntersectionObserver) {
|
|
||||||
let loaded;
|
|
||||||
new pw.IntersectionObserver(([e]) => {
|
|
||||||
const el = pw.document.scrollingElement;
|
|
||||||
const h = e.isIntersecting && !pw.scrollY ? el.offsetHeight : el.scrollHeight;
|
|
||||||
const hasSB = h > el.offsetHeight;
|
|
||||||
const {width} = e.boundingClientRect;
|
|
||||||
frame.height = h;
|
|
||||||
if (!hasSB !== !frame._scrollbarWidth || frame.width - width) {
|
|
||||||
frame._scrollbarWidth = hasSB ? width - el.offsetWidth : 0;
|
|
||||||
frame.width = width + frame._scrollbarWidth;
|
|
||||||
}
|
|
||||||
if (!loaded) {
|
|
||||||
loaded = true;
|
|
||||||
frame.dataset.loaded = '';
|
|
||||||
}
|
|
||||||
}).observe(body.appendChild(
|
|
||||||
$create('div', {style: {height: '1px', marginTop: '-1px'}})
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
frame.dataset.loaded = '';
|
|
||||||
frame.height = body.scrollHeight;
|
|
||||||
}
|
|
||||||
new pw.MutationObserver(() => {
|
|
||||||
const bs = body.style;
|
|
||||||
const w = parseFloat(bs.minWidth || bs.width) + (frame._scrollbarWidth || 0);
|
|
||||||
const h = parseFloat(bs.minHeight || body.offsetHeight);
|
|
||||||
if (frame.width - w) frame.width = w;
|
|
||||||
if (frame.height - h) frame.height = h;
|
|
||||||
}).observe(body, {attributes: true, attributeFilter: ['style']});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// saving the listener here so it's the same function reference for window.off
|
|
||||||
if (!embedPopup._close) {
|
|
||||||
embedPopup._close = () => {
|
|
||||||
$remove(SEL);
|
|
||||||
window.off('mousedown', embedPopup._close);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
window.on('mousedown', embedPopup._close);
|
|
||||||
document.body.appendChild(frame);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,6 +22,8 @@ define(require => {
|
||||||
const editor = {
|
const editor = {
|
||||||
dirty,
|
dirty,
|
||||||
isUsercss: false,
|
isUsercss: false,
|
||||||
|
isWindowed: false,
|
||||||
|
isWindowSimple: false,
|
||||||
/** @type {'customName'|'name'} */
|
/** @type {'customName'|'name'} */
|
||||||
nameTarget: 'name',
|
nameTarget: 'name',
|
||||||
previewDelay: 200, // Chrome devtools uses 200
|
previewDelay: 200, // Chrome devtools uses 200
|
||||||
|
|
114
edit/embedded-popup.js
Normal file
114
edit/embedded-popup.js
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
define(require => {
|
||||||
|
const {
|
||||||
|
$,
|
||||||
|
$create,
|
||||||
|
$remove,
|
||||||
|
getEventKeyName,
|
||||||
|
} = require('/js/dom');
|
||||||
|
const t = require('/js/localization');
|
||||||
|
const prefs = require('/js/prefs');
|
||||||
|
const {CodeMirror} = require('./codemirror-factory');
|
||||||
|
|
||||||
|
const ID = 'popup-iframe';
|
||||||
|
const SEL = '#' + ID;
|
||||||
|
const URL = chrome.runtime.getManifest().browser_action.default_popup;
|
||||||
|
/** @type {HTMLIFrameElement} */
|
||||||
|
let frame;
|
||||||
|
let isLoaded;
|
||||||
|
let scrollbarWidth;
|
||||||
|
|
||||||
|
return {
|
||||||
|
initPopupButton() {
|
||||||
|
const POPUP_HOTKEY = 'Shift-Ctrl-Alt-S';
|
||||||
|
const btn = $create('img', {
|
||||||
|
id: 'popup-button',
|
||||||
|
title: t('optionsCustomizePopup') + '\n' + POPUP_HOTKEY,
|
||||||
|
onclick: embedPopup,
|
||||||
|
});
|
||||||
|
const onIconsetChanged = (_, val) => {
|
||||||
|
const prefix = `images/icon/${val ? 'light/' : ''}`;
|
||||||
|
btn.srcset = `${prefix}16.png 1x,${prefix}32.png 2x`;
|
||||||
|
};
|
||||||
|
prefs.subscribe('iconset', onIconsetChanged, {runNow: true});
|
||||||
|
document.body.appendChild(btn);
|
||||||
|
window.on('keydown', e => getEventKeyName(e) === POPUP_HOTKEY && embedPopup());
|
||||||
|
CodeMirror.defaults.extraKeys[POPUP_HOTKEY] = 'openStylusPopup'; // adds to keymap help
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function embedPopup() {
|
||||||
|
if ($(SEL)) return;
|
||||||
|
isLoaded = false;
|
||||||
|
scrollbarWidth = 0;
|
||||||
|
frame = $create('iframe', {
|
||||||
|
id: ID,
|
||||||
|
src: URL,
|
||||||
|
height: 600,
|
||||||
|
width: prefs.get('popupWidth'),
|
||||||
|
onload: initFrame,
|
||||||
|
});
|
||||||
|
window.on('mousedown', removePopup);
|
||||||
|
document.body.appendChild(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initFrame() {
|
||||||
|
frame = this;
|
||||||
|
frame.focus();
|
||||||
|
const pw = frame.contentWindow;
|
||||||
|
const body = pw.document.body;
|
||||||
|
pw.on('keydown', removePopupOnEsc);
|
||||||
|
pw.close = removePopup;
|
||||||
|
if (pw.IntersectionObserver) {
|
||||||
|
new pw.IntersectionObserver(onIntersect).observe(body.appendChild(
|
||||||
|
$create('div', {style: {height: '1px', marginTop: '-1px'}})
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
frame.dataset.loaded = '';
|
||||||
|
frame.height = body.scrollHeight;
|
||||||
|
}
|
||||||
|
new pw.MutationObserver(onMutation).observe(body, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['style'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMutation() {
|
||||||
|
const body = frame.contentDocument.body;
|
||||||
|
const bs = body.style;
|
||||||
|
const w = parseFloat(bs.minWidth || bs.width) + (scrollbarWidth || 0);
|
||||||
|
const h = parseFloat(bs.minHeight || body.offsetHeight);
|
||||||
|
if (frame.width - w) frame.width = w;
|
||||||
|
if (frame.height - h) frame.height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onIntersect([e]) {
|
||||||
|
const pw = frame.contentWindow;
|
||||||
|
const el = pw.document.scrollingElement;
|
||||||
|
const h = e.isIntersecting && !pw.scrollY ? el.offsetHeight : el.scrollHeight;
|
||||||
|
const hasSB = h > el.offsetHeight;
|
||||||
|
const {width} = e.boundingClientRect;
|
||||||
|
frame.height = h;
|
||||||
|
if (!hasSB !== !scrollbarWidth || frame.width - width) {
|
||||||
|
scrollbarWidth = hasSB ? width - el.offsetWidth : 0;
|
||||||
|
frame.width = width + scrollbarWidth;
|
||||||
|
}
|
||||||
|
if (!isLoaded) {
|
||||||
|
isLoaded = true;
|
||||||
|
frame.dataset.loaded = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePopup() {
|
||||||
|
frame = null;
|
||||||
|
$remove(SEL);
|
||||||
|
window.off('mousedown', removePopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePopupOnEsc(e) {
|
||||||
|
if (getEventKeyName(e) === 'Escape') {
|
||||||
|
removePopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
106
edit/preinit.js
106
edit/preinit.js
|
@ -2,8 +2,14 @@
|
||||||
|
|
||||||
define(require => {
|
define(require => {
|
||||||
const {API} = require('/js/msg');
|
const {API} = require('/js/msg');
|
||||||
const {sessionStore, tryCatch, tryJSONparse} = require('/js/toolbox');
|
const {
|
||||||
const {waitForSelector} = require('/js/dom');
|
FIREFOX,
|
||||||
|
getOwnTab,
|
||||||
|
sessionStore,
|
||||||
|
tryCatch,
|
||||||
|
tryJSONparse,
|
||||||
|
} = require('/js/toolbox');
|
||||||
|
const {$, waitForSelector} = require('/js/dom');
|
||||||
const prefs = require('/js/prefs');
|
const prefs = require('/js/prefs');
|
||||||
const editor = require('./editor');
|
const editor = require('./editor');
|
||||||
const util = require('./util');
|
const util = require('./util');
|
||||||
|
@ -12,9 +18,12 @@ define(require => {
|
||||||
emacs: '/vendor/codemirror/keymap/emacs',
|
emacs: '/vendor/codemirror/keymap/emacs',
|
||||||
vim: '/vendor/codemirror/keymap/vim',
|
vim: '/vendor/codemirror/keymap/vim',
|
||||||
};
|
};
|
||||||
|
const domReady = waitForSelector('#sections');
|
||||||
|
let ownTabId;
|
||||||
|
|
||||||
// resize the window on 'undo close'
|
// resize the window on 'undo close'
|
||||||
if (chrome.windows) {
|
if (chrome.windows) {
|
||||||
|
initWindowedMode();
|
||||||
const pos = tryJSONparse(sessionStore.windowPos);
|
const pos = tryJSONparse(sessionStore.windowPos);
|
||||||
delete sessionStore.windowPos;
|
delete sessionStore.windowPos;
|
||||||
if (pos && pos.left != null && chrome.windows) {
|
if (pos && pos.left != null && chrome.windows) {
|
||||||
|
@ -22,27 +31,35 @@ define(require => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function preinit() {
|
getOwnTab().then(async tab => {
|
||||||
const params = new URLSearchParams(location.search);
|
ownTabId = tab.id;
|
||||||
const id = Number(params.get('id'));
|
// use browser history back when 'back to manage' is clicked
|
||||||
const style = id && await API.styles.get(id) || {
|
if (sessionStore['manageStylesHistory' + ownTabId] === location.href) {
|
||||||
name: params.get('domain') ||
|
await domReady;
|
||||||
tryCatch(() => new URL(params.get('url-prefix')).hostname) ||
|
$('#cancel-button').onclick = event => {
|
||||||
'',
|
event.stopPropagation();
|
||||||
enabled: true,
|
event.preventDefault();
|
||||||
sections: [
|
history.back();
|
||||||
util.DocFuncMapper.toSection([...params], {code: ''}),
|
};
|
||||||
],
|
}
|
||||||
};
|
});
|
||||||
// switching the mode here to show the correct page ASAP, usually before DOMContentLoaded
|
|
||||||
editor.isUsercss = Boolean(style.usercssData || !style.id && prefs.get('newStyleAsUsercss'));
|
async function initWindowedMode() {
|
||||||
editor.lazyKeymaps = lazyKeymaps;
|
chrome.tabs.onAttached.addListener(onTabAttached);
|
||||||
editor.style = style;
|
editor.isWindowSimple =
|
||||||
editor.updateTitle(false);
|
(await browser.windows.getCurrent()).type === 'popup';
|
||||||
document.documentElement.classList.toggle('usercss', editor.isUsercss);
|
if (editor.isWindowSimple) {
|
||||||
sessionStore.justEditedStyleId = style.id || '';
|
Promise.all([
|
||||||
// no such style so let's clear the invalid URL parameters
|
require(['./embedded-popup']),
|
||||||
if (!style.id) history.replaceState({}, '', location.pathname);
|
domReady,
|
||||||
|
]).then(([_]) => _.initPopupButton());
|
||||||
|
}
|
||||||
|
editor.isWindowed = editor.isWindowSimple || (
|
||||||
|
history.length === 1 &&
|
||||||
|
await prefs.initializing && prefs.get('openEditInWindow') &&
|
||||||
|
(await browser.windows.getAll()).length > 1 &&
|
||||||
|
(await browser.tabs.query({currentWindow: true})).length === 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Preloads the theme so CodeMirror can use the correct metrics in its first render */
|
/** Preloads the theme so CodeMirror can use the correct metrics in its first render */
|
||||||
|
@ -70,6 +87,47 @@ define(require => {
|
||||||
/vim/i.test(km) && require([lazyKeymaps.vim]);
|
/vim/i.test(km) && require([lazyKeymaps.vim]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onTabAttached(tabId, info) {
|
||||||
|
if (tabId !== ownTabId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (info.newPosition !== 0) {
|
||||||
|
prefs.set('openEditInWindow', false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const win = await browser.windows.get(info.newWindowId, {populate: true});
|
||||||
|
// If there's only one tab in this window, it's been dragged to new window
|
||||||
|
const openEditInWindow = win.tabs.length === 1;
|
||||||
|
// FF-only because Chrome retardedly resets the size during dragging
|
||||||
|
if (openEditInWindow && FIREFOX) {
|
||||||
|
chrome.windows.update(info.newWindowId, prefs.get('windowPosition'));
|
||||||
|
}
|
||||||
|
prefs.set('openEditInWindow', openEditInWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function preinit() {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const id = Number(params.get('id'));
|
||||||
|
const style = id && await API.styles.get(id) || {
|
||||||
|
name: params.get('domain') ||
|
||||||
|
tryCatch(() => new URL(params.get('url-prefix')).hostname) ||
|
||||||
|
'',
|
||||||
|
enabled: true,
|
||||||
|
sections: [
|
||||||
|
util.DocFuncMapper.toSection([...params], {code: ''}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
// switching the mode here to show the correct page ASAP, usually before DOMContentLoaded
|
||||||
|
editor.isUsercss = Boolean(style.usercssData || !style.id && prefs.get('newStyleAsUsercss'));
|
||||||
|
editor.lazyKeymaps = lazyKeymaps;
|
||||||
|
editor.style = style;
|
||||||
|
editor.updateTitle(false);
|
||||||
|
document.documentElement.classList.toggle('usercss', editor.isUsercss);
|
||||||
|
sessionStore.justEditedStyleId = style.id || '';
|
||||||
|
// no such style so let's clear the invalid URL parameters
|
||||||
|
if (!style.id) history.replaceState({}, '', location.pathname);
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
preinit(),
|
preinit(),
|
||||||
prefs.initializing.then(() =>
|
prefs.initializing.then(() =>
|
||||||
|
@ -77,6 +135,6 @@ define(require => {
|
||||||
loadTheme(),
|
loadTheme(),
|
||||||
loadKeymaps(),
|
loadKeymaps(),
|
||||||
])),
|
])),
|
||||||
waitForSelector('#sections'),
|
domReady,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user