Merge branch 'master' into dev-chrome-49
This commit is contained in:
commit
37dca280f2
|
@ -1373,6 +1373,10 @@
|
||||||
"message": "Stylus can access file:// URLs only if you enable the corresponding checkbox for Stylus extension on chrome://extensions page.",
|
"message": "Stylus can access file:// URLs only if you enable the corresponding checkbox for Stylus extension on chrome://extensions page.",
|
||||||
"description": "Note in the toolbar popup for file:// URLs"
|
"description": "Note in the toolbar popup for file:// URLs"
|
||||||
},
|
},
|
||||||
|
"InaccessibleFileHint": {
|
||||||
|
"message": "Stylus can not access some file types (e.g. pdf & json files).",
|
||||||
|
"description": "Note in the toolbar popup for some file types that cannot be accessed"
|
||||||
|
},
|
||||||
"updateAllCheckSucceededNoUpdate": {
|
"updateAllCheckSucceededNoUpdate": {
|
||||||
"message": "No updates found.",
|
"message": "No updates found.",
|
||||||
"description": "Text that displays when an update all check completed and no updates are available"
|
"description": "Text that displays when an update all check completed and no updates are available"
|
||||||
|
@ -1477,6 +1481,9 @@
|
||||||
"connectingDropbox": {
|
"connectingDropbox": {
|
||||||
"message": "Connecting Dropbox..."
|
"message": "Connecting Dropbox..."
|
||||||
},
|
},
|
||||||
|
"connectingDropboxNotAllowed": {
|
||||||
|
"message": "Connecting to Dropbox is only available in apps installed directly from the webstore"
|
||||||
|
},
|
||||||
"gettingStyles": {
|
"gettingStyles": {
|
||||||
"message": "Getting all styles..."
|
"message": "Getting all styles..."
|
||||||
},
|
},
|
||||||
|
|
|
@ -56,7 +56,7 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
|
||||||
return browser.runtime.openOptionsPage()
|
return browser.runtime.openOptionsPage()
|
||||||
.then(() => new Promise(resolve => setTimeout(resolve, 100)))
|
.then(() => new Promise(resolve => setTimeout(resolve, 100)))
|
||||||
.then(() => msg.broadcastExtension({method: 'optionsCustomizeHotkeys'}));
|
.then(() => msg.broadcastExtension({method: 'optionsCustomizeHotkeys'}));
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line no-var
|
// eslint-disable-next-line no-var
|
||||||
|
@ -150,6 +150,9 @@ chrome.runtime.onInstalled.addListener(({reason}) => {
|
||||||
});
|
});
|
||||||
// themes may change
|
// themes may change
|
||||||
delete localStorage.codeMirrorThemes;
|
delete localStorage.codeMirrorThemes;
|
||||||
|
// save install type: "admin", "development", "normal", "sideload" or "other"
|
||||||
|
// "normal" = addon installed from webstore
|
||||||
|
chrome.management.getSelf(info => localStorage.installType = info.installType);
|
||||||
});
|
});
|
||||||
|
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
* @returns {Promise<{style, dup:Boolean?}>}
|
* @returns {Promise<{style, dup:Boolean?}>}
|
||||||
*/
|
*/
|
||||||
function build({
|
function build({
|
||||||
|
styleId,
|
||||||
sourceCode,
|
sourceCode,
|
||||||
checkDup,
|
checkDup,
|
||||||
metaOnly,
|
metaOnly,
|
||||||
|
@ -83,7 +84,8 @@
|
||||||
}) {
|
}) {
|
||||||
return usercss.buildMeta(sourceCode)
|
return usercss.buildMeta(sourceCode)
|
||||||
.then(style => {
|
.then(style => {
|
||||||
const findDup = checkDup || assignVars ? find(style) : null;
|
const findDup = checkDup || assignVars ?
|
||||||
|
find(styleId ? {id: styleId} : style) : Promise.resolve();
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
metaOnly ? style : doBuild(style, findDup),
|
metaOnly ? style : doBuild(style, findDup),
|
||||||
findDup
|
findDup
|
||||||
|
|
|
@ -65,10 +65,9 @@ const APPLY = (() => {
|
||||||
// Since it's easy to spoof the browser version in pre-Quantum FF we're checking
|
// Since it's easy to spoof the browser version in pre-Quantum FF we're checking
|
||||||
// for getPreventDefault which got removed in FF59 https://bugzil.la/691151
|
// for getPreventDefault which got removed in FF59 https://bugzil.la/691151
|
||||||
const EVENT_NAME = chrome.runtime.id;
|
const EVENT_NAME = chrome.runtime.id;
|
||||||
const usePageScript = CHROME || isOwnPage || Event.prototype.getPreventDefault ?
|
let ready;
|
||||||
Promise.resolve(false) : injectPageScript();
|
|
||||||
return (el, content) =>
|
return (el, content) =>
|
||||||
usePageScript.then(ok => {
|
checkPageScript().then(ok => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
const disabled = el.disabled;
|
const disabled = el.disabled;
|
||||||
el.textContent = content;
|
el.textContent = content;
|
||||||
|
@ -83,9 +82,19 @@ const APPLY = (() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function checkPageScript() {
|
||||||
|
if (!ready) {
|
||||||
|
ready = CHROME || isOwnPage || Event.prototype.getPreventDefault ?
|
||||||
|
Promise.resolve(false) : injectPageScript();
|
||||||
|
}
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
function injectPageScript() {
|
function injectPageScript() {
|
||||||
const scriptContent = EVENT_NAME => {
|
const scriptContent = EVENT_NAME => {
|
||||||
document.currentScript.remove();
|
document.currentScript.remove();
|
||||||
|
const available = checkStyleApplied();
|
||||||
|
if (available) {
|
||||||
window.addEventListener(EVENT_NAME, function handler(e) {
|
window.addEventListener(EVENT_NAME, function handler(e) {
|
||||||
const {method, id, content} = e.detail;
|
const {method, id, content} = e.detail;
|
||||||
if (method === 'setStyleContent') {
|
if (method === 'setStyleContent') {
|
||||||
|
@ -100,16 +109,41 @@ const APPLY = (() => {
|
||||||
window.removeEventListener(EVENT_NAME, handler);
|
window.removeEventListener(EVENT_NAME, handler);
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
}
|
||||||
|
window.dispatchEvent(new CustomEvent(EVENT_NAME, {detail: {
|
||||||
|
method: 'init',
|
||||||
|
available
|
||||||
|
}}));
|
||||||
|
|
||||||
|
function checkStyleApplied() {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = ':root{--stylus-applied:1}';
|
||||||
|
document.documentElement.appendChild(style);
|
||||||
|
const applied = getComputedStyle(document.documentElement)
|
||||||
|
.getPropertyValue('--stylus-applied');
|
||||||
|
style.remove();
|
||||||
|
return Boolean(applied);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const code = `(${scriptContent})(${JSON.stringify(EVENT_NAME)})`;
|
const code = `(${scriptContent})(${JSON.stringify(EVENT_NAME)})`;
|
||||||
const src = `data:application/javascript;base64,${btoa(code)}`;
|
const src = `data:application/javascript;base64,${btoa(code)}`;
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
const {resolve, promise} = deferred();
|
const {resolve, promise} = deferred();
|
||||||
script.src = src;
|
script.src = src;
|
||||||
script.onload = () => resolve(true);
|
|
||||||
script.onerror = () => resolve(false);
|
script.onerror = () => resolve(false);
|
||||||
document.documentElement.appendChild(script);
|
window.addEventListener(EVENT_NAME, handleInit);
|
||||||
return promise;
|
(document.head || document.documentElement).appendChild(script);
|
||||||
|
return promise.then(result => {
|
||||||
|
script.remove();
|
||||||
|
window.removeEventListener(EVENT_NAME, handleInit);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleInit(e) {
|
||||||
|
if (e.detail.method === 'init') {
|
||||||
|
resolve(e.detail.available);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
<link href="vendor/codemirror/addon/search/matchesonscrollbar.css" rel="stylesheet">
|
<link href="vendor/codemirror/addon/search/matchesonscrollbar.css" rel="stylesheet">
|
||||||
<script src="vendor/codemirror/addon/search/matchesonscrollbar.js"></script>
|
<script src="vendor/codemirror/addon/search/matchesonscrollbar.js"></script>
|
||||||
<script src="vendor/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
<script src="vendor/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||||
<script src="vendor/codemirror/addon/search/match-highlighter.js"></script>
|
|
||||||
<script src="vendor/codemirror/addon/search/searchcursor.js"></script>
|
<script src="vendor/codemirror/addon/search/searchcursor.js"></script>
|
||||||
|
|
||||||
<script src="vendor/codemirror/addon/comment/comment.js"></script>
|
<script src="vendor/codemirror/addon/comment/comment.js"></script>
|
||||||
|
@ -62,6 +61,8 @@
|
||||||
<script src="vendor-overwrites/colorpicker/colorpicker.js"></script>
|
<script src="vendor-overwrites/colorpicker/colorpicker.js"></script>
|
||||||
<script src="vendor-overwrites/colorpicker/colorview.js"></script>
|
<script src="vendor-overwrites/colorpicker/colorview.js"></script>
|
||||||
|
|
||||||
|
<script src="vendor-overwrites/codemirror-addon/match-highlighter.js"></script>
|
||||||
|
|
||||||
<script src="js/polyfill.js"></script>
|
<script src="js/polyfill.js"></script>
|
||||||
<script src="js/promisify.js"></script>
|
<script src="js/promisify.js"></script>
|
||||||
<script src="js/dom.js"></script>
|
<script src="js/dom.js"></script>
|
||||||
|
@ -78,8 +79,6 @@
|
||||||
<link href="edit/global-search.css" rel="stylesheet">
|
<link href="edit/global-search.css" rel="stylesheet">
|
||||||
<script src="edit/global-search.js"></script>
|
<script src="edit/global-search.js"></script>
|
||||||
|
|
||||||
<script src="edit/match-highlighter-helper.js"></script>
|
|
||||||
|
|
||||||
<link href="edit/codemirror-default.css" rel="stylesheet">
|
<link href="edit/codemirror-default.css" rel="stylesheet">
|
||||||
<script src="edit/codemirror-default.js"></script>
|
<script src="edit/codemirror-default.js"></script>
|
||||||
|
|
||||||
|
|
|
@ -37,14 +37,6 @@
|
||||||
.cm-uso-variable {
|
.cm-uso-variable {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.cm-searching.cm-matchhighlight {
|
|
||||||
/* tokens found by manual search should not animate by cm-matchhighlight */
|
|
||||||
animation-name: search-and-match-highlighter !important;
|
|
||||||
}
|
|
||||||
@keyframes search-and-match-highlighter {
|
|
||||||
from { background-color: rgba(255, 255, 0, .4); } /* search color */
|
|
||||||
to { background-color: rgba(100, 255, 100, .4); } /* sarch + highlight */
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-activeline .applies-to:before {
|
.CodeMirror-activeline .applies-to:before {
|
||||||
background-color: hsla(214, 100%, 90%, 0.15);
|
background-color: hsla(214, 100%, 90%, 0.15);
|
||||||
|
|
|
@ -230,7 +230,7 @@
|
||||||
|
|
||||||
// editor commands
|
// editor commands
|
||||||
for (const name of ['save', 'toggleStyle', 'nextEditor', 'prevEditor']) {
|
for (const name of ['save', 'toggleStyle', 'nextEditor', 'prevEditor']) {
|
||||||
CodeMirror.commands[name] = () => editor[name]();
|
CodeMirror.commands[name] = (...args) => editor[name](...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeMirror convenience commands
|
// CodeMirror convenience commands
|
||||||
|
|
|
@ -32,12 +32,14 @@ const cmFactory = (() => {
|
||||||
if (value === 'token') {
|
if (value === 'token') {
|
||||||
cm.setOption('highlightSelectionMatches', {
|
cm.setOption('highlightSelectionMatches', {
|
||||||
showToken: /[#.\-\w]/,
|
showToken: /[#.\-\w]/,
|
||||||
annotateScrollbar: true
|
annotateScrollbar: true,
|
||||||
|
onUpdate: updateMatchHighlightCount
|
||||||
});
|
});
|
||||||
} else if (value === 'selection') {
|
} else if (value === 'selection') {
|
||||||
cm.setOption('highlightSelectionMatches', {
|
cm.setOption('highlightSelectionMatches', {
|
||||||
showToken: false,
|
showToken: false,
|
||||||
annotateScrollbar: true
|
annotateScrollbar: true,
|
||||||
|
onUpdate: updateMatchHighlightCount
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
cm.setOption('highlightSelectionMatches', null);
|
cm.setOption('highlightSelectionMatches', null);
|
||||||
|
@ -80,6 +82,10 @@ const cmFactory = (() => {
|
||||||
});
|
});
|
||||||
return {create, destroy, setOption};
|
return {create, destroy, setOption};
|
||||||
|
|
||||||
|
function updateMatchHighlightCount(cm, state) {
|
||||||
|
cm.display.wrapper.dataset.matchHighlightCount = state.matchesonscroll.matches.length;
|
||||||
|
}
|
||||||
|
|
||||||
function configureMouseFn(cm, repeat) {
|
function configureMouseFn(cm, repeat) {
|
||||||
return repeat === 'double' ?
|
return repeat === 'double' ?
|
||||||
{unit: selectTokenOnDoubleclick} :
|
{unit: selectTokenOnDoubleclick} :
|
||||||
|
|
|
@ -364,14 +364,14 @@ input:invalid {
|
||||||
.resize-grip-enabled .CodeMirror-scrollbar-filler {
|
.resize-grip-enabled .CodeMirror-scrollbar-filler {
|
||||||
bottom: 7px; /* make space for resize-grip */
|
bottom: 7px; /* make space for resize-grip */
|
||||||
}
|
}
|
||||||
body[data-match-highlight="token"] .cm-matchhighlight-approved .cm-matchhighlight,
|
body:not(.find-open) .cm-matchhighlight,
|
||||||
body[data-match-highlight="token"] .CodeMirror-selection-highlight-scrollbar {
|
body:not(.find-open) .CodeMirror-selection-highlight-scrollbar {
|
||||||
animation: fadein-match-highlighter 1s cubic-bezier(.97,.01,.42,.98);
|
animation: fadein-match-highlighter 1s cubic-bezier(.97,.01,.42,.98);
|
||||||
animation-fill-mode: both;
|
animation-fill-mode: both;
|
||||||
}
|
}
|
||||||
body[data-match-highlight="selection"] .cm-matchhighlight-approved .cm-matchhighlight,
|
body:not(.find-open) [data-match-highlight-count="1"] .cm-matchhighlight,
|
||||||
body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar {
|
body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-highlight-scrollbar {
|
||||||
background-color: rgba(1, 151, 193, 0.1);
|
animation: none;
|
||||||
}
|
}
|
||||||
@-webkit-keyframes highlight {
|
@-webkit-keyframes highlight {
|
||||||
from {
|
from {
|
||||||
|
|
28
edit/edit.js
28
edit/edit.js
|
@ -174,11 +174,29 @@ preinit();
|
||||||
$('#beautify').onclick = () => beautify(editor.getEditors());
|
$('#beautify').onclick = () => beautify(editor.getEditors());
|
||||||
$('#lint').addEventListener('scroll', hideLintHeaderOnScroll, {passive: true});
|
$('#lint').addEventListener('scroll', hideLintHeaderOnScroll, {passive: true});
|
||||||
window.addEventListener('resize', () => debounce(rememberWindowSize, 100));
|
window.addEventListener('resize', () => debounce(rememberWindowSize, 100));
|
||||||
editor = usercss ? createSourceEditor(style) : createSectionsEditor(style);
|
editor = (usercss ? createSourceEditor : createSectionsEditor)({
|
||||||
if (editor.ready) {
|
style,
|
||||||
return editor.ready();
|
onTitleChanged: updateTitle
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
editor.dirty.onChange(updateDirty);
|
||||||
|
return Promise.resolve(editor.ready && editor.ready())
|
||||||
|
.then(updateDirty);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTitle() {
|
||||||
|
if (editor) {
|
||||||
|
const styleName = editor.getStyle().name;
|
||||||
|
const isDirty = editor.dirty.isDirty();
|
||||||
|
document.title = (isDirty ? '* ' : '') + styleName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDirty() {
|
||||||
|
const isDirty = editor.dirty.isDirty();
|
||||||
|
document.body.classList.toggle('dirty', isDirty);
|
||||||
|
$('#save-button').disabled = !isDirty;
|
||||||
|
updateTitle();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -306,7 +324,7 @@ function beforeUnload(e) {
|
||||||
// refocus if unloading was canceled
|
// refocus if unloading was canceled
|
||||||
setTimeout(() => activeElement.focus());
|
setTimeout(() => activeElement.focus());
|
||||||
}
|
}
|
||||||
if (editor && editor.isDirty()) {
|
if (editor && editor.dirty.isDirty()) {
|
||||||
// neither confirm() nor custom messages work in modern browsers but just in case
|
// neither confirm() nor custom messages work in modern browsers but just in case
|
||||||
e.returnValue = t('styleChangesNotSaved');
|
e.returnValue = t('styleChangesNotSaved');
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,17 +181,33 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********** CodeMirror ****************/
|
/*********** CM search highlight restyling, which shouldn't need color variables ****************/
|
||||||
|
body.find-open .search-target-editor {
|
||||||
.search-target-editor {
|
outline-color: darkorange !important;
|
||||||
outline: 1px solid darkorange;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#stylus .search-target-match {
|
body.find-open .cm-searching {
|
||||||
|
background-color: rgba(255, 255, 0, .4);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.find-open .cm-searching.search-target-match {
|
||||||
background-color: darkorange;
|
background-color: darkorange;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.find-open .CodeMirror-search-match {
|
||||||
|
background: gold;
|
||||||
|
border-top: 1px solid orange;
|
||||||
|
border-bottom: 1px solid orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hide default CM search highlight styling */
|
||||||
|
body .cm-searching,
|
||||||
|
body .CodeMirror-search-match {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
@media (max-width: 500px) {
|
||||||
#search-replace-dialog {
|
#search-replace-dialog {
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
|
@ -752,8 +752,14 @@ onDOMready().then(() => {
|
||||||
function makeTargetVisible(element) {
|
function makeTargetVisible(element) {
|
||||||
const old = $('.' + TARGET_CLASS);
|
const old = $('.' + TARGET_CLASS);
|
||||||
if (old !== element) {
|
if (old !== element) {
|
||||||
if (old) old.classList.remove(TARGET_CLASS);
|
if (old) {
|
||||||
if (element) element.classList.add(TARGET_CLASS);
|
old.classList.remove(TARGET_CLASS);
|
||||||
|
document.body.classList.remove('find-open');
|
||||||
|
}
|
||||||
|
if (element) {
|
||||||
|
element.classList.add(TARGET_CLASS);
|
||||||
|
document.body.classList.add('find-open');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,18 +33,18 @@ function createLinterHelpDialog(getIssues) {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
headerLink = $createLink(baseUrl, 'stylelint');
|
headerLink = $createLink(baseUrl, 'stylelint');
|
||||||
template = ({rule}) =>
|
template = rule =>
|
||||||
$create('li',
|
$create('li',
|
||||||
rule === 'CssSyntaxError' ? rule : $createLink(baseUrl + rule, rule));
|
rule === 'CssSyntaxError' ? rule : $createLink(baseUrl + rule, rule));
|
||||||
}
|
}
|
||||||
const header = t('linterIssuesHelp', '\x01').split('\x01');
|
const header = t('linterIssuesHelp', '\x01').split('\x01');
|
||||||
const activeRules = getIssues();
|
const activeRules = new Set([...getIssues()].map(issue => issue.rule));
|
||||||
Promise.resolve(linter === 'csslint' && prepareCsslintRules())
|
Promise.resolve(linter === 'csslint' && prepareCsslintRules())
|
||||||
.then(() =>
|
.then(() =>
|
||||||
showHelp(t('linterIssues'),
|
showHelp(t('linterIssues'),
|
||||||
$create([
|
$create([
|
||||||
header[0], headerLink, header[1],
|
header[0], headerLink, header[1],
|
||||||
$create('ul.rules', [...activeRules.values()].map(template)),
|
$create('ul.rules', [...activeRules].map(template)),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,7 +12,7 @@ function createMetaCompiler(cm) {
|
||||||
if (_cm !== cm) {
|
if (_cm !== cm) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const match = text.match(/\/\*\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i);
|
const match = text.match(/\/\*\!?\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,223 +0,0 @@
|
||||||
/* global CodeMirror prefs */
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
(() => {
|
|
||||||
/*
|
|
||||||
The original match-highlighter addon always recreates the highlight overlay
|
|
||||||
even if the token under cursor hasn't changed, which is terribly ineffective
|
|
||||||
(the entire view is re-rendered) and makes our animated token highlight effect
|
|
||||||
restart on every cursor movement.
|
|
||||||
|
|
||||||
Invocation sequence of our hooks:
|
|
||||||
|
|
||||||
1. removeOverlayForHighlighter()
|
|
||||||
The original addon removes the overlay unconditionally
|
|
||||||
so this hook saves the state if the token hasn't changed.
|
|
||||||
|
|
||||||
2. addOverlayForHighlighter()
|
|
||||||
Restores the saved state instead of creating a new overlay,
|
|
||||||
installs a hook to count occurrences.
|
|
||||||
|
|
||||||
3. matchesOnScrollbar()
|
|
||||||
Saves the query regexp passed from the original addon in our helper object,
|
|
||||||
and in case removeOverlayForHighlighter() decided to keep the overlay
|
|
||||||
only rewrites the regexp without invoking the original constructor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const HL_APPROVED = 'cm-matchhighlight-approved';
|
|
||||||
const SEARCH_MATCH_TOKEN_NAME = 'searching';
|
|
||||||
|
|
||||||
const originalAddOverlay = CodeMirror.prototype.addOverlay;
|
|
||||||
const originalRemoveOverlay = CodeMirror.prototype.removeOverlay;
|
|
||||||
const originalMatchesOnScrollbar = CodeMirror.prototype.showMatchesOnScrollbar;
|
|
||||||
const originalSetOption = CodeMirror.prototype.setOption;
|
|
||||||
let originalGetOption;
|
|
||||||
|
|
||||||
CodeMirror.prototype.addOverlay = addOverlay;
|
|
||||||
CodeMirror.prototype.removeOverlay = removeOverlay;
|
|
||||||
CodeMirror.prototype.showMatchesOnScrollbar = matchesOnScrollbar;
|
|
||||||
CodeMirror.prototype.setOption = setOption;
|
|
||||||
|
|
||||||
let enabled = Boolean(prefs.get('editor.matchHighlight'));
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
function setOption(option, value) {
|
|
||||||
enabled = option === 'highlightSelectionMatches' ? value : enabled;
|
|
||||||
return originalSetOption.apply(this, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldIntercept(overlay) {
|
|
||||||
const hlState = this.state.matchHighlighter || {};
|
|
||||||
return overlay === hlState.overlay && (hlState.options || {}).showToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addOverlay() {
|
|
||||||
return enabled && shouldIntercept.apply(this, arguments) &&
|
|
||||||
addOverlayForHighlighter.apply(this, arguments) ||
|
|
||||||
originalAddOverlay.apply(this, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeOverlay() {
|
|
||||||
return enabled && shouldIntercept.apply(this, arguments) &&
|
|
||||||
removeOverlayForHighlighter.apply(this, arguments) ||
|
|
||||||
originalRemoveOverlay.apply(this, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addOverlayForHighlighter(overlay) {
|
|
||||||
const state = this.state.matchHighlighter || {};
|
|
||||||
const helper = state.highlightHelper = state.highlightHelper || {};
|
|
||||||
|
|
||||||
helper.rewriteScrollbarQuery = true;
|
|
||||||
|
|
||||||
// since we're here the original addon decided there's something to highlight,
|
|
||||||
// so we cancel removeOverlayIfExpired() scheduled in our removeOverlay hook
|
|
||||||
clearTimeout(helper.hookTimer);
|
|
||||||
|
|
||||||
// the original addon just removed its overlays, which was intercepted by removeOverlayForHighlighter,
|
|
||||||
// which decided to restore it and saved the previous overlays in our helper object,
|
|
||||||
// so here we are now, restoring them
|
|
||||||
if (helper.skipMatchesOnScrollbar) {
|
|
||||||
state.matchesonscroll = helper.matchesonscroll;
|
|
||||||
state.overlay = helper.overlay;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hook the newly created overlay's token() to count the occurrences
|
|
||||||
if (overlay.token !== tokenHook) {
|
|
||||||
overlay.highlightHelper = {
|
|
||||||
token: overlay.token,
|
|
||||||
occurrences: 0,
|
|
||||||
};
|
|
||||||
overlay.token = tokenHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
// speed up rendering of scrollbar marks 4 times: we don't need ultimate precision there
|
|
||||||
// so for the duration of this event loop cycle we spoof the "lineWrapping" option
|
|
||||||
// and restore it in the next event loop cycle
|
|
||||||
if (this.options.lineWrapping && CodeMirror.prototype.getOption !== spoofLineWrappingOption) {
|
|
||||||
originalGetOption = CodeMirror.prototype.getOption;
|
|
||||||
CodeMirror.prototype.getOption = spoofLineWrappingOption;
|
|
||||||
setTimeout(() => (CodeMirror.prototype.getOption = originalGetOption));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function spoofLineWrappingOption(option) {
|
|
||||||
return option !== 'lineWrapping' && originalGetOption.apply(this, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenHook(stream) {
|
|
||||||
// we don't highlight a single match in case 'editor.matchHighlight' option is 'token'
|
|
||||||
// so this hook counts the occurrences and toggles HL_APPROVED class on CM's wrapper element
|
|
||||||
const style = this.highlightHelper.token.call(this, stream);
|
|
||||||
if (style !== 'matchhighlight') {
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokens = stream.lineOracle.baseTokens;
|
|
||||||
const tokenIndex = tokens.indexOf(stream.pos, 1);
|
|
||||||
if (tokenIndex > 0) {
|
|
||||||
const tokenStart = tokenIndex > 2 ? tokens[tokenIndex - 2] : 0;
|
|
||||||
const token = tokenStart === stream.start && tokens[tokenIndex + 1];
|
|
||||||
const index = token && token.indexOf(SEARCH_MATCH_TOKEN_NAME);
|
|
||||||
if (token && index >= 0 &&
|
|
||||||
(token[index - 1] || ' ') === ' ' &&
|
|
||||||
(token[index + SEARCH_MATCH_TOKEN_NAME.length] || ' ') === ' ') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const num = ++this.highlightHelper.occurrences;
|
|
||||||
if (num === 1) {
|
|
||||||
stream.lineOracle.doc.cm.display.wrapper.classList.remove(HL_APPROVED);
|
|
||||||
} else if (num === 2) {
|
|
||||||
stream.lineOracle.doc.cm.display.wrapper.classList.add(HL_APPROVED);
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeOverlayForHighlighter() {
|
|
||||||
const state = this.state.matchHighlighter || {};
|
|
||||||
const helper = state.highlightHelper;
|
|
||||||
const {query, originalToken} = helper || state.matchesonscroll || {};
|
|
||||||
// no current query means nothing to preserve => remove the overlay
|
|
||||||
if (!query || !originalToken) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const sel = this.getSelection();
|
|
||||||
// current query differs from the selected text => remove the overlay
|
|
||||||
if (sel && sel.toLowerCase() !== originalToken.toLowerCase()) {
|
|
||||||
helper.query = helper.originalToken = sel;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if token under cursor has changed => remove the overlay
|
|
||||||
if (!sel) {
|
|
||||||
const {line, ch} = this.getCursor();
|
|
||||||
const queryLen = originalToken.length;
|
|
||||||
const start = Math.max(0, ch - queryLen + 1);
|
|
||||||
const end = ch + queryLen;
|
|
||||||
const string = this.getLine(line);
|
|
||||||
const area = string.slice(start, end);
|
|
||||||
const i = area.indexOf(query);
|
|
||||||
const startInArea = i < 0 ? NaN : i;
|
|
||||||
if (isNaN(startInArea) || start + startInArea > ch ||
|
|
||||||
state.options.showToken.test(string[start + startInArea - 1] || '') ||
|
|
||||||
state.options.showToken.test(string[start + startInArea + queryLen] || '')) {
|
|
||||||
// pass the displayed instance back to the original code to remove it
|
|
||||||
state.matchesonscroll = state.matchesonscroll || helper && helper.matchesonscroll;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// since the same token is under cursor we prevent the highlighter from rerunning
|
|
||||||
// by saving current overlays in a helper object so that it's restored in addOverlayForHighlighter()
|
|
||||||
state.highlightHelper = {
|
|
||||||
overlay: state.overlay,
|
|
||||||
matchesonscroll: state.matchesonscroll || (helper || {}).matchesonscroll,
|
|
||||||
// instruct our matchesOnScrollbar hook to preserve current scrollbar annotations
|
|
||||||
skipMatchesOnScrollbar: true,
|
|
||||||
// in case the original addon won't highlight anything we need to actually remove the overlays
|
|
||||||
// by setting a timer that runs in the next event loop cycle and can be canceled in this cycle
|
|
||||||
hookTimer: setTimeout(removeOverlayIfExpired, 0, this, state),
|
|
||||||
originalToken,
|
|
||||||
query,
|
|
||||||
};
|
|
||||||
// fool the original addon so it won't invoke state.matchesonscroll.clear()
|
|
||||||
state.matchesonscroll = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeOverlayIfExpired(self, state) {
|
|
||||||
const {overlay, matchesonscroll} = state.highlightHelper || {};
|
|
||||||
if (overlay) {
|
|
||||||
originalRemoveOverlay.call(self, overlay);
|
|
||||||
}
|
|
||||||
if (matchesonscroll) {
|
|
||||||
matchesonscroll.clear();
|
|
||||||
}
|
|
||||||
state.highlightHelper = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function matchesOnScrollbar(query, ...args) {
|
|
||||||
if (!enabled) {
|
|
||||||
return originalMatchesOnScrollbar.call(this, query, ...args);
|
|
||||||
}
|
|
||||||
const state = this.state.matchHighlighter;
|
|
||||||
const helper = state.highlightHelper = state.highlightHelper || {};
|
|
||||||
// rewrite the \btoken\b regexp so it matches .token and #token and --token
|
|
||||||
if (helper.rewriteScrollbarQuery && /^\\b.*?\\b$/.test(query.source)) {
|
|
||||||
helper.rewriteScrollbarQuery = undefined;
|
|
||||||
helper.originalToken = query.source.slice(2, -2);
|
|
||||||
const notToken = '(?!' + state.options.showToken.source + ').';
|
|
||||||
query = new RegExp(`(^|${notToken})` + helper.originalToken + `(${notToken}|$)`);
|
|
||||||
}
|
|
||||||
// save the query for future use in removeOverlayForHighlighter
|
|
||||||
helper.query = query;
|
|
||||||
// if removeOverlayForHighlighter() decided to keep the overlay
|
|
||||||
if (helper.skipMatchesOnScrollbar) {
|
|
||||||
helper.skipMatchesOnScrollbar = undefined;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
return originalMatchesOnScrollbar.call(this, query, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
|
@ -6,10 +6,9 @@
|
||||||
/* exported createSectionsEditor */
|
/* exported createSectionsEditor */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function createSectionsEditor(style) {
|
function createSectionsEditor({style, onTitleChanged}) {
|
||||||
let INC_ID = 0; // an increment id that is used by various object to track the order
|
let INC_ID = 0; // an increment id that is used by various object to track the order
|
||||||
const dirty = dirtyReporter();
|
const dirty = dirtyReporter();
|
||||||
dirty.onChange(updateTitle);
|
|
||||||
|
|
||||||
const container = $('#sections');
|
const container = $('#sections');
|
||||||
const sections = [];
|
const sections = [];
|
||||||
|
@ -18,7 +17,7 @@ function createSectionsEditor(style) {
|
||||||
nameEl.addEventListener('input', () => {
|
nameEl.addEventListener('input', () => {
|
||||||
dirty.modify('name', style.name, nameEl.value);
|
dirty.modify('name', style.name, nameEl.value);
|
||||||
style.name = nameEl.value;
|
style.name = nameEl.value;
|
||||||
updateTitle();
|
onTitleChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
const enabledEl = $('#enabled');
|
const enabledEl = $('#enabled');
|
||||||
|
@ -64,7 +63,7 @@ function createSectionsEditor(style) {
|
||||||
return {
|
return {
|
||||||
ready: () => initializing,
|
ready: () => initializing,
|
||||||
replaceStyle,
|
replaceStyle,
|
||||||
isDirty: dirty.isDirty,
|
dirty,
|
||||||
getStyle: () => style,
|
getStyle: () => style,
|
||||||
getEditors,
|
getEditors,
|
||||||
scrollToEditor,
|
scrollToEditor,
|
||||||
|
@ -201,35 +200,27 @@ function createSectionsEditor(style) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextEditor(cm, cycle = true) {
|
function nextEditor(cm, cycle = true) {
|
||||||
if (!cycle) {
|
if (!cycle && findLast(sections, s => !s.isRemoved()).cm === cm) {
|
||||||
for (const section of sections) {
|
|
||||||
if (section.isRemoved()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cm === section.cm) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nextPrevEditor(cm, 1);
|
return nextPrevEditor(cm, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevEditor(cm, cycle = true) {
|
function prevEditor(cm, cycle = true) {
|
||||||
if (!cycle) {
|
if (!cycle && sections.find(s => !s.isRemoved()).cm === cm) {
|
||||||
for (let i = sections.length - 1; i >= 0; i--) {
|
|
||||||
if (sections[i].isRemoved()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cm === sections[i].cm) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nextPrevEditor(cm, -1);
|
return nextPrevEditor(cm, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findLast(arr, match) {
|
||||||
|
for (let i = arr.length - 1; i >= 0; i--) {
|
||||||
|
if (match(arr[i])) {
|
||||||
|
return arr[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function nextPrevEditor(cm, direction) {
|
function nextPrevEditor(cm, direction) {
|
||||||
const editors = getEditors();
|
const editors = getEditors();
|
||||||
cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length];
|
cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length];
|
||||||
|
@ -421,7 +412,7 @@ function createSectionsEditor(style) {
|
||||||
nameEl.value = style.name || '';
|
nameEl.value = style.name || '';
|
||||||
enabledEl.checked = style.enabled !== false;
|
enabledEl.checked = style.enabled !== false;
|
||||||
$('#url').href = style.url || '';
|
$('#url').href = style.url || '';
|
||||||
updateTitle();
|
onTitleChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLivePreview() {
|
function updateLivePreview() {
|
||||||
|
@ -432,14 +423,6 @@ function createSectionsEditor(style) {
|
||||||
livePreview.update(getModel());
|
livePreview.update(getModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTitle() {
|
|
||||||
const name = style.name;
|
|
||||||
const clean = !dirty.isDirty();
|
|
||||||
const title = !style.id ? t('addStyleTitle') : name;
|
|
||||||
document.title = (clean ? '' : '* ') + title;
|
|
||||||
$('#save-button').disabled = clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initSection({
|
function initSection({
|
||||||
sections: originalSections,
|
sections: originalSections,
|
||||||
total = originalSections.length,
|
total = originalSections.length,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/* exported createSourceEditor */
|
/* exported createSourceEditor */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function createSourceEditor(style) {
|
function createSourceEditor({style, onTitleChanged}) {
|
||||||
$('#name').disabled = true;
|
$('#name').disabled = true;
|
||||||
$('#save-button').disabled = true;
|
$('#save-button').disabled = true;
|
||||||
$('#mozilla-format-container').remove();
|
$('#mozilla-format-container').remove();
|
||||||
|
@ -16,12 +16,6 @@ function createSourceEditor(style) {
|
||||||
$('#sections').appendChild($create('.single-editor'));
|
$('#sections').appendChild($create('.single-editor'));
|
||||||
|
|
||||||
const dirty = dirtyReporter();
|
const dirty = dirtyReporter();
|
||||||
dirty.onChange(() => {
|
|
||||||
const isDirty = dirty.isDirty();
|
|
||||||
document.body.classList.toggle('dirty', isDirty);
|
|
||||||
$('#save-button').disabled = !isDirty;
|
|
||||||
updateTitle();
|
|
||||||
});
|
|
||||||
|
|
||||||
// normalize style
|
// normalize style
|
||||||
if (!style.id) setupNewStyle(style);
|
if (!style.id) setupNewStyle(style);
|
||||||
|
@ -82,6 +76,7 @@ function createSourceEditor(style) {
|
||||||
|
|
||||||
function preprocess(style) {
|
function preprocess(style) {
|
||||||
return API.buildUsercss({
|
return API.buildUsercss({
|
||||||
|
styleId: style.id,
|
||||||
sourceCode: style.sourceCode,
|
sourceCode: style.sourceCode,
|
||||||
assignVars: true
|
assignVars: true
|
||||||
})
|
})
|
||||||
|
@ -170,18 +165,10 @@ function createSourceEditor(style) {
|
||||||
$('#name').value = style.name;
|
$('#name').value = style.name;
|
||||||
$('#enabled').checked = style.enabled;
|
$('#enabled').checked = style.enabled;
|
||||||
$('#url').href = style.url;
|
$('#url').href = style.url;
|
||||||
updateTitle();
|
onTitleChanged();
|
||||||
return cm.setPreprocessor((style.usercssData || {}).preprocessor);
|
return cm.setPreprocessor((style.usercssData || {}).preprocessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTitle() {
|
|
||||||
const newTitle = (dirty.isDirty() ? '* ' : '') +
|
|
||||||
(style.id ? style.name : t('addStyleTitle'));
|
|
||||||
if (document.title !== newTitle) {
|
|
||||||
document.title = newTitle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceStyle(newStyle, codeIsUpdated) {
|
function replaceStyle(newStyle, codeIsUpdated) {
|
||||||
const sameCode = newStyle.sourceCode === cm.getValue();
|
const sameCode = newStyle.sourceCode === cm.getValue();
|
||||||
if (sameCode) {
|
if (sameCode) {
|
||||||
|
@ -384,7 +371,7 @@ function createSourceEditor(style) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
replaceStyle,
|
replaceStyle,
|
||||||
isDirty: dirty.isDirty,
|
dirty,
|
||||||
getStyle: () => style,
|
getStyle: () => style,
|
||||||
getEditors: () => [cm],
|
getEditors: () => [cm],
|
||||||
scrollToEditor: () => {},
|
scrollToEditor: () => {},
|
||||||
|
|
|
@ -10,7 +10,7 @@ const usercss = (() => {
|
||||||
// updateURL: 'updateUrl',
|
// updateURL: 'updateUrl',
|
||||||
name: undefined,
|
name: undefined,
|
||||||
};
|
};
|
||||||
const RX_META = /\/\*\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i;
|
const RX_META = /\/\*\!?\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i;
|
||||||
const ERR_ARGS_IS_LIST = new Set(['missingMandatory', 'missingChar']);
|
const ERR_ARGS_IS_LIST = new Set(['missingMandatory', 'missingChar']);
|
||||||
return {buildMeta, buildCode, assignVars};
|
return {buildMeta, buildCode, assignVars};
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,9 @@ function reportUpdateState({updated, style, error, STATES}) {
|
||||||
error = t('updateCheckSkippedLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
|
error = t('updateCheckSkippedLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
|
||||||
} else if (error === STATES.MAYBE_EDITED) {
|
} else if (error === STATES.MAYBE_EDITED) {
|
||||||
error = t('updateCheckSkippedMaybeLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
|
error = t('updateCheckSkippedMaybeLocallyEdited') + '\n' + t('updateCheckManualUpdateHint');
|
||||||
|
} else if (typeof error === 'object' && error.message) {
|
||||||
|
// UserCSS meta errors provide an object
|
||||||
|
error = error.message;
|
||||||
}
|
}
|
||||||
const message = same ? t('updateCheckSucceededNoUpdate') : error;
|
const message = same ? t('updateCheckSucceededNoUpdate') : error;
|
||||||
newClasses.set('no-update', true);
|
newClasses.set('no-update', true);
|
||||||
|
|
|
@ -80,7 +80,6 @@
|
||||||
<template data-id="unreachableInfo">
|
<template data-id="unreachableInfo">
|
||||||
<div class="blocked-info">
|
<div class="blocked-info">
|
||||||
<label i18n-text="unreachableContentScript"></label>
|
<label i18n-text="unreachableContentScript"></label>
|
||||||
<p i18n-text="unreachableFileHint"></p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -554,7 +554,8 @@ body.blocked .actions > .main-controls {
|
||||||
}
|
}
|
||||||
|
|
||||||
.blocked-info {
|
.blocked-info {
|
||||||
hyphens: auto;
|
hyphens: none;
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blocked-info label {
|
.blocked-info label {
|
||||||
|
|
|
@ -130,6 +130,10 @@ function initPopup() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const info = template.unreachableInfo;
|
const info = template.unreachableInfo;
|
||||||
|
if (!FIREFOX) {
|
||||||
|
// Chrome "Allow access to file URLs" in chrome://extensions message
|
||||||
|
info.appendChild($create('p', t('unreachableFileHint')));
|
||||||
|
}
|
||||||
if (FIREFOX && tabURL.startsWith(URLS.browserWebStore)) {
|
if (FIREFOX && tabURL.startsWith(URLS.browserWebStore)) {
|
||||||
$('label', info).textContent = t('unreachableAMO');
|
$('label', info).textContent = t('unreachableAMO');
|
||||||
const note = (FIREFOX < 59 ? t('unreachableAMOHintOldFF') : t('unreachableAMOHint')) +
|
const note = (FIREFOX < 59 ? t('unreachableAMOHintOldFF') : t('unreachableAMOHint')) +
|
||||||
|
@ -137,9 +141,11 @@ function initPopup() {
|
||||||
const renderToken = s => s[0] === '<' ? $create('b', tWordBreak(s.slice(1, -1))) : s;
|
const renderToken = s => s[0] === '<' ? $create('b', tWordBreak(s.slice(1, -1))) : s;
|
||||||
const renderLine = line => $create('p', line.split(/(<.*?>)/).map(renderToken));
|
const renderLine = line => $create('p', line.split(/(<.*?>)/).map(renderToken));
|
||||||
const noteNode = $create('fragment', note.split('\n').map(renderLine));
|
const noteNode = $create('fragment', note.split('\n').map(renderLine));
|
||||||
const target = $('p', info);
|
info.appendChild(noteNode);
|
||||||
target.parentNode.insertBefore(noteNode, target);
|
}
|
||||||
target.remove();
|
// Inaccessible locally hosted file type, e.g. JSON, PDF, etc.
|
||||||
|
if (tabURL.length - tabURL.lastIndexOf(".") <= 5) {
|
||||||
|
info.appendChild($create('p', t('InaccessibleFileHint')));
|
||||||
}
|
}
|
||||||
document.body.classList.add('unreachable');
|
document.body.classList.add('unreachable');
|
||||||
document.body.insertBefore(info, document.body.firstChild);
|
document.body.insertBefore(info, document.body.firstChild);
|
||||||
|
|
|
@ -689,6 +689,7 @@ window.addEventListener('showStyles:done', function _() {
|
||||||
'/api/v1/styles/subcategory' +
|
'/api/v1/styles/subcategory' +
|
||||||
'?search=' + encodeURIComponent(category) +
|
'?search=' + encodeURIComponent(category) +
|
||||||
'&page=' + searchCurrentPage +
|
'&page=' + searchCurrentPage +
|
||||||
|
'&per_page=10' +
|
||||||
'&country=NA';
|
'&country=NA';
|
||||||
|
|
||||||
const cacheKey = category + '/' + searchCurrentPage;
|
const cacheKey = category + '/' + searchCurrentPage;
|
||||||
|
|
|
@ -46,8 +46,11 @@ function uploadFileDropbox(client, stylesText) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#sync-dropbox-export').onclick = () => {
|
$('#sync-dropbox-export').onclick = () => {
|
||||||
|
const mode = localStorage.installType;
|
||||||
const title = t('syncDropboxStyles');
|
const title = t('syncDropboxStyles');
|
||||||
messageProgressBar({title: title, text: t('connectingDropbox')});
|
const text = mode === 'normal' ? t('connectingDropbox') : t('connectingDropboxNotAllowed');
|
||||||
|
messageProgressBar({title, text});
|
||||||
|
if (mode !== 'normal') return;
|
||||||
|
|
||||||
hasDropboxAccessToken()
|
hasDropboxAccessToken()
|
||||||
.then(token => token || requestDropboxAccessToken())
|
.then(token => token || requestDropboxAccessToken())
|
||||||
|
@ -116,8 +119,11 @@ $('#sync-dropbox-export').onclick = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
$('#sync-dropbox-import').onclick = () => {
|
$('#sync-dropbox-import').onclick = () => {
|
||||||
|
const mode = localStorage.installType;
|
||||||
const title = t('retrieveDropboxSync');
|
const title = t('retrieveDropboxSync');
|
||||||
messageProgressBar({title: title, text: t('connectingDropbox')});
|
const text = mode === 'normal' ? t('connectingDropbox') : t('connectingDropboxNotAllowed');
|
||||||
|
messageProgressBar({title, text});
|
||||||
|
if (mode !== 'normal') return;
|
||||||
|
|
||||||
hasDropboxAccessToken()
|
hasDropboxAccessToken()
|
||||||
.then(token => token || requestDropboxAccessToken())
|
.then(token => token || requestDropboxAccessToken())
|
||||||
|
|
|
@ -402,10 +402,6 @@
|
||||||
if (!ch) {
|
if (!ch) {
|
||||||
break;
|
break;
|
||||||
} else if (ch === '/' && peek() === '*') { /* css comment */
|
} else if (ch === '/' && peek() === '*') { /* css comment */
|
||||||
if (isAfterNewline) {
|
|
||||||
print.newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
print.text(eatComment());
|
print.text(eatComment());
|
||||||
if (peek() !== ';') print.newLine();
|
if (peek() !== ';') print.newLine();
|
||||||
} else if (ch === '/' && peek() === '/') { // single line comment
|
} else if (ch === '/' && peek() === '/') { // single line comment
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
wordsOnly: false,
|
wordsOnly: false,
|
||||||
annotateScrollbar: false,
|
annotateScrollbar: false,
|
||||||
showToken: false,
|
showToken: false,
|
||||||
trim: true
|
trim: true,
|
||||||
|
onUpdate: () => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function State(options) {
|
function State(options) {
|
||||||
|
@ -46,6 +47,7 @@
|
||||||
this.overlay = this.timeout = null;
|
this.overlay = this.timeout = null;
|
||||||
this.matchesonscroll = null;
|
this.matchesonscroll = null;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
this.query = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
|
CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
|
||||||
|
@ -88,12 +90,24 @@
|
||||||
|
|
||||||
function addOverlay(cm, query, hasBoundary, style) {
|
function addOverlay(cm, query, hasBoundary, style) {
|
||||||
var state = cm.state.matchHighlighter;
|
var state = cm.state.matchHighlighter;
|
||||||
|
if (state.query === query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeOverlay(cm);
|
||||||
|
state.query = query;
|
||||||
cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
|
cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
|
||||||
if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
|
if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
|
||||||
var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + "\\b") : query;
|
var searchFor = hasBoundary ?
|
||||||
|
new RegExp(
|
||||||
|
(/[a-z]/i.test(query[0]) ? "\\b" : "") +
|
||||||
|
query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") +
|
||||||
|
(/[a-z]/i.test(query[query.length - 1]) ? "\\b" : ""),
|
||||||
|
"m"
|
||||||
|
) : query;
|
||||||
state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
|
state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
|
||||||
{className: "CodeMirror-selection-highlight-scrollbar"});
|
{className: "CodeMirror-selection-highlight-scrollbar"});
|
||||||
}
|
}
|
||||||
|
state.options.onUpdate(cm, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeOverlay(cm) {
|
function removeOverlay(cm) {
|
||||||
|
@ -106,19 +120,22 @@
|
||||||
state.matchesonscroll = null;
|
state.matchesonscroll = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
state.query = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function highlightMatches(cm) {
|
function highlightMatches(cm) {
|
||||||
cm.operation(function() {
|
cm.operation(function() {
|
||||||
var state = cm.state.matchHighlighter;
|
var state = cm.state.matchHighlighter;
|
||||||
removeOverlay(cm);
|
|
||||||
if (!cm.somethingSelected() && state.options.showToken) {
|
if (!cm.somethingSelected() && state.options.showToken) {
|
||||||
var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
|
var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
|
||||||
var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
|
var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
|
||||||
while (start && re.test(line.charAt(start - 1))) --start;
|
while (start && re.test(line.charAt(start - 1))) --start;
|
||||||
while (end < line.length && re.test(line.charAt(end))) ++end;
|
while (end < line.length && re.test(line.charAt(end))) ++end;
|
||||||
if (start < end)
|
if (start < end) {
|
||||||
addOverlay(cm, line.slice(start, end), re, state.options.style);
|
addOverlay(cm, line.slice(start, end), re, state.options.style);
|
||||||
|
} else {
|
||||||
|
removeOverlay(cm);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
||||||
|
@ -126,8 +143,11 @@
|
||||||
if (state.options.wordsOnly && !isWord(cm, from, to)) return;
|
if (state.options.wordsOnly && !isWord(cm, from, to)) return;
|
||||||
var selection = cm.getRange(from, to)
|
var selection = cm.getRange(from, to)
|
||||||
if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
|
if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
|
||||||
if (selection.length >= state.options.minChars)
|
if (selection.length >= state.options.minChars) {
|
||||||
addOverlay(cm, selection, false, state.options.style);
|
addOverlay(cm, selection, false, state.options.style);
|
||||||
|
} else {
|
||||||
|
removeOverlay(cm);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4777,7 +4777,7 @@ self.parserlib = (() => {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_expr(inFunction) {
|
_expr(inFunction, endToken = Tokens.RPAREN) {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
const values = [];
|
const values = [];
|
||||||
|
|
||||||
|
@ -4786,7 +4786,7 @@ self.parserlib = (() => {
|
||||||
if (!value && !values.length) return null;
|
if (!value && !values.length) return null;
|
||||||
|
|
||||||
// get everything inside the parens and let validateProperty handle that
|
// get everything inside the parens and let validateProperty handle that
|
||||||
if (!value && inFunction && stream.peek() !== Tokens.RPAREN) {
|
if (!value && inFunction && stream.peek() !== endToken) {
|
||||||
stream.get();
|
stream.get();
|
||||||
value = new PropertyValuePart(stream._token);
|
value = new PropertyValuePart(stream._token);
|
||||||
} else if (!value) {
|
} else if (!value) {
|
||||||
|
@ -4914,8 +4914,9 @@ self.parserlib = (() => {
|
||||||
inFunction && Tokens.LBRACE,
|
inFunction && Tokens.LBRACE,
|
||||||
])) {
|
])) {
|
||||||
const token = stream._token;
|
const token = stream._token;
|
||||||
token.expr = this._expr(inFunction);
|
const endToken = Tokens.type(token.endChar);
|
||||||
stream.mustMatch(Tokens.type(token.endChar));
|
token.expr = this._expr(inFunction, endToken);
|
||||||
|
stream.mustMatch(endToken);
|
||||||
return finalize(token, token.value + (token.expr || '') + token.endChar);
|
return finalize(token, token.value + (token.expr || '') + token.endChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user