From eda1472fb0813455cec38af296a24057b254c29d Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Sat, 2 Jun 2018 15:02:55 -0700 Subject: [PATCH] update parchment to support multiple registries closes #1101 --- blots/block.js | 15 ++++++++------- blots/cursor.js | 13 +++++-------- blots/embed.js | 10 +++++----- blots/inline.js | 4 ++-- blots/scroll.js | 29 +++++++++-------------------- core.js | 3 --- core/editor.js | 8 +++----- core/quill.js | 22 +++++++++++++--------- core/selection.js | 11 +++++------ formats/align.js | 8 ++------ formats/background.js | 6 +++--- formats/color.js | 6 +++--- formats/direction.js | 8 ++------ formats/font.js | 4 ++-- formats/indent.js | 4 ++-- formats/list.js | 6 +++--- formats/size.js | 6 +++--- formats/table.js | 9 ++++----- modules/clipboard.js | 31 +++++++++++++++++-------------- modules/history.js | 12 ++++++------ modules/keyboard.js | 14 ++++---------- modules/toolbar.js | 26 +++++++++++--------------- package.json | 2 +- test/helpers/unit.js | 4 ++-- test/unit/blots/block.js | 7 ++++--- test/unit/blots/scroll.js | 14 -------------- test/unit/modules/history.js | 21 +++++++++++---------- 27 files changed, 130 insertions(+), 173 deletions(-) diff --git a/blots/block.js b/blots/block.js index 54be6efcf9..5fad9ce8ef 100644 --- a/blots/block.js +++ b/blots/block.js @@ -1,10 +1,11 @@ import extend from 'extend'; import Delta from 'quill-delta'; -import Parchment, { +import { AttributorStore, BlockBlot, EmbedBlot, LeafBlot, + Scope, } from 'parchment'; import Break from './break'; import Inline from './inline'; @@ -13,8 +14,8 @@ import TextBlot from './text'; const NEWLINE_LENGTH = 1; class Block extends BlockBlot { - constructor(domNode) { - super(domNode); + constructor(scroll, domNode) { + super(scroll, domNode); this.cache = {}; } @@ -39,7 +40,7 @@ class Block extends BlockBlot { formatAt(index, length, name, value) { if (length <= 0) return; - if (Parchment.query(name, Parchment.Scope.BLOCK)) { + if (this.scroll.query(name, Scope.BLOCK)) { if (index + length === this.length()) { this.format(name, value); } @@ -147,7 +148,7 @@ class BlockEmbed extends EmbedBlot { } format(name, value) { - const attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE); + const attribute = this.scroll.query(name, Scope.BLOCK_ATTRIBUTE); if (attribute != null) { this.attributes.attribute(attribute, value); } @@ -159,7 +160,7 @@ class BlockEmbed extends EmbedBlot { insertAt(index, value, def) { if (typeof value === 'string' && value.endsWith('\n')) { - const block = Parchment.create(Block.blotName); + const block = this.scroll.create(Block.blotName); this.parent.insertBefore(block, index === 0 ? this : this.next); block.insertAt(0, value.slice(0, -1)); } else { @@ -167,7 +168,7 @@ class BlockEmbed extends EmbedBlot { } } } -BlockEmbed.scope = Parchment.Scope.BLOCK_BLOT; +BlockEmbed.scope = Scope.BLOCK_BLOT; // It is important for cursor behavior BlockEmbeds use tags that are block level elements function bubbleFormats(blot, formats = {}) { diff --git a/blots/cursor.js b/blots/cursor.js index e992c7bfa9..3f9524e925 100644 --- a/blots/cursor.js +++ b/blots/cursor.js @@ -1,4 +1,4 @@ -import Parchment, { EmbedBlot } from 'parchment'; +import { EmbedBlot, Scope } from 'parchment'; import TextBlot from './text'; class Cursor extends EmbedBlot { @@ -6,8 +6,8 @@ class Cursor extends EmbedBlot { return undefined; } - constructor(domNode, selection) { - super(domNode); + constructor(scroll, domNode, selection) { + super(scroll, domNode); this.selection = selection; this.textNode = document.createTextNode(Cursor.CONTENTS); this.domNode.appendChild(this.textNode); @@ -26,10 +26,7 @@ class Cursor extends EmbedBlot { } let target = this; let index = 0; - while ( - target != null && - target.statics.scope !== Parchment.Scope.BLOCK_BLOT - ) { + while (target != null && target.statics.scope !== Scope.BLOCK_BLOT) { index += target.offset(target.parent); target = target.parent; } @@ -94,7 +91,7 @@ class Cursor extends EmbedBlot { this.textNode.data = Cursor.CONTENTS; } else { this.textNode.data = text; - this.parent.insertBefore(Parchment.create(this.textNode), this); + this.parent.insertBefore(this.scroll.create(this.textNode), this); this.textNode = document.createTextNode(Cursor.CONTENTS); this.domNode.appendChild(this.textNode); } diff --git a/blots/embed.js b/blots/embed.js index 5b5ecb0c27..4d66120ba1 100644 --- a/blots/embed.js +++ b/blots/embed.js @@ -1,11 +1,11 @@ -import Parchment, { EmbedBlot } from 'parchment'; +import { EmbedBlot } from 'parchment'; import TextBlot from './text'; const GUARD_TEXT = '\uFEFF'; class Embed extends EmbedBlot { - constructor(node) { - super(node); + constructor(scroll, node) { + super(scroll, node); this.contentNode = document.createElement('span'); this.contentNode.setAttribute('contenteditable', false); Array.from(this.domNode.childNodes).forEach(childNode => { @@ -38,7 +38,7 @@ class Embed extends EmbedBlot { }; } else { textNode = document.createTextNode(text); - this.parent.insertBefore(Parchment.create(textNode), this); + this.parent.insertBefore(this.scroll.create(textNode), this); range = { startNode: textNode, startOffset: text.length, @@ -53,7 +53,7 @@ class Embed extends EmbedBlot { }; } else { textNode = document.createTextNode(text); - this.parent.insertBefore(Parchment.create(textNode), this.next); + this.parent.insertBefore(this.scroll.create(textNode), this.next); range = { startNode: textNode, startOffset: text.length, diff --git a/blots/inline.js b/blots/inline.js index 446a904761..cad61ac6d6 100644 --- a/blots/inline.js +++ b/blots/inline.js @@ -1,4 +1,4 @@ -import Parchment, { EmbedBlot, InlineBlot } from 'parchment'; +import { EmbedBlot, InlineBlot, Scope } from 'parchment'; import Break from './break'; import Text from './text'; @@ -19,7 +19,7 @@ class Inline extends InlineBlot { formatAt(index, length, name, value) { if ( Inline.compare(this.statics.blotName, name) < 0 && - Parchment.query(name, Parchment.Scope.BLOT) + this.scroll.query(name, Scope.BLOT) ) { const blot = this.isolate(index, length); if (value) { diff --git a/blots/scroll.js b/blots/scroll.js index 1681cd3f0d..1493dcd80c 100644 --- a/blots/scroll.js +++ b/blots/scroll.js @@ -1,4 +1,4 @@ -import Parchment, { ScrollBlot, ContainerBlot } from 'parchment'; +import { Scope, ScrollBlot, ContainerBlot } from 'parchment'; import Emitter from '../core/emitter'; import Block, { BlockEmbed } from './block'; import Break from './break'; @@ -9,15 +9,9 @@ function isLine(blot) { } class Scroll extends ScrollBlot { - constructor(domNode, config) { - super(domNode); - this.emitter = config.emitter; - if (Array.isArray(config.whitelist)) { - this.whitelist = config.whitelist.reduce((whitelist, format) => { - whitelist[format] = true; - return whitelist; - }, {}); - } + constructor(registry, domNode, { emitter }) { + super(registry, domNode); + this.emitter = emitter; // Some reason fixes composition issues with character languages in Windows/Chrome, Safari this.domNode.addEventListener('DOMNodeInserted', () => {}); this.optimize(); @@ -63,19 +57,14 @@ class Scroll extends ScrollBlot { } formatAt(index, length, format, value) { - if (this.whitelist != null && !this.whitelist[format]) return; super.formatAt(index, length, format, value); this.optimize(); } insertAt(index, value, def) { - if (def != null && this.whitelist != null && !this.whitelist[value]) return; if (index >= this.length()) { - if ( - def == null || - Parchment.query(value, Parchment.Scope.BLOCK) == null - ) { - const blot = Parchment.create(this.statics.defaultChild.blotName); + if (def == null || this.scroll.query(value, Scope.BLOCK) == null) { + const blot = this.scroll.create(this.statics.defaultChild.blotName); this.appendChild(blot); if (def == null && value.endsWith('\n')) { blot.insertAt(0, value.slice(0, -1), def); @@ -83,7 +72,7 @@ class Scroll extends ScrollBlot { blot.insertAt(0, value, def); } } else { - const embed = Parchment.create(value, def); + const embed = this.scroll.create(value, def); this.appendChild(embed); } } else { @@ -93,8 +82,8 @@ class Scroll extends ScrollBlot { } insertBefore(blot, ref) { - if (blot.statics.scope === Parchment.Scope.INLINE_BLOT) { - const wrapper = Parchment.create(this.statics.defaultChild.blotName); + if (blot.statics.scope === Scope.INLINE_BLOT) { + const wrapper = this.scroll.create(this.statics.defaultChild.blotName); wrapper.appendChild(blot); super.insertBefore(wrapper, ref); } else { diff --git a/core.js b/core.js index 5f85d704a3..052ad45935 100644 --- a/core.js +++ b/core.js @@ -1,4 +1,3 @@ -import Parchment from 'parchment'; import Quill from './core/quill'; import Block, { BlockEmbed } from './blots/block'; @@ -32,6 +31,4 @@ Quill.register({ 'modules/uploader': Uploader, }); -Parchment.register(Block, Break, Cursor, Inline, Scroll, TextBlot); - export default Quill; diff --git a/core/editor.js b/core/editor.js index 1143bff43b..b55c002c38 100644 --- a/core/editor.js +++ b/core/editor.js @@ -3,7 +3,7 @@ import equal from 'deep-equal'; import extend from 'extend'; import Delta from 'quill-delta'; import DeltaOp from 'quill-delta/lib/op'; -import Parchment, { LeafBlot } from 'parchment'; +import { LeafBlot } from 'parchment'; import CursorBlot from '../blots/cursor'; import Block, { bubbleFormats } from '../blots/block'; import Break from '../blots/break'; @@ -75,8 +75,6 @@ class Editor { formatLine(index, length, formats = {}) { this.scroll.update(); Object.keys(formats).forEach(format => { - if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) - return; this.scroll.lines(index, Math.max(length, 1)).forEach(line => { line.format(format, formats[format]); }); @@ -187,10 +185,10 @@ class Editor { mutations.length === 1 && mutations[0].type === 'characterData' && mutations[0].target.data.match(ASCII) && - Parchment.find(mutations[0].target) + this.scroll.find(mutations[0].target) ) { // Optimization for character changes - const textBlot = Parchment.find(mutations[0].target); + const textBlot = this.scroll.find(mutations[0].target); const formats = bubbleFormats(textBlot); const index = textBlot.offset(this.scroll); const oldValue = mutations[0].oldValue.replace(CursorBlot.CONTENTS, ''); diff --git a/core/quill.js b/core/quill.js index b32a50fad9..8a9c01c02e 100644 --- a/core/quill.js +++ b/core/quill.js @@ -1,5 +1,5 @@ import Delta from 'quill-delta'; -import Parchment from 'parchment'; +import * as Parchment from 'parchment'; import extend from 'extend'; import Editor from './editor'; import Emitter from './emitter'; @@ -11,6 +11,8 @@ import Theme from './theme'; const debug = logger('quill'); +const globalRegistry = new Parchment.Registry(); + class Quill { static debug(limit) { if (limit === true) { @@ -20,7 +22,7 @@ class Quill { } static find(node) { - return instances.get(node) || Parchment.find(node); + return instances.get(node) || globalRegistry.find(node); } static import(name) { @@ -50,10 +52,10 @@ class Quill { (path.startsWith('blots/') || path.startsWith('formats/')) && target.blotName !== 'abstract' ) { - Parchment.register(target); + globalRegistry.register(target); } if (typeof target.register === 'function') { - target.register(); + target.register(globalRegistry); } } } @@ -79,9 +81,11 @@ class Quill { this.root.setAttribute('data-gramm', false); this.scrollingContainer = this.options.scrollingContainer || this.root; this.emitter = new Emitter(); - this.scroll = Parchment.create(this.root, { + const ScrollBlot = this.options.registry.query( + Parchment.ScrollBlot.blotName, + ); + this.scroll = new ScrollBlot(this.options.registry, this.root, { emitter: this.emitter, - whitelist: this.options.formats, }); this.editor = new Editor(this.scroll); this.selection = new Selection(this.scroll, this.emitter); @@ -169,7 +173,7 @@ class Quill { let change = new Delta(); if (range == null) { return change; - } else if (Parchment.query(name, Parchment.Scope.BLOCK)) { + } else if (this.scroll.query(name, Parchment.Scope.BLOCK)) { change = this.editor.formatLine(range.index, range.length, { [name]: value, }); @@ -420,10 +424,10 @@ class Quill { } Quill.DEFAULTS = { bounds: null, - formats: null, modules: {}, placeholder: '', readOnly: false, + registry: globalRegistry, scrollingContainer: null, theme: 'default', }; @@ -608,4 +612,4 @@ function shiftRange(range, index, length, source) { return new Range(start, end - start); } -export { expandConfig, overload, Quill as default }; +export { globalRegistry, expandConfig, overload, Quill as default }; diff --git a/core/selection.js b/core/selection.js index 2bef5cb950..80c42cc6d0 100644 --- a/core/selection.js +++ b/core/selection.js @@ -1,4 +1,4 @@ -import Parchment, { LeafBlot, ContainerBlot } from 'parchment'; +import { ContainerBlot, LeafBlot, Scope } from 'parchment'; import clone from 'clone'; import equal from 'deep-equal'; import Emitter from './emitter'; @@ -20,7 +20,7 @@ class Selection { this.composing = false; this.mouseDown = false; this.root = this.scroll.domNode; - this.cursor = Parchment.create('cursor', this); + this.cursor = this.scroll.create('cursor', this); // savedRange is last non-null range this.savedRange = new Range(0, 0); this.lastRange = this.savedRange; @@ -98,17 +98,16 @@ class Selection { } format(format, value) { - if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) return; this.scroll.update(); const nativeRange = this.getNativeRange(); if ( nativeRange == null || !nativeRange.native.collapsed || - Parchment.query(format, Parchment.Scope.BLOCK) + this.scroll.query(format, Scope.BLOCK) ) return; if (nativeRange.start.node !== this.cursor.textNode) { - const blot = Parchment.find(nativeRange.start.node, false); + const blot = this.scroll.find(nativeRange.start.node, false); if (blot == null) return; // TODO Give blot ability to not split if (blot instanceof LeafBlot) { @@ -199,7 +198,7 @@ class Selection { } const indexes = positions.map(position => { const [node, offset] = position; - const blot = Parchment.find(node, true); + const blot = this.scroll.find(node, true); const index = blot.offset(this.scroll); if (offset === 0) { return index; diff --git a/formats/align.js b/formats/align.js index 20e1c71d52..65f1bf82bf 100644 --- a/formats/align.js +++ b/formats/align.js @@ -1,11 +1,7 @@ -import Parchment, { - Attributor, - ClassAttributor, - StyleAttributor, -} from 'parchment'; +import { Attributor, ClassAttributor, Scope, StyleAttributor } from 'parchment'; const config = { - scope: Parchment.Scope.BLOCK, + scope: Scope.BLOCK, whitelist: ['right', 'center', 'justify'], }; diff --git a/formats/background.js b/formats/background.js index df858ff779..07c764d0ff 100644 --- a/formats/background.js +++ b/formats/background.js @@ -1,11 +1,11 @@ -import Parchment, { ClassAttributor } from 'parchment'; +import { ClassAttributor, Scope } from 'parchment'; import { ColorAttributor } from './color'; const BackgroundClass = new ClassAttributor('background', 'ql-bg', { - scope: Parchment.Scope.INLINE, + scope: Scope.INLINE, }); const BackgroundStyle = new ColorAttributor('background', 'background-color', { - scope: Parchment.Scope.INLINE, + scope: Scope.INLINE, }); export { BackgroundClass, BackgroundStyle }; diff --git a/formats/color.js b/formats/color.js index a7628e2793..eb5f6ef391 100644 --- a/formats/color.js +++ b/formats/color.js @@ -1,4 +1,4 @@ -import Parchment, { ClassAttributor, StyleAttributor } from 'parchment'; +import { ClassAttributor, Scope, StyleAttributor } from 'parchment'; class ColorAttributor extends StyleAttributor { value(domNode) { @@ -14,10 +14,10 @@ class ColorAttributor extends StyleAttributor { } const ColorClass = new ClassAttributor('color', 'ql-color', { - scope: Parchment.Scope.INLINE, + scope: Scope.INLINE, }); const ColorStyle = new ColorAttributor('color', 'color', { - scope: Parchment.Scope.INLINE, + scope: Scope.INLINE, }); export { ColorAttributor, ColorClass, ColorStyle }; diff --git a/formats/direction.js b/formats/direction.js index 4c8ce69a58..24fcd82542 100644 --- a/formats/direction.js +++ b/formats/direction.js @@ -1,11 +1,7 @@ -import Parchment, { - Attributor, - ClassAttributor, - StyleAttributor, -} from 'parchment'; +import { Attributor, ClassAttributor, Scope, StyleAttributor } from 'parchment'; const config = { - scope: Parchment.Scope.BLOCK, + scope: Scope.BLOCK, whitelist: ['rtl'], }; diff --git a/formats/font.js b/formats/font.js index d9d8551ada..95b4764846 100644 --- a/formats/font.js +++ b/formats/font.js @@ -1,7 +1,7 @@ -import Parchment, { ClassAttributor, StyleAttributor } from 'parchment'; +import { ClassAttributor, Scope, StyleAttributor } from 'parchment'; const config = { - scope: Parchment.Scope.INLINE, + scope: Scope.INLINE, whitelist: ['serif', 'monospace'], }; diff --git a/formats/indent.js b/formats/indent.js index 89d66f5037..e41f80dd40 100644 --- a/formats/indent.js +++ b/formats/indent.js @@ -1,4 +1,4 @@ -import Parchment, { ClassAttributor } from 'parchment'; +import { ClassAttributor, Scope } from 'parchment'; class IndentAttributor extends ClassAttributor { add(node, value) { @@ -23,7 +23,7 @@ class IndentAttributor extends ClassAttributor { } const IndentClass = new IndentAttributor('indent', 'ql-indent', { - scope: Parchment.Scope.BLOCK, + scope: Scope.BLOCK, whitelist: [1, 2, 3, 4, 5, 6, 7, 8], }); diff --git a/formats/list.js b/formats/list.js index 4aef663bd7..40e2e2be5a 100644 --- a/formats/list.js +++ b/formats/list.js @@ -21,11 +21,11 @@ class ListItem extends Block { Quill.register(ListContainer); } - constructor(domNode) { - super(domNode); + constructor(scroll, domNode) { + super(scroll, domNode); const listEventHandler = e => { if (e.target !== domNode) return; - const format = this.statics.formats(domNode); + const format = this.statics.formats(domNode, scroll); if (format === 'checked') { this.format('list', 'unchecked'); } else if (format === 'unchecked') { diff --git a/formats/size.js b/formats/size.js index b7677d8ece..1f84a6f4e4 100644 --- a/formats/size.js +++ b/formats/size.js @@ -1,11 +1,11 @@ -import Parchment, { ClassAttributor, StyleAttributor } from 'parchment'; +import { ClassAttributor, Scope, StyleAttributor } from 'parchment'; const SizeClass = new ClassAttributor('size', 'ql-size', { - scope: Parchment.Scope.INLINE, + scope: Scope.INLINE, whitelist: ['small', 'large', 'huge'], }); const SizeStyle = new StyleAttributor('size', 'font-size', { - scope: Parchment.Scope.INLINE, + scope: Scope.INLINE, whitelist: ['10px', '18px', '32px'], }); diff --git a/formats/table.js b/formats/table.js index bcda0931c2..8d40b767d1 100644 --- a/formats/table.js +++ b/formats/table.js @@ -1,4 +1,3 @@ -import Parchment from 'parchment'; import Block from '../blots/block'; import Container from '../blots/container'; @@ -84,7 +83,7 @@ class TableContainer extends Container { if (row.children.head != null) { value = TableCell.formats(row.children.head.domNode); } - const blot = Parchment.create(TableCell.blotName, value); + const blot = this.scroll.create(TableCell.blotName, value); row.appendChild(blot); blot.optimize(); // Add break blot }); @@ -107,7 +106,7 @@ class TableContainer extends Container { if (body == null || body.children.head == null) return; body.children.forEach(row => { const ref = row.children.at(index); - const cell = Parchment.create(TableCell.blotName); + const cell = this.scroll.create(TableCell.blotName); row.insertBefore(cell, ref); }); } @@ -115,9 +114,9 @@ class TableContainer extends Container { insertRow(index) { const [body] = this.descendant(TableBody); if (body == null || body.children.head == null) return; - const row = Parchment.create(TableRow.blotName); + const row = this.scroll.create(TableRow.blotName); body.children.head.children.forEach(() => { - const cell = Parchment.create(TableCell.blotName); + const cell = this.scroll.create(TableCell.blotName); row.appendChild(cell); }); const ref = body.children.at(index); diff --git a/modules/clipboard.js b/modules/clipboard.js index f5e32744cc..6b4905dea1 100644 --- a/modules/clipboard.js +++ b/modules/clipboard.js @@ -1,9 +1,10 @@ import extend from 'extend'; import Delta from 'quill-delta'; -import Parchment, { +import { Attributor, ClassAttributor, EmbedBlot, + Scope, StyleAttributor, } from 'parchment'; import Quill from '../core/quill'; @@ -92,6 +93,7 @@ class Clipboard extends Module { const nodeMatches = new WeakMap(); const [elementMatchers, textMatchers] = this.prepareMatching(nodeMatches); let delta = traverse( + this.quill.scroll, this.container, elementMatchers, textMatchers, @@ -255,15 +257,16 @@ function isLine(node) { return ['block', 'list-item', 'table-cell'].indexOf(style.display) > -1; } -function traverse(node, elementMatchers, textMatchers, nodeMatches) { +function traverse(scroll, node, elementMatchers, textMatchers, nodeMatches) { // Post-order if (node.nodeType === node.TEXT_NODE) { return textMatchers.reduce((delta, matcher) => { - return matcher(node, delta); + return matcher(node, delta, scroll); }, new Delta()); } else if (node.nodeType === node.ELEMENT_NODE) { return Array.from(node.childNodes || []).reduce((delta, childNode) => { let childrenDelta = traverse( + scroll, childNode, elementMatchers, textMatchers, @@ -271,11 +274,11 @@ function traverse(node, elementMatchers, textMatchers, nodeMatches) { ); if (childNode.nodeType === node.ELEMENT_NODE) { childrenDelta = elementMatchers.reduce((reducedDelta, matcher) => { - return matcher(childNode, reducedDelta); + return matcher(childNode, reducedDelta, scroll); }, childrenDelta); childrenDelta = (nodeMatches.get(childNode) || []).reduce( (reducedDelta, matcher) => { - return matcher(childNode, reducedDelta); + return matcher(childNode, reducedDelta, scroll); }, childrenDelta, ); @@ -290,7 +293,7 @@ function matchAlias(format, node, delta) { return applyFormat(delta, format, true); } -function matchAttributor(node, delta) { +function matchAttributor(node, delta, scroll) { const attributes = Attributor.keys(node); const classes = ClassAttributor.keys(node); const styles = StyleAttributor.keys(node); @@ -299,7 +302,7 @@ function matchAttributor(node, delta) { .concat(classes) .concat(styles) .forEach(name => { - let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE); + let attr = scroll.query(name, Scope.ATTRIBUTE); if (attr != null) { formats[attr.attrName] = attr.value(node); if (formats[attr.attrName]) return; @@ -320,18 +323,18 @@ function matchAttributor(node, delta) { return delta; } -function matchBlot(node, delta) { - const match = Parchment.query(node); +function matchBlot(node, delta, scroll) { + const match = scroll.query(node); if (match == null) return delta; if (match.prototype instanceof EmbedBlot) { const embed = {}; const value = match.value(node); if (value != null) { embed[match.blotName] = value; - return new Delta().insert(embed, match.formats(node)); + return new Delta().insert(embed, match.formats(node, scroll)); } } else if (typeof match.formats === 'function') { - return applyFormat(delta, match.blotName, match.formats(node)); + return applyFormat(delta, match.blotName, match.formats(node, scroll)); } return delta; } @@ -352,8 +355,8 @@ function matchIgnore() { return new Delta(); } -function matchIndent(node, delta) { - const match = Parchment.query(node); +function matchIndent(node, delta, scroll) { + const match = scroll.query(node); if ( match == null || match.blotName !== 'list' || @@ -364,7 +367,7 @@ function matchIndent(node, delta) { let indent = -1; let parent = node.parentNode; while (!parent.classList.contains('ql-clipboard')) { - if ((Parchment.query(parent) || {}).blotName === 'list-container') { + if ((scroll.query(parent) || {}).blotName === 'list-container') { indent += 1; } parent = parent.parentNode; diff --git a/modules/history.js b/modules/history.js index 4df4d52f3c..312afdbc46 100644 --- a/modules/history.js +++ b/modules/history.js @@ -1,4 +1,4 @@ -import Parchment from 'parchment'; +import { Scope } from 'parchment'; import Quill from '../core/quill'; import Module from '../core/module'; @@ -43,7 +43,7 @@ class History extends Module { this.ignoreChange = true; this.quill.updateContents(delta[source], Quill.sources.USER); this.ignoreChange = false; - const index = getLastChangeIndex(delta[source]); + const index = getLastChangeIndex(this.quill.scroll, delta[source]); this.quill.setSelection(index); } @@ -104,7 +104,7 @@ History.DEFAULTS = { userOnly: false, }; -function endsWithNewlineChange(delta) { +function endsWithNewlineChange(scroll, delta) { const lastOp = delta.ops[delta.ops.length - 1]; if (lastOp == null) return false; if (lastOp.insert != null) { @@ -112,18 +112,18 @@ function endsWithNewlineChange(delta) { } if (lastOp.attributes != null) { return Object.keys(lastOp.attributes).some(attr => { - return Parchment.query(attr, Parchment.Scope.BLOCK) != null; + return scroll.query(attr, Scope.BLOCK) != null; }); } return false; } -function getLastChangeIndex(delta) { +function getLastChangeIndex(scroll, delta) { const deleteLength = delta.reduce((length, op) => { return length + (op.delete || 0); }, 0); let changeIndex = delta.length() - deleteLength; - if (endsWithNewlineChange(delta)) { + if (endsWithNewlineChange(scroll, delta)) { changeIndex -= 1; } return changeIndex; diff --git a/modules/keyboard.js b/modules/keyboard.js index d85b2147c9..34bc2b9d77 100644 --- a/modules/keyboard.js +++ b/modules/keyboard.js @@ -3,7 +3,7 @@ import equal from 'deep-equal'; import extend from 'extend'; import Delta from 'quill-delta'; import DeltaOp from 'quill-delta/lib/op'; -import Parchment, { EmbedBlot, TextBlot } from 'parchment'; +import { EmbedBlot, Scope, TextBlot } from 'parchment'; import Quill from '../core/quill'; import logger from '../core/logger'; import Module from '../core/module'; @@ -28,13 +28,6 @@ class Keyboard extends Module { super(quill, options); this.bindings = {}; Object.keys(this.options.bindings).forEach(name => { - if ( - name === 'list autofill' && - quill.scroll.whitelist != null && - !quill.scroll.whitelist.list - ) { - return; - } if (this.options.bindings[name]) { this.addBinding(this.options.bindings[name]); } @@ -313,6 +306,7 @@ Keyboard.DEFAULTS = { format: { list: false }, prefix: /^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/, handler(range, context) { + if (this.quill.scroll.query('list') == null) return true; const { length } = context.prefix; const [line, offset] = this.quill.getLine(range.index); if (offset > length) return true; @@ -461,7 +455,7 @@ function handleEnter(range, context) { } const lineFormats = Object.keys(context.format).reduce((formats, format) => { if ( - Parchment.query(format, Parchment.Scope.BLOCK) && + this.quill.scroll.query(format, Scope.BLOCK) && !Array.isArray(context.format[format]) ) { formats[format] = context.format[format]; @@ -487,7 +481,7 @@ function makeCodeBlockHandler(indent) { shiftKey: !indent, format: { 'code-block': true }, handler(range) { - const CodeBlock = Parchment.query('code-block'); + const CodeBlock = this.quill.scroll.query('code-block'); const lines = range.length === 0 ? this.quill.getLines(range.index, 1) diff --git a/modules/toolbar.js b/modules/toolbar.js index 2897da253b..a16923ec0a 100644 --- a/modules/toolbar.js +++ b/modules/toolbar.js @@ -1,5 +1,5 @@ import Delta from 'quill-delta'; -import Parchment, { EmbedBlot } from 'parchment'; +import { EmbedBlot, Scope } from 'parchment'; import Quill from '../core/quill'; import logger from '../core/logger'; import Module from '../core/module'; @@ -57,18 +57,12 @@ class Toolbar extends Module { if (input.tagName === 'BUTTON') { input.setAttribute('type', 'button'); } - if (this.handlers[format] == null) { - if ( - this.quill.scroll.whitelist != null && - this.quill.scroll.whitelist[format] == null - ) { - debug.warn('ignoring attaching to disabled format', format, input); - return; - } - if (Parchment.query(format) == null) { - debug.warn('ignoring attaching to nonexistent format', format, input); - return; - } + if ( + this.handlers[format] == null && + this.quill.scroll.query(format) == null + ) { + debug.warn('ignoring attaching to nonexistent format', format, input); + return; } const eventName = input.tagName === 'SELECT' ? 'change' : 'click'; input.addEventListener(eventName, e => { @@ -93,7 +87,9 @@ class Toolbar extends Module { const [range] = this.quill.selection.getRange(); if (this.handlers[format] != null) { this.handlers[format].call(this, value); - } else if (Parchment.query(format).prototype instanceof EmbedBlot) { + } else if ( + this.quill.scroll.query(format).prototype instanceof EmbedBlot + ) { value = prompt(`Enter ${format}`); // eslint-disable-line no-alert if (!value) return; this.quill.updateContents( @@ -212,7 +208,7 @@ Toolbar.DEFAULTS = { const formats = this.quill.getFormat(); Object.keys(formats).forEach(name => { // Clean functionality in existing apps only clean inline formats - if (Parchment.query(name, Parchment.Scope.INLINE) != null) { + if (this.quill.scroll.query(name, Scope.INLINE) != null) { this.quill.format(name, false, Quill.sources.USER); } }); diff --git a/package.json b/package.json index 332d2bea5c..2b0f3920a1 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "deep-equal": "^1.0.1", "eventemitter3": "^3.1.0", "extend": "^3.0.1", - "parchment": "quilljs/parchment#0928cfdd220bb2b1636583c1f667e07341816736", + "parchment": "quilljs/parchment#487850f7eb030a6c4e750ba809e58b09444e0bdb", "quill-delta": "^3.6.2" }, "devDependencies": { diff --git a/test/helpers/unit.js b/test/helpers/unit.js index b5afd45a3d..c11bbaaab9 100644 --- a/test/helpers/unit.js +++ b/test/helpers/unit.js @@ -3,7 +3,7 @@ import Editor from '../../core/editor'; import Emitter from '../../core/emitter'; import Selection from '../../core/selection'; import Scroll from '../../blots/scroll'; -import Quill from '../../core/quill'; +import Quill, { globalRegistry } from '../../core/quill'; const div = document.createElement('div'); div.id = 'test-container'; @@ -110,7 +110,7 @@ function initialize(klass, html, container = this.container) { if (klass === HTMLElement) return container; if (klass === Quill) return new Quill(container); const emitter = new Emitter(); - const scroll = new Scroll(container, { emitter }); + const scroll = new Scroll(globalRegistry, container, { emitter }); if (klass === Scroll) return scroll; if (klass === Editor) return new Editor(scroll); if (klass === Selection) return new Selection(scroll, emitter); diff --git a/test/unit/blots/block.js b/test/unit/blots/block.js index 5f2aaa09d2..eda24bf6d5 100644 --- a/test/unit/blots/block.js +++ b/test/unit/blots/block.js @@ -1,15 +1,16 @@ -import Parchment from 'parchment'; import Scroll from '../../../blots/scroll'; describe('Block', function() { it('childless', function() { - const block = Parchment.create('block'); + const scroll = this.initialize(Scroll, ''); + const block = scroll.create('block'); block.optimize(); expect(block.domNode).toEqualHTML('
'); }); it('insert into empty', function() { - const block = Parchment.create('block'); + const scroll = this.initialize(Scroll, ''); + const block = scroll.create('block'); block.insertAt(0, 'Test'); expect(block.domNode).toEqualHTML('Test'); }); diff --git a/test/unit/blots/scroll.js b/test/unit/blots/scroll.js index 8dcd2ae2ad..92926432ae 100644 --- a/test/unit/blots/scroll.js +++ b/test/unit/blots/scroll.js @@ -1,4 +1,3 @@ -import Parchment from 'parchment'; import Emitter from '../../../core/emitter'; import Selection, { Range } from '../../../core/selection'; import Cursor from '../../../blots/cursor'; @@ -40,19 +39,6 @@ describe('Scroll', function() { }, 1); }); - it('whitelist', function() { - const scroll = Parchment.create('scroll', { - emitter: new Emitter(), - whitelist: ['bold'], - }); - scroll.insertAt(0, 'Hello World!'); - scroll.formatAt(0, 5, 'bold', true); - scroll.formatAt(6, 5, 'italic', true); - expect(scroll.domNode.firstChild).toEqualHTML( - 'Hello World!', - ); - }); - describe('leaf()', function() { it('text', function() { const scroll = this.initialize(Scroll, '

Tests

'); diff --git a/test/unit/modules/history.js b/test/unit/modules/history.js index e554ac439a..d4e3156762 100644 --- a/test/unit/modules/history.js +++ b/test/unit/modules/history.js @@ -1,12 +1,13 @@ import Delta from 'quill-delta'; import Quill from '../../../core'; +import { globalRegistry } from '../../../core/quill'; import { getLastChangeIndex } from '../../../modules/history'; describe('History', function() { describe('getLastChangeIndex', function() { it('delete', function() { const delta = new Delta().retain(4).delete(2); - expect(getLastChangeIndex(delta)).toEqual(4); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(4); }); it('delete with inserts', function() { @@ -14,17 +15,17 @@ describe('History', function() { .retain(4) .insert('test') .delete(2); - expect(getLastChangeIndex(delta)).toEqual(8); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(8); }); it('insert text', function() { const delta = new Delta().retain(4).insert('testing'); - expect(getLastChangeIndex(delta)).toEqual(11); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(11); }); it('insert embed', function() { const delta = new Delta().retain(4).insert({ image: true }); - expect(getLastChangeIndex(delta)).toEqual(5); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(5); }); it('insert with deletes', function() { @@ -32,34 +33,34 @@ describe('History', function() { .retain(4) .delete(3) .insert('!'); - expect(getLastChangeIndex(delta)).toEqual(5); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(5); }); it('format', function() { const delta = new Delta().retain(4).retain(3, { bold: true }); - expect(getLastChangeIndex(delta)).toEqual(7); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(7); }); it('format newline', function() { const delta = new Delta().retain(4).retain(1, { align: 'left' }); - expect(getLastChangeIndex(delta)).toEqual(4); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(4); }); it('format mixed', function() { const delta = new Delta() .retain(4) .retain(1, { align: 'left', bold: true }); - expect(getLastChangeIndex(delta)).toEqual(4); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(4); }); it('insert newline', function() { const delta = new Delta().retain(4).insert('a\n'); - expect(getLastChangeIndex(delta)).toEqual(5); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(5); }); it('mutliple newline inserts', function() { const delta = new Delta().retain(4).insert('ab\n\n'); - expect(getLastChangeIndex(delta)).toEqual(7); + expect(getLastChangeIndex(globalRegistry, delta)).toEqual(7); }); });