prevent inline overlays from breaking color swatches

This commit is contained in:
tophf 2017-11-24 13:16:59 +03:00
parent 7e4683d417
commit 7c0d8f0841
4 changed files with 60 additions and 42 deletions

View File

@ -1,7 +1,8 @@
/* global CodeMirror loadScript editors showHelp */ /* global CodeMirror loadScript editors showHelp */
'use strict'; 'use strict';
window.initColorpicker = () => { // eslint-disable-next-line no-var
var initColorpicker = () => {
initOverlayHooks(); initOverlayHooks();
onDOMready().then(() => { onDOMready().then(() => {
$('#colorpicker-settings').onclick = configureColorpicker; $('#colorpicker-settings').onclick = configureColorpicker;
@ -109,41 +110,37 @@ window.initColorpicker = () => {
function initOverlayHooks() { function initOverlayHooks() {
const COLORVIEW_DISABLED_SUFFIX = ' colorview-disabled'; const COLORVIEW_DISABLED_SUFFIX = ' colorview-disabled';
const COLORVIEW_NEXT_DISABLED_SUFFIX = ' colorview-next-disabled';
const originalAddOverlay = CodeMirror.prototype.addOverlay; const originalAddOverlay = CodeMirror.prototype.addOverlay;
CodeMirror.prototype.addOverlay = addOverlayHook; CodeMirror.prototype.addOverlay = addOverlayHook;
function addOverlayHook(overlay) { function addOverlayHook(overlay) {
if (overlay === (this.state.matchHighlighter || {}).overlay) { if (overlay.token !== tokenHook && (
if (overlay.token !== tokenHook) { overlay === (this.state.matchHighlighter || {}).overlay ||
overlay.stylusColorpickerHelper = { overlay === (this.state.search || {}).overlay)) {
token: overlay.token, overlay.colopickerHelper = {token: overlay.token};
};
overlay.token = tokenHook; overlay.token = tokenHook;
} }
}
originalAddOverlay.apply(this, arguments); originalAddOverlay.apply(this, arguments);
} }
function tokenHook(stream) { function tokenHook(stream) {
const style = this.stylusColorpickerHelper.token.call(this, stream); const style = this.colopickerHelper.token.apply(this, arguments);
if (style === 'matchhighlight') { if (!style) {
return tokenHookForHighlighter.call(this, stream, style);
} else {
return style; return style;
} }
}
function tokenHookForHighlighter(stream, style) {
const {start, pos, lineOracle: {baseTokens}} = stream; const {start, pos, lineOracle: {baseTokens}} = stream;
if (!baseTokens) { if (!baseTokens) {
return style; return style;
} }
for (let prev = 0, i = 1; i < baseTokens.length; i += 2) { for (let prev = 0, i = 1; i < baseTokens.length; i += 2) {
const end = baseTokens[i]; const end = baseTokens[i];
if (prev < start && start < end) { if (prev <= start && start <= end) {
const base = baseTokens[i + 1]; const base = baseTokens[i + 1];
if (base && base.includes('colorview')) { if (base && base.includes('colorview')) {
return style + COLORVIEW_DISABLED_SUFFIX; return style +
(start > prev ? COLORVIEW_DISABLED_SUFFIX : '') +
(pos < end ? COLORVIEW_NEXT_DISABLED_SUFFIX : '');
} }
} else if (end > pos) { } else if (end > pos) {
break; break;

View File

@ -28,7 +28,10 @@
function addOverlayForHighlighter(overlay) { function addOverlayForHighlighter(overlay) {
const state = this.state.matchHighlighter || {}; const state = this.state.matchHighlighter || {};
const helper = state.stylusMHLHelper || {}; const helper = state.highlightHelper = state.highlightHelper || {};
clearTimeout(helper.hookTimer);
if (helper.matchesonscroll) { if (helper.matchesonscroll) {
// restore the original addon's unwanted removeOverlay effects // restore the original addon's unwanted removeOverlay effects
// (in case the token under cursor hasn't changed) // (in case the token under cursor hasn't changed)
@ -38,22 +41,22 @@
helper.overlay = null; helper.overlay = null;
return true; return true;
} }
if (overlay.token !== tokenHook) { if (overlay.token !== tokenHook) {
overlay.stylusMHLHelper = { overlay.highlightHelper = {
token: overlay.token, token: overlay.token,
occurrences: 0, occurrences: 0,
}; };
overlay.token = tokenHook; overlay.token = tokenHook;
} }
clearTimeout(helper.hookTimer);
} }
function tokenHook(stream) { function tokenHook(stream) {
const style = this.stylusMHLHelper.token.call(this, stream); const style = this.highlightHelper.token.call(this, stream);
if (style !== 'matchhighlight') { if (style !== 'matchhighlight') {
return style; return style;
} }
const num = ++this.stylusMHLHelper.occurrences; const num = ++this.highlightHelper.occurrences;
if (num === 1) { if (num === 1) {
stream.lineOracle.doc.cm.display.wrapper.classList.remove(HL_APPROVED); stream.lineOracle.doc.cm.display.wrapper.classList.remove(HL_APPROVED);
} else if (num === 2) { } else if (num === 2) {
@ -64,38 +67,54 @@
function removeOverlayForHighlighter() { function removeOverlayForHighlighter() {
const state = this.state.matchHighlighter || {}; const state = this.state.matchHighlighter || {};
const {query} = state.matchesonscroll || {}; const {query} = state.highlightHelper || state.matchesonscroll || {};
if (!query) { if (!query) {
return; return;
} }
const {line, ch} = this.getCursor();
const rx = query instanceof RegExp && query; const rx = query instanceof RegExp && query;
const sel = this.getSelection();
if (sel && (rx && !rx.test(sel) || sel.toLowerCase() !== query)) {
return;
}
if (!sel) {
const {line, ch} = this.getCursor();
const queryLen = rx ? rx.source.length - 4 : query.length; const queryLen = rx ? rx.source.length - 4 : query.length;
const start = Math.max(0, ch - queryLen + 1); const start = Math.max(0, ch - queryLen + 1);
const end = ch + queryLen; const end = ch + queryLen;
const area = this.getLine(line).substring(start, end); const area = this.getLine(line).substring(start, end);
const startInArea = rx ? (area.match(rx) || {}).index : const startInArea = rx ? (area.match(rx) || {}).index :
(area.indexOf(query) + 1 || NaN) - 1; (area.indexOf(query) + 1 || NaN) - 1;
if (start + startInArea <= ch) { if (start + startInArea > ch) {
return;
}
}
// same token on cursor => prevent the highlighter from rerunning // same token on cursor => prevent the highlighter from rerunning
state.stylusMHLHelper = { state.highlightHelper = {
overlay: state.overlay, overlay: state.overlay,
matchesonscroll: state.matchesonscroll, matchesonscroll: state.matchesonscroll,
showMatchesOnScrollbar: this.showMatchesOnScrollbar,
hookTimer: setTimeout(removeOverlayIfExpired, 0, this, state), hookTimer: setTimeout(removeOverlayIfExpired, 0, this, state),
}; };
state.matchesonscroll = null; state.matchesonscroll = null;
this.showMatchesOnScrollbar = scrollbarForHighlighter;
return true; return true;
} }
}
function removeOverlayIfExpired(self, state) { function removeOverlayIfExpired(self, state) {
const {overlay, matchesonscroll} = state.stylusMHLHelper || {}; const {overlay, matchesonscroll} = state.highlightHelper || {};
if (overlay) { if (overlay) {
originalRemoveOverlay.call(self, overlay); originalRemoveOverlay.call(self, overlay);
} }
if (matchesonscroll) { if (matchesonscroll) {
matchesonscroll.clear(); matchesonscroll.clear();
} }
state.stylusMHLHelper = null; self.showMatchesOnScrollbar = state.showMatchesOnScrollbar;
state.highlightHelper = null;
}
function scrollbarForHighlighter(query) {
const helper = this.state.matchHighlighter.highlightHelper;
this.showMatchesOnScrollbar = helper.showMatchesOnScrollbar;
helper.query = query;
} }
})(); })();

View File

@ -16,6 +16,9 @@
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAJElEQVQYV2NctWrVfwYkEBYWxojMZ6SDAmT7QGx0K1EcRBsFAADeG/3M/HteAAAAAElFTkSuQmCC"); background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAJElEQVQYV2NctWrVfwYkEBYWxojMZ6SDAmT7QGx0K1EcRBsFAADeG/3M/HteAAAAAElFTkSuQmCC");
background-repeat: repeat; background-repeat: repeat;
} }
.cm-colorview-next-disabled + .cm-colorview::before {
content: none;
}
.codemirror-colorview-background { .codemirror-colorview-background {
position: absolute; position: absolute;

View File

@ -177,7 +177,6 @@
if (el.colorpickerData && el.colorpickerData.color === data.color) { if (el.colorpickerData && el.colorpickerData.color === data.color) {
continue; continue;
} }
el.dataset.colorpicker = '';
el.colorpickerData = Object.assign({line, ch: start}, data); el.colorpickerData = Object.assign({line, ch: start}, data);
let bg = el.firstElementChild; let bg = el.firstElementChild;
if (!bg) { if (!bg) {