Add: a draggable list to customize injection order (#1364)

+ implement messageBox.close()
+ fix require() with root urls in /dir/page.html
+ limit messageBox focus shift to config-dialog
+ flatten vendor dirs and simplify build-vendor:
  + replace the unicode symbol with ASCII `->`
  + flatten dirs by default to simplify writing the rules and improve their readability
  + rename and sort functions in the order they run
  + use `node-fetch` instead of the gargantuan `make-fetch-happen`
  + use `glob` which is already installed by other deps

Co-authored-by: tophf <tophf@gmx.com>
This commit is contained in:
eight 2022-01-14 20:44:48 +08:00 committed by GitHub
parent 8ddeef221b
commit ddc09f3511
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1303 additions and 1521 deletions

View File

@ -1667,6 +1667,14 @@
"styleIncludeLabel": {
"message": "Custom included sites"
},
"styleInjectionOrder": {
"message": "Style injection order",
"description": "Tooltip for the button in the manager to open the dialog and also the title of this dialog"
},
"styleInjectionOrderHint": {
"message": "Drag'n'drop a style to change its position. Styles are injected sequentially in the order shown below so a style further down the list can override the earlier styles.",
"description": "Hint in the injection order dialog in the manager"
},
"styleExcludeLabel": {
"message": "Custom excluded sites"
},

View File

@ -59,6 +59,7 @@ const styleMan = (() => {
const DELETE_IF_NULL = ['id', 'customName', 'md5Url', 'originalMd5'];
/** @type {Promise|boolean} will be `true` to avoid wasting a microtask tick on each `await` */
let ready = init();
let order = {};
chrome.runtime.onConnect.addListener(handleLivePreview);
// function handleColorScheme() {
@ -70,6 +71,17 @@ const styleMan = (() => {
}
});
prefs.subscribe(['injectionOrder'], (key, value) => {
order = {};
value.forEach((uid, i) => {
const id = uuidIndex.get(uid);
if (id) {
order[id] = i;
}
});
msg.broadcast({method: 'styleSort', order});
});
//#endregion
//#region Exports
@ -148,7 +160,11 @@ const styleMan = (() => {
async getSectionsByUrl(url, id, isInitialApply) {
if (ready.then) await ready;
if (isInitialApply && prefs.get('disableAll')) {
return {disableAll: true};
return {
cfg: {
disableAll: true,
},
};
}
const sender = CHROME && this && this.sender || {};
if (sender.frameId === 0) {
@ -170,7 +186,7 @@ const styleMan = (() => {
}
return id
? cache.sections[id] ? {[id]: cache.sections[id]} : {}
: cache.sections;
: Object.assign({cfg: {order}}, cache.sections);
},
/** @returns {Promise<StyleObj>} */

View File

@ -56,6 +56,7 @@
return NOP;
}
return API.styles.getSectionsByUrl(url, id).then(sections => {
delete sections.cfg;
const tasks = [];
for (const section of Object.values(sections)) {
const styleId = section.id;

View File

@ -13,11 +13,19 @@
let hasStyles = false;
let isDisabled = false;
let isTab = !chrome.tabs || location.pathname !== '/popup.html';
let order = {};
const isFrame = window !== parent;
const isFrameAboutBlank = isFrame && location.href === 'about:blank';
const isUnstylable = !chrome.app && document instanceof XMLDocument;
const styleInjector = StyleInjector({
compare: (a, b) => a.id - b.id,
compare: (a, b) => {
const ia = order[a.id];
const ib = order[b.id];
if (ia === ib) return 0;
if (ia == null) return 1;
if (ib == null) return -1;
return ia - ib;
},
onUpdate: onInjectorUpdate,
});
// dynamic iframes don't have a URL yet so we'll use their parent's URL (hash isn't inherited)
@ -100,7 +108,11 @@
parentStyles && await new Promise(onFrameElementInView) && parentStyles ||
!isFrameAboutBlank && chrome.app && !chrome.tabs && tryCatch(getStylesViaXhr) ||
await API.styles.getSectionsByUrl(matchUrl, null, true);
isDisabled = styles.disableAll;
if (styles.cfg) {
isDisabled = styles.cfg.disableAll;
order = styles.cfg.order || {};
delete styles.cfg;
}
hasStyles = !isDisabled;
if (hasStyles) {
window[SYM] = styles;
@ -166,10 +178,16 @@
}
break;
case 'styleSort':
order = request.order;
styleInjector.sort();
break;
case 'urlChanged':
if (!hasStyles && isDisabled || matchUrl === request.url) break;
matchUrl = request.url;
API.styles.getSectionsByUrl(matchUrl).then(sections => {
delete sections.cfg;
hasStyles = true;
styleInjector.replace(sections);
});

View File

@ -78,6 +78,8 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
_addRemoveElements(enable);
if (enable) _toggleObservers(true);
},
sort: _sort,
};
function _add(style) {

View File

@ -0,0 +1,58 @@
#message-box.injection-order > div {
height: 100%;
max-width: 80vw;
}
#message-box.injection-order #message-box-contents {
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
--border: 1px solid rgba(128, 128, 128, .25);
}
.injection-order header {
padding: 1rem;
width: 0;
min-width: 100%;
box-sizing: border-box;
}
.injection-order ol {
height: 100%;
padding: 1px 0 0; /* 1px for keyboard-focused element's outline */
margin: 0;
font-size: 14px;
overflow-y: auto;
border-top: var(--border);
}
.injection-order a {
display: block;
color: #000;
text-decoration: none;
transition: transform .25s ease-in-out;
z-index: 1;
user-select: none;
padding: 0.3em .5em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: move;
}
.injection-order a.enabled {
font-weight: bold;
}
.injection-order a:hover {
text-decoration: underline;
}
.injection-order a:not(:first-child) {
border-top: var(--border);
}
.injection-order a::before {
content: "\2261";
padding: 0.6em;
font-weight: normal;
}
.injection-order .draggable-list-target {
position: relative;
background: lightcyan;
transition: none;
z-index: 100;
}

View File

@ -0,0 +1,79 @@
/* global $create messageBoxProxy */// dom.js
/* global API */// msg.js
/* global DraggableList */
/* global prefs */
/* global t */// localization.js
'use strict';
/* exported InjectionOrder */
async function InjectionOrder(show = true) {
if (!show) {
return messageBoxProxy.close();
}
const entries = (await getOrderedStyles()).map(makeEntry);
const ol = $create('ol');
let maxTranslateY;
ol.append(...entries.map(l => l.el));
ol.on('d:dragstart', ({detail: d}) => {
d.origin.dataTransfer.setDragImage(new Image(), 0, 0);
maxTranslateY = ol.scrollHeight + ol.offsetTop - d.dragTarget.offsetHeight - d.dragTarget.offsetTop;
});
ol.on('d:dragmove', ({detail: d}) => {
d.origin.stopPropagation(); // preserves dropEffect
d.origin.dataTransfer.dropEffect = 'move';
const y = Math.min(d.currentPos.y - d.startPos.y, maxTranslateY);
d.dragTarget.style.transform = `translateY(${y}px)`;
});
ol.on('d:dragend', ({detail: d}) => {
const [item] = entries.splice(d.originalIndex, 1);
entries.splice(d.spliceIndex, 0, item);
ol.insertBefore(d.dragTarget, d.insertBefore);
prefs.set('injectionOrder', entries.map(l => l.style._id));
});
DraggableList(ol, {scrollContainer: ol});
await messageBoxProxy.show({
title: t('styleInjectionOrder'),
contents: $create('fragment', [
$create('header', t('styleInjectionOrderHint')),
ol,
]),
className: 'injection-order center-dialog',
blockScroll: true,
buttons: [t('confirmClose')],
});
async function getOrderedStyles() {
const [styles] = await Promise.all([
API.styles.getAll(),
prefs.ready,
]);
const styleSet = new Set(styles);
const uuidIndex = new Map();
for (const s of styleSet) {
uuidIndex.set(s._id, s);
}
const orderedStyles = [];
for (const uid of prefs.get('injectionOrder')) {
const s = uuidIndex.get(uid);
if (s) {
uuidIndex.delete(uid);
orderedStyles.push(s);
styleSet.delete(s);
}
}
orderedStyles.push(...styleSet);
return orderedStyles;
}
function makeEntry(style) {
return {
style,
el: $create('a', {
className: style.enabled ? 'enabled' : '',
href: '/edit.html?id=' + style.id,
target: '_blank',
}, style.name),
};
}
}

View File

@ -12,6 +12,19 @@ window.messageBox = {
_resolve: null,
};
messageBox.close = async isAnimated => {
window.off('keydown', messageBox.listeners.key, true);
window.off('scroll', messageBox.listeners.scroll);
window.off('mouseup', messageBox.listeners.mouseUp);
window.off('mousemove', messageBox.listeners.mouseMove);
if (isAnimated) {
await animateElement(messageBox.element, 'fadeout');
}
messageBox.element.remove();
messageBox.element = null;
messageBox._resolve = null;
};
/**
* @param {Object} params
* @param {String} params.title
@ -46,7 +59,8 @@ messageBox.show = async ({
messageBox._originalFocus = document.activeElement;
// focus the first focusable child but skip the first external link which is usually `feedback`
if ((moveFocus(messageBox.element, 0) || {}).target === '_blank') {
if ((moveFocus(messageBox.element, 0) || {}).target === '_blank' &&
/config-dialog/.test(className)) {
moveFocus(messageBox.element, 1);
}
// suppress focus outline when invoked via click
@ -143,9 +157,7 @@ messageBox.show = async ({
function resolveWith(value) {
setTimeout(messageBox._resolve, 0, value);
unbindGlobalListeners();
animateElement(messageBox.element, 'fadeout')
.then(removeSelf);
messageBox.close(true);
if (messageBox.element.contains(document.activeElement)) {
messageBox._originalFocus.focus();
}
@ -153,8 +165,7 @@ messageBox.show = async ({
function createElement() {
if (messageBox.element) {
unbindGlobalListeners();
removeSelf();
messageBox.close();
}
const id = 'message-box';
messageBox.element =
@ -186,19 +197,6 @@ messageBox.show = async ({
}
window.on('keydown', messageBox.listeners.key, true);
}
function unbindGlobalListeners() {
window.off('keydown', messageBox.listeners.key, true);
window.off('scroll', messageBox.listeners.scroll);
window.off('mouseup', messageBox.listeners.mouseUp);
window.off('mousemove', messageBox.listeners.mouseMove);
}
function removeSelf() {
messageBox.element.remove();
messageBox.element = null;
messageBox._resolve = null;
}
};
/**

View File

@ -15,7 +15,7 @@
window.on('click', showTooltipNote);
window.on('resize', () => debounce(addTooltipsToEllipsized, 100));
// Removing transition-suppressor rule
const {sheet} = $('link[href^="global.css"]');
const {sheet} = $('link[href$="global.css"]');
for (let i = 0, rule; (rule = sheet.cssRules[i]); i++) {
if (/#\\1\s?transition-suppressor/.test(rule.selectorText)) {
sheet.deleteRule(i);

View File

@ -86,7 +86,7 @@
const tag = isCss ? 'link' : 'script';
const attr = isCss ? 'href' : 'src';
if (!isCss && !url.endsWith('.js')) url += '.js';
if (url.startsWith('/')) url = url.slice(1);
if (url[0] === '/' && location.pathname.indexOf('/', 1) < 0) url = url.slice(1);
let el = document.head.querySelector(`${tag}[${attr}$="${url}"]`);
if (!el) {
el = document.createElement(tag);

View File

@ -17,6 +17,7 @@
* @namespace PrefsValues
*/
const defaults = {
// TODO: sort everything aphabetically
'openEditInWindow': false, // new editor opens in a own browser window
'openEditInWindow.popup': false, // new editor opens in a simplified browser window without omnibox
'windowPosition': {}, // detached window position
@ -132,6 +133,8 @@
'popupWidth': 246, // popup width in pixels
'updateInterval': 24, // user-style automatic update interval, hours (0 = disable)
'injectionOrder': [],
};
const knownKeys = Object.keys(defaults);
/** @type {PrefsValues} */

View File

@ -313,8 +313,8 @@
</a>
</label>
</div>
<button id="manage-options-button" i18n-text="openOptions"></button>
<button id="injection-order-button" i18n-title="styleInjectionOrder">↑↓</button>
</details>
</div>

View File

@ -1165,3 +1165,4 @@ a:hover {
margin-left: -2px;
}
}

View File

@ -61,6 +61,7 @@ newUI.renderClass();
installed.on('mouseover', Events.lazyAddEntryTitle, {passive: true});
installed.on('mouseout', Events.lazyAddEntryTitle, {passive: true});
$('#manage-options-button').onclick = () => router.updateHash('#stylus-options');
$('#injection-order-button').onclick = () => router.updateHash('#injection-order');
$('#sync-styles').onclick = () => router.updateHash('#stylus-options');
$$('#header a[href^="http"]').forEach(a => (a.onclick = Events.external));
document.on('visibilitychange', handleVisibilityChange);
@ -101,6 +102,7 @@ newUI.renderClass();
msg.onExtension(onRuntimeMessage);
window.on('closeOptions', () => router.updateHash(''));
router.watch({hash: '#stylus-options'}, toggleEmbeddedOptions);
router.watch({hash: '#injection-order'}, toggleInjectionOrder);
function onRuntimeMessage(msg) {
switch (msg.method) {
@ -137,3 +139,18 @@ async function toggleEmbeddedOptions(state) {
el.remove();
}
}
async function toggleInjectionOrder(state) {
const shown = $('.injection-order');
if (state && !shown) {
await require([
'/vendor/draggable-list/draggable-list.iife.min.js',
'/injection-order/injection-order.css',
'/injection-order/injection-order', /* global InjectionOrder */
]);
await InjectionOrder();
router.updateHash('');
} else if (!state && shown) {
await InjectionOrder(false);
}
}

2113
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
"codemirror": "WARNING! Always use an exact version and test it for a while before releasing"
},
"dependencies": {
"@eight04/draggable-list": "^0.3.0",
"codemirror": "5.63.3",
"db-to-cloud": "^0.7.0",
"jsonlint": "^1.6.3",
@ -21,12 +22,11 @@
},
"devDependencies": {
"archiver": "^4.0.1",
"endent": "^1.4.0",
"eslint": "^7.20.0",
"fs-extra": "^9.0.0",
"make-fetch-happen": "^8.0.7",
"glob": "^7.2.0",
"node-fetch": "^2.6.6",
"sync-version": "^1.0.1",
"tiny-glob": "^0.2.6",
"web-ext": "^6.5.0"
},
"scripts": {

View File

@ -1,14 +1,16 @@
/* eslint-env node */
'use strict';
const fetch = require('node-fetch');
const fs = require('fs');
const fse = require('fs-extra');
const glob = require('glob').sync;
const path = require('path');
const endent = require('endent');
const fetch = require('make-fetch-happen');
const fse = require('fs-extra');
const glob = require('tiny-glob');
const KEEP_DIRECTORIES = null;
const files = {
'codemirror': [
KEEP_DIRECTORIES,
'addon/comment/comment.js',
'addon/dialog',
'addon/edit/closebrackets.js',
@ -35,30 +37,33 @@ const files = {
'theme/*',
],
'jsonlint': [
'lib/jsonlint.js → jsonlint.js',
'README.md LICENSE',
'lib/jsonlint.js',
'README.md -> LICENSE',
],
'less-bundle': [
'dist/less.min.js → less.min.js',
'dist/less.min.js',
],
'lz-string-unsafe': [
'lz-string-unsafe.min.js',
],
'stylelint-bundle': [
'dist/stylelint-bundle.min.js → stylelint-bundle.min.js',
'https://github.com/stylelint/stylelint/raw/{VERSION}/LICENSE → LICENSE',
'dist/stylelint-bundle.min.js',
'https://github.com/stylelint/stylelint/raw/{VERSION}/LICENSE',
],
'stylus-lang-bundle': [
'dist/stylus-renderer.min.js → stylus-renderer.min.js',
'dist/stylus-renderer.min.js',
],
'usercss-meta': [
'dist/usercss-meta.min.js → usercss-meta.min.js',
'dist/usercss-meta.min.js',
],
'db-to-cloud': [
'dist/db-to-cloud.min.js → db-to-cloud.min.js',
'dist/db-to-cloud.min.js',
],
'webext-launch-web-auth-flow': [
'dist/webext-launch-web-auth-flow.min.js → webext-launch-web-auth-flow.min.js',
'dist/webext-launch-web-auth-flow.min.js',
],
'@eight04/draggable-list': [
'dist/draggable-list.iife.min.js',
],
};
@ -66,96 +71,97 @@ main().catch(console.error);
async function main() {
fse.emptyDirSync('vendor');
for (const pkg in files) {
console.log('\x1b[32m%s\x1b[0m', `Building ${pkg}...`);
// other files
const [fetched, copied] = await buildFiles(pkg, files[pkg]);
// README
await fse.outputFile(`vendor/${pkg}/README.md`, generateReadme(pkg, fetched, copied));
// LICENSE
await copyLicense(pkg);
}
console.log('\x1b[32m%s\x1b[0m', 'updating codemirror themes list...');
await fse.outputFile('edit/codemirror-themes.js', await generateThemeList());
await Promise.all(Object.keys(files).map(async pkg => {
console.log(`Building ${pkg}...`);
const pkgName = getFileName(pkg);
const flatPkg = pkg === pkgName || files[pkgName]
? pkg.replace(/\//g, '-')
: pkgName;
const res = await buildFiles(pkg, flatPkg, files[pkg]);
buildLicense(pkg, flatPkg);
buildReadme(pkg, flatPkg, res);
}));
console.log('Building CodeMirror theme list...');
buildThemeList();
}
async function generateThemeList() {
const themes = (await fse.readdir('vendor/codemirror/theme'))
.filter(name => name.endsWith('.css'))
.map(name => name.replace('.css', ''))
.sort();
return endent`
async function buildFiles(pkg, flatPkg, patterns) {
const keepDirs = patterns.includes(KEEP_DIRECTORIES);
let fetched = '';
let copied = '';
for (let pattern of patterns) {
if (pattern === KEEP_DIRECTORIES) continue;
pattern = pattern.replace('{VERSION}', require(`${pkg}/package.json`).version);
const [src, dest = !keepDirs && getFileName(src)] = pattern.split(/\s*->\s*/);
if (/^https?:/.test(src)) {
fse.outputFileSync(`vendor/${flatPkg}/${dest}`, await (await fetch(src)).text());
fetched += `* ${dest}: ${src}\n`;
} else {
const files = glob(`node_modules/${pkg}/${src}`);
if (!files.length) {
throw new Error(`Pattern ${src} matches no files`);
}
for (const file of files) {
fse.copySync(file, dest
? `vendor/${flatPkg}/${dest}`
: `vendor/${path.relative('node_modules', file).replace(pkg + '/', flatPkg + '/')}`);
const relSrc = path.relative(`node_modules/${pkg}`, file).replace(/\\/g, '/');
copied += dest && dest !== relSrc
? `* ${dest}: ${
keepDirs || getFileName(dest) !== getFileName(relSrc)
? relSrc
: relSrc.replace(/[^/]+$/, '*')
}\n`
: `* ${relSrc}\n`;
}
}
}
return {fetched, copied};
}
function buildLicense(pkg, flatPkg) {
const LICENSE = `vendor/${flatPkg}/LICENSE`;
if (!fs.existsSync(LICENSE)) {
const [src] = glob(`node_modules/${pkg}/LICEN[SC]E*`);
if (!src) throw new Error(`Cannot find license file for ${pkg}`);
fse.copySync(src, LICENSE);
}
}
function buildReadme(pkg, flatPkg, {fetched, copied}) {
const {name, version} = require(`${pkg}/package.json`);
fse.outputFileSync(`vendor/${flatPkg}/README.md`, [
`## ${name} v${version}`,
fetched && `Files downloaded from URL:\n${fetched}`,
copied && `Files copied from NPM (node_modules):\n${copied}`,
].filter(Boolean).join('\n\n'));
}
function buildThemeList() {
fse.outputFileSync('edit/codemirror-themes.js', deindent(`\
/* Do not edit. This file is auto-generated by build-vendor.js */
'use strict';
/* exported CODEMIRROR_THEMES */
const CODEMIRROR_THEMES = [
${
themes.map(t => ` '${t.replace(/'/g, '\\$&')}',\n`).join('')
}];
` + '\n';
}
async function copyLicense(pkg) {
try {
await fse.access(`vendor/${pkg}/LICENSE`);
return;
} catch (err) {
// pass
}
for (const file of await glob(`node_modules/${pkg}/LICEN[SC]E*`)) {
await fse.copy(file, `vendor/${pkg}/LICENSE`);
return;
}
throw new Error(`cannot find license file for ${pkg}`);
}
async function buildFiles(pkg, patterns) {
const fetchedFiles = [];
const copiedFiles = [];
for (let pattern of patterns) {
pattern = pattern.replace('{VERSION}', require(`${pkg}/package.json`).version);
const [src, dest] = pattern.split(/\s*→\s*/);
if (src.startsWith('http')) {
const content = await (await fetch(src)).text();
await fse.outputFile(`vendor/${pkg}/${dest}`, content);
fetchedFiles.push([src, dest]);
} else {
let dirty = false;
for (const file of await glob(`node_modules/${pkg}/${src}`)) {
if (dest) {
await fse.copy(file, `vendor/${pkg}/${dest}`);
} else {
await fse.copy(file, path.join('vendor', path.relative('node_modules', file)));
}
copiedFiles.push([path.relative(`node_modules/${pkg}`, file), dest]);
dirty = true;
}
if (!dirty) {
throw new Error(`Pattern ${src} matches no files`);
}
fs.readdirSync('vendor/codemirror/theme')
.filter(name => name.endsWith('.css'))
.map(name => name.replace('.css', ''))
.sort()
.map(t => ` '${t.replace(/'/g, '\\$&')}',`).join('\n')
}
}
return [fetchedFiles, copiedFiles];
];
`));
}
function generateReadme(lib, fetched, copied) {
const pkg = require(`${lib}/package.json`);
let txt = `## ${pkg.name} v${pkg.version}\n\n`;
if (fetched.length) {
txt += `Following files are downloaded from HTTP:\n\n${generateList(fetched)}\n\n`;
}
if (copied.length) {
txt += `Following files are copied from npm (node_modules):\n\n${generateList(copied)}\n`;
}
return txt;
function deindent(str) {
const indent = str.match(/^\s*/)[0];
return indent
? str.replace(new RegExp('^' + indent, 'gm'), '')
: str;
}
function generateList(list) {
return list.map(([src, dest]) => {
if (dest) {
return `* ${dest}: ${src}`;
}
return `* ${src}`;
}).join('\n');
function getFileName(path) {
return path.split('/').pop();
}

View File

@ -1,99 +1,98 @@
## codemirror v5.63.3
Following files are copied from npm (node_modules):
* addon\comment\comment.js
* addon\dialog
* addon\edit\closebrackets.js
* addon\edit\matchbrackets.js
* addon\fold\brace-fold.js
* addon\fold\comment-fold.js
* addon\fold\foldcode.js
* addon\fold\foldgutter.css
* addon\fold\foldgutter.js
* addon\fold\indent-fold.js
* addon\hint\css-hint.js
* addon\hint\show-hint.css
* addon\hint\show-hint.js
* addon\lint\css-lint.js
* addon\lint\json-lint.js
* addon\lint\lint.css
* addon\lint\lint.js
* addon\scroll\annotatescrollbar.js
* addon\search\matchesonscrollbar.css
* addon\search\matchesonscrollbar.js
* addon\search\searchcursor.js
* addon\selection\active-line.js
* keymap\emacs.js
* keymap\sublime.js
* keymap\vim.js
* lib\codemirror.css
* lib\codemirror.js
* mode\css
* mode\javascript
* mode\stylus
* theme\3024-day.css
* theme\3024-night.css
* theme\abbott.css
* theme\abcdef.css
* theme\ambiance-mobile.css
* theme\ambiance.css
* theme\ayu-dark.css
* theme\ayu-mirage.css
* theme\base16-dark.css
* theme\base16-light.css
* theme\bespin.css
* theme\blackboard.css
* theme\cobalt.css
* theme\colorforth.css
* theme\darcula.css
* theme\dracula.css
* theme\duotone-dark.css
* theme\duotone-light.css
* theme\eclipse.css
* theme\elegant.css
* theme\erlang-dark.css
* theme\gruvbox-dark.css
* theme\hopscotch.css
* theme\icecoder.css
* theme\idea.css
* theme\isotope.css
* theme\juejin.css
* theme\lesser-dark.css
* theme\liquibyte.css
* theme\lucario.css
* theme\material-darker.css
* theme\material-ocean.css
* theme\material-palenight.css
* theme\material.css
* theme\mbo.css
* theme\mdn-like.css
* theme\midnight.css
* theme\monokai.css
* theme\moxer.css
* theme\neat.css
* theme\neo.css
* theme\night.css
* theme\nord.css
* theme\oceanic-next.css
* theme\panda-syntax.css
* theme\paraiso-dark.css
* theme\paraiso-light.css
* theme\pastel-on-dark.css
* theme\railscasts.css
* theme\rubyblue.css
* theme\seti.css
* theme\shadowfox.css
* theme\solarized.css
* theme\ssms.css
* theme\the-matrix.css
* theme\tomorrow-night-bright.css
* theme\tomorrow-night-eighties.css
* theme\ttcn.css
* theme\twilight.css
* theme\vibrant-ink.css
* theme\xq-dark.css
* theme\xq-light.css
* theme\yeti.css
* theme\yonce.css
* theme\zenburn.css
Files copied from NPM (node_modules):
* addon/comment/comment.js
* addon/dialog
* addon/edit/closebrackets.js
* addon/edit/matchbrackets.js
* addon/fold/brace-fold.js
* addon/fold/comment-fold.js
* addon/fold/foldcode.js
* addon/fold/foldgutter.css
* addon/fold/foldgutter.js
* addon/fold/indent-fold.js
* addon/hint/css-hint.js
* addon/hint/show-hint.css
* addon/hint/show-hint.js
* addon/lint/css-lint.js
* addon/lint/json-lint.js
* addon/lint/lint.css
* addon/lint/lint.js
* addon/scroll/annotatescrollbar.js
* addon/search/matchesonscrollbar.css
* addon/search/matchesonscrollbar.js
* addon/search/searchcursor.js
* addon/selection/active-line.js
* keymap/emacs.js
* keymap/sublime.js
* keymap/vim.js
* lib/codemirror.css
* lib/codemirror.js
* mode/css
* mode/javascript
* mode/stylus
* theme/3024-day.css
* theme/3024-night.css
* theme/abbott.css
* theme/abcdef.css
* theme/ambiance-mobile.css
* theme/ambiance.css
* theme/ayu-dark.css
* theme/ayu-mirage.css
* theme/base16-dark.css
* theme/base16-light.css
* theme/bespin.css
* theme/blackboard.css
* theme/cobalt.css
* theme/colorforth.css
* theme/darcula.css
* theme/dracula.css
* theme/duotone-dark.css
* theme/duotone-light.css
* theme/eclipse.css
* theme/elegant.css
* theme/erlang-dark.css
* theme/gruvbox-dark.css
* theme/hopscotch.css
* theme/icecoder.css
* theme/idea.css
* theme/isotope.css
* theme/juejin.css
* theme/lesser-dark.css
* theme/liquibyte.css
* theme/lucario.css
* theme/material-darker.css
* theme/material-ocean.css
* theme/material-palenight.css
* theme/material.css
* theme/mbo.css
* theme/mdn-like.css
* theme/midnight.css
* theme/monokai.css
* theme/moxer.css
* theme/neat.css
* theme/neo.css
* theme/night.css
* theme/nord.css
* theme/oceanic-next.css
* theme/panda-syntax.css
* theme/paraiso-dark.css
* theme/paraiso-light.css
* theme/pastel-on-dark.css
* theme/railscasts.css
* theme/rubyblue.css
* theme/seti.css
* theme/shadowfox.css
* theme/solarized.css
* theme/ssms.css
* theme/the-matrix.css
* theme/tomorrow-night-bright.css
* theme/tomorrow-night-eighties.css
* theme/ttcn.css
* theme/twilight.css
* theme/vibrant-ink.css
* theme/xq-dark.css
* theme/xq-light.css
* theme/yeti.css
* theme/yonce.css
* theme/zenburn.css

View File

@ -1,5 +1,4 @@
## db-to-cloud v0.7.0
Following files are copied from npm (node_modules):
* db-to-cloud.min.js: dist\db-to-cloud.min.js
Files copied from NPM (node_modules):
* db-to-cloud.min.js: dist/*

22
vendor/draggable-list/LICENSE vendored Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2021 eight
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

4
vendor/draggable-list/README.md vendored Normal file
View File

@ -0,0 +1,4 @@
## @eight04/draggable-list v0.3.0
Files copied from NPM (node_modules):
* draggable-list.iife.min.js: dist/*

View File

@ -0,0 +1 @@
var DraggableList=function(){"use strict";const t="draggable-list-transformed";return function(e,{bound:r,scrollContainer:n}={}){for(const t of e.children)t.draggable=!0;new MutationObserver((t=>{for(const e of t)for(const t of e.addedNodes)t.draggable=!0})).observe(e,{childList:!0});let a=null,o=0,s=0,l=null,d=[],i=null,c=!1,g=0;function f(t,r,n){const o={origin:t,startPos:a,currentPos:l,dragTarget:i};n&&Object.assign(o,n),e.dispatchEvent(new CustomEvent(r,{detail:o}))}e.addEventListener("dragstart",(t=>{if(t.target.parentNode!==e)return;i=t.target,c=!1;const r=n?n.scrollLeft:0,u=n?n.scrollTop:0;a={x:t.pageX+r,y:t.pageY+u},o=[...e.children].indexOf(t.target),s=o,l=a,d=[...e.children].map((t=>{const e=t.getBoundingClientRect();return{top:e.top+window.scrollY+u,bottom:e.bottom+window.scrollY+u}})),g=o+1<d.length?d[o+1].top-d[o].top:o>0?d[o].bottom-d[o-1].bottom:0,i.classList.add("draggable-list-target"),e.classList.add("draggable-list-dragging"),f(t,"d:dragstart")})),e.addEventListener("dragenter",(t=>{i&&(t.preventDefault(),f(t,"d:dragmove"))})),e.addEventListener("dragover",(a=>{if(!i)return;a.preventDefault();const c=n?n.scrollLeft:0,u=n?n.scrollTop:0,p={x:a.pageX+c,y:a.pageY+u},m=function(t,e,r,n){if(r<t[0].top&&n)return e;for(let n=0;n<e;n++)if(!(t[n].bottom<r))return n;if(r>t[t.length-1].bottom&&n)return e;for(let n=t.length-1;n>e;n--)if(!(t[n].top>r))return n;return e}(d,o,p.y,r);!function(e,r,n,a,o){function s(r,n,a,o){for(let s=n;s<=a;s++)r&&!e[s].classList.contains(t)?(e[s].classList.add(t),e[s].style.transform=o):!r&&e[s].classList.contains(t)&&(e[s].classList.remove(t),e[s].style="")}a>n?(s(!1,n,Math.min(r-1,a-1)),r<e.length-1&&s(!0,Math.max(n+1,r+1),a,"translateY(".concat(-o,"px)"))):(s(!1,Math.max(r+1,a+1),n),r>0&&s(!0,a,Math.min(n-1,r-1),"translateY(".concat(o,"px)")))}(e.children,o,s,m,g),s=m,l=p,f(a,"d:dragmove")})),document.addEventListener("dragend",(r=>{if(i){for(const r of e.children)r.classList.remove(t),r.style="";i.classList.remove("draggable-list-target"),e.classList.remove("draggable-list-dragging"),f(r,"d:dragend",{originalIndex:o,spliceIndex:s,insertBefore:s<o?e.children[s]:e.children[s+1],dropped:c}),i=null}})),e.addEventListener("drop",(t=>{i&&(c=!0,t.preventDefault())}))}}();

View File

@ -1,6 +1,5 @@
## jsonlint v1.6.3
Following files are copied from npm (node_modules):
* jsonlint.js: lib\jsonlint.js
Files copied from NPM (node_modules):
* jsonlint.js: lib/*
* LICENSE: README.md

View File

@ -1,5 +1,4 @@
## less-bundle v0.1.0
Following files are copied from npm (node_modules):
* less.min.js: dist\less.min.js
Files copied from NPM (node_modules):
* less.min.js: dist/*

View File

@ -1,5 +1,4 @@
## lz-string-unsafe v1.4.4-fork-1
Following files are copied from npm (node_modules):
Files copied from NPM (node_modules):
* lz-string-unsafe.min.js

View File

@ -1,9 +1,8 @@
## stylelint-bundle v13.8.0
Following files are downloaded from HTTP:
Files downloaded from URL:
* LICENSE: https://github.com/stylelint/stylelint/raw/13.8.0/LICENSE
Following files are copied from npm (node_modules):
* stylelint-bundle.min.js: dist\stylelint-bundle.min.js
Files copied from NPM (node_modules):
* stylelint-bundle.min.js: dist/*

View File

@ -1,5 +1,4 @@
## stylus-lang-bundle v0.54.7
Following files are copied from npm (node_modules):
* stylus-renderer.min.js: dist\stylus-renderer.min.js
Files copied from NPM (node_modules):
* stylus-renderer.min.js: dist/*

View File

@ -1,5 +1,4 @@
## usercss-meta v0.12.0
Following files are copied from npm (node_modules):
* usercss-meta.min.js: dist\usercss-meta.min.js
Files copied from NPM (node_modules):
* usercss-meta.min.js: dist/*

View File

@ -1,5 +1,4 @@
## webext-launch-web-auth-flow v0.1.1
Following files are copied from npm (node_modules):
* webext-launch-web-auth-flow.min.js: dist\webext-launch-web-auth-flow.min.js
Files copied from NPM (node_modules):
* webext-launch-web-auth-flow.min.js: dist/*