diff --git a/vendors/squire/build/squire-raw.js b/vendors/squire/build/squire-raw.js index cc1640391..8f144c07d 100644 --- a/vendors/squire/build/squire-raw.js +++ b/vendors/squire/build/squire-raw.js @@ -17,6 +17,7 @@ const DOCUMENT_FRAGMENT_NODE = 11, // Node.DOCUMENT_FRAGMENT_NODE, SHOW_ELEMENT = 1, // NodeFilter.SHOW_ELEMENT, SHOW_TEXT = 4, // NodeFilter.SHOW_TEXT, + SHOW_ELEMENT_OR_TEXT = 5, START_TO_START = 0, // Range.START_TO_START START_TO_END = 1, // Range.START_TO_END @@ -1034,7 +1035,7 @@ const }, newContentWalker = root => createTreeWalker( root, - SHOW_TEXT|SHOW_ELEMENT, + SHOW_ELEMENT_OR_TEXT, node => node.nodeType === TEXT_NODE ? notWS.test( node.data ) : node.nodeName === 'IMG' ), @@ -1206,7 +1207,7 @@ const // Save undo checkpoint and add any links in the preceding section. // Remove any zws so we don't think there's content in an empty // block. - self._recordUndoState( range ); + self._recordUndoState( range, false ); if ( self._config.addLinks ) { addLinks( range.startContainer, root, self ); } @@ -1505,7 +1506,7 @@ const while ( isInline( nonInlineParent ) ) { nonInlineParent = nonInlineParent.parentNode; } - let walker = createTreeWalker( nonInlineParent, SHOW_TEXT|SHOW_ELEMENT ); + let walker = createTreeWalker( nonInlineParent, SHOW_ELEMENT_OR_TEXT ); for ( i = 0, l = children.length; i < l; ++i ) { child = children[i]; @@ -1612,7 +1613,7 @@ const while ( isInline( block ) ) { block = block.parentNode; } - walker = createTreeWalker( block, SHOW_ELEMENT|SHOW_TEXT, notWSTextNode ); + walker = createTreeWalker( block, SHOW_ELEMENT_OR_TEXT, notWSTextNode ); walker.currentNode = br; return !!walker.nextNode() || ( isLBIfEmptyBlock && !walker.previousNode() ); @@ -1929,7 +1930,7 @@ let contentWalker, TreeWalker.prototype.previousPONode = function () { let current = this.currentNode, root = this.root, - nodeType = this.nodeType, + nodeType = this.nodeType, // whatToShow? filter = this.filter, node; while ( current ) { @@ -2405,7 +2406,7 @@ let keyHandlers = { space: ( self, _, range ) => { let node; let root = self._root; - self._recordUndoState( range ); + self._recordUndoState( range, false ); if ( self._config.addLinks ) { addLinks( range.startContainer, root, self ); } @@ -2470,6 +2471,123 @@ keyHandlers[ ctrlKey + 'z' ] = mapKeyTo( 'undo' ); keyHandlers[ 'undo' ] = mapKeyTo( 'undo' ); keyHandlers[ ctrlKey + 'shift-z' ] = mapKeyTo( 'redo' ); +class EditStack extends Array +{ + constructor(squire) { + super(); + this.squire = squire; + this.index = -1; + this.inUndoState = false; + + this.threshold = -1; // -1 means no threshold + this.limit = -1; // -1 means no limit + } + + clear() { + this.index = -1; + this.length = 0; + } + + stateChanged ( /*canUndo, canRedo*/ ) { + this.squire.fireEvent( 'undoStateChange', { + canUndo: this.index > 0, + canRedo: this.index + 1 < this.length + }); + this.squire.fireEvent( 'input' ); + } + + docWasChanged () { + if ( this.inUndoState ) { + this.inUndoState = false; + this.stateChanged ( /*true, false*/ ); + } else + this.squire.fireEvent( 'input' ); + } + + // Leaves bookmark + recordUndoState ( range, replace ) { + replace = replace !== false && this.inUndoState; + // Don't record if we're already in an undo state + if ( !this.inUndoState || replace ) { + // Advance pointer to new position + let undoIndex = this.index; + let undoThreshold = this.threshold; + let undoLimit = this.limit; + let squire = this.squire; + let html; + + if ( !replace ) { + ++undoIndex; + } + undoIndex = Math.max(0, undoIndex); + + // Truncate stack if longer (i.e. if has been previously undone) + this.length = Math.min(undoIndex + 1, this.length); + + // Get data + if ( range ) { + squire._saveRangeToBookmark( range ); + } + html = squire._getHTML(); + + // If this document is above the configured size threshold, + // limit the number of saved undo states. + // Threshold is in bytes, JS uses 2 bytes per character + if ( undoThreshold > -1 && html.length * 2 > undoThreshold + && undoLimit > -1 && undoIndex > undoLimit ) { + this.splice( 0, undoIndex - undoLimit ); + } + + // Save data + this[ undoIndex ] = html; + this.index = undoIndex; + this.inUndoState = true; + } + } + + saveUndoState ( range ) { + let squire = this.squire; + if ( range === undefined ) { + range = squire.getSelection(); + } + this.recordUndoState( range ); + squire._getRangeAndRemoveBookmark( range ); + } + + undo () { + let squire = this.squire, + undoIndex = this.index - 1; + // Sanity check: must not be at beginning of the history stack + if ( undoIndex >= 0 || !this.inUndoState ) { + // Make sure any changes since last checkpoint are saved. + this.recordUndoState( squire.getSelection(), false ); + this.index = undoIndex; + squire._setHTML( this[ undoIndex ] ); + let range = squire._getRangeAndRemoveBookmark(); + if ( range ) { + squire.setSelection( range ); + } + this.inUndoState = true; + this.stateChanged ( /*undoIndex > 0, true*/ ); + } + } + + redo () { + // Sanity check: must not be at end of stack and must be in an undo state. + let squire = this.squire, + undoIndex = this.index + 1; + if ( undoIndex < this.length && this.inUndoState ) { + this.index = undoIndex; + squire._setHTML( this[ undoIndex ] ); + let range = squire._getRangeAndRemoveBookmark(); + if ( range ) { + squire.setSelection( range ); + } + this.stateChanged ( /*true, undoIndex + 1 < this.length*/ ); + } + } +} + class Squire { constructor (root, config) { @@ -2501,10 +2619,7 @@ class Squire }; this.addEventListener('selectstart', () => this.addEventListener('selectionchange', selectionchange)); - this._undoIndex = -1; - this._undoStack = []; - this._undoStackLength = 0; - this._isInUndoState = false; + this.editStack = new EditStack(this); this._ignoreChange = false; this._ignoreAllChanges = false; @@ -2558,10 +2673,6 @@ class Squire setConfig ( config ) { config = mergeObjects({ blockTag: 'DIV', - undo: { - documentSizeThreshold: -1, // -1 means no threshold - undoLimit: -1 // -1 means no limit - }, addLinks: true }, config, true ); @@ -2663,24 +2774,6 @@ class Squire return this; } - destroy () { - let events = this._events; - let type; - - for ( type in events ) { - this.removeEventListener( type ); - } - if ( this._mutation ) { - this._mutation.disconnect(); - } - delete this._root.__squire__; - - // Destroy undo stack - this._undoIndex = -1; - this._undoStack = []; - this._undoStackLength = 0; - } - handleEvent ( event ) { this.fireEvent( event.type, event ); } @@ -2849,7 +2942,7 @@ class Squire } let walker = createTreeWalker( range.commonAncestorContainer, - SHOW_TEXT|SHOW_ELEMENT, + SHOW_ELEMENT_OR_TEXT, node => isNodeContainedInRange( range, node ) ); let startContainer = range.startContainer; @@ -3024,121 +3117,29 @@ class Squire _docWasChanged () { nodeCategoryCache = new WeakMap(); - if ( this._ignoreAllChanges ) { - return; + if ( !this._ignoreAllChanges ) { + if ( this._ignoreChange ) { + this._ignoreChange = false; + } else { + this.editStack.docWasChanged(); + } } - - if ( this._ignoreChange ) { - this._ignoreChange = false; - return; - } - if ( this._isInUndoState ) { - this._isInUndoState = false; - this.fireEvent( 'undoStateChange', { - canUndo: true, - canRedo: false - }); - } - this.fireEvent( 'input' ); } - // Leaves bookmark _recordUndoState ( range, replace ) { - // Don't record if we're already in an undo state - if ( !this._isInUndoState|| replace ) { - // Advance pointer to new position - let undoIndex = this._undoIndex; - let undoStack = this._undoStack; - let undoConfig = this._config.undo; - let undoThreshold = undoConfig.documentSizeThreshold; - let undoLimit = undoConfig.undoLimit; - let html; - - if ( !replace ) { - ++undoIndex; - } - - // Truncate stack if longer (i.e. if has been previously undone) - if ( undoIndex < this._undoStackLength ) { - undoStack.length = this._undoStackLength = undoIndex; - } - - // Get data - if ( range ) { - this._saveRangeToBookmark( range ); - } - html = this._getHTML(); - - // If this document is above the configured size threshold, - // limit the number of saved undo states. - // Threshold is in bytes, JS uses 2 bytes per character - if ( undoThreshold > -1 && html.length * 2 > undoThreshold ) { - if ( undoLimit > -1 && undoIndex > undoLimit ) { - undoStack.splice( 0, undoIndex - undoLimit ); - undoIndex = undoLimit; - this._undoStackLength = undoLimit; - } - } - - // Save data - undoStack[ undoIndex ] = html; - this._undoIndex = undoIndex; - ++this._undoStackLength; - this._isInUndoState = true; - } + this.editStack.recordUndoState( range, replace ); } saveUndoState ( range ) { - if ( range === undefined ) { - range = this.getSelection(); - } - this._recordUndoState( range, this._isInUndoState ); - this._getRangeAndRemoveBookmark( range ); - - return this; + this.editStack.saveUndoState( range ); } undo () { - // Sanity check: must not be at beginning of the history stack - if ( this._undoIndex !== 0 || !this._isInUndoState ) { - // Make sure any changes since last checkpoint are saved. - this._recordUndoState( this.getSelection(), false ); - - --this._undoIndex; - this._setHTML( this._undoStack[ this._undoIndex ] ); - let range = this._getRangeAndRemoveBookmark(); - if ( range ) { - this.setSelection( range ); - } - this._isInUndoState = true; - this.fireEvent( 'undoStateChange', { - canUndo: this._undoIndex !== 0, - canRedo: true - }); - this.fireEvent( 'input' ); - } - return this; + this.editStack.undo(); } redo () { - // Sanity check: must not be at end of stack and must be in an undo - // state. - let undoIndex = this._undoIndex, - undoStackLength = this._undoStackLength; - if ( undoIndex + 1 < undoStackLength && this._isInUndoState ) { - ++this._undoIndex; - this._setHTML( this._undoStack[ this._undoIndex ] ); - let range = this._getRangeAndRemoveBookmark(); - if ( range ) { - this.setSelection( range ); - } - this.fireEvent( 'undoStateChange', { - canUndo: true, - canRedo: undoIndex + 2 < undoStackLength - }); - this.fireEvent( 'input' ); - } - return this; + this.editStack.redo(); } // --- Inline formatting --- @@ -3282,7 +3283,7 @@ class Squire // them, and adding other styles is harmless. walker = createTreeWalker( range.commonAncestorContainer, - SHOW_TEXT|SHOW_ELEMENT, + SHOW_ELEMENT_OR_TEXT, node => ( node.nodeType === TEXT_NODE || node.nodeName === 'BR' || node.nodeName === 'IMG' @@ -3515,7 +3516,7 @@ class Squire } // 1. Save undo checkpoint and bookmark selection - this._recordUndoState( range, this._isInUndoState ); + this._recordUndoState( range ); let root = this._root; let frag; @@ -3563,7 +3564,7 @@ class Squire } // Save undo checkpoint and bookmark selection - this._recordUndoState( range, this._isInUndoState ); + this._recordUndoState( range ); // Increase list depth let type = list.nodeName; @@ -3613,7 +3614,7 @@ class Squire } // Save undo checkpoint and bookmark selection - this._recordUndoState( range, this._isInUndoState ); + this._recordUndoState( range ); if ( startLi ) { // Find the new parent list node @@ -3670,13 +3671,6 @@ class Squire } } - // --- Keyboard interaction --- - - setKeyHandler ( key, fn ) { - this._keyHandlers[ key ] = fn; - return this; - } - // --- Get/Set data --- _getHTML () { @@ -3751,10 +3745,7 @@ class Squire fixCursor( root, root ); // Reset the undo stack - this._undoIndex = -1; - this._undoStack.length = 0; - this._undoStackLength = 0; - this._isInUndoState = false; + this.editStack.clear(); // Record undo state let range = this._getRangeAndRemoveBookmark() ||