@@ -3,22 +3,6 @@ const ELEMENT_NODE_TYPE = 1
33const TEXT_NODE_TYPE = 3
44const TREE_WALKER_SHOW_ELEMENT = 1
55
6- const IS_PARENT_NODE_TYPE = [
7- 0 , // 0: (unused)
8- 1 , // 1: Element
9- 0 , // 2: Attribute (deprecated)
10- 0 , // 3: Text
11- 0 , // 4: CDATASection (deprecated)
12- 0 , // 5: EntityReference (deprecated)
13- 0 , // 6: Entity (deprecated)
14- 0 , // 7: ProcessingInstruction
15- 0 , // 8: Comment
16- 1 , // 9: Document
17- 0 , // 10: DocumentType
18- 1 , // 11: DocumentFragment
19- 0 , // 12: Notation (deprecated)
20- ]
21-
226const Operation = {
237 EqualNode : 0 ,
248 SameElement : 1 ,
@@ -149,7 +133,7 @@ export function morphDocument(from: Document, to: Document | string, options?: O
149133export function morph ( from : ChildNode , to : ChildNode | NodeListOf < ChildNode > | string , options : Options = { } ) : void {
150134 if ( typeof to === "string" ) to = parseFragment ( to ) . childNodes
151135
152- if ( isParentNode ( from ) ) flagDirtyInputs ( from )
136+ if ( isElementNode ( from ) ) flagDirtyInputs ( from )
153137 new Morph ( options ) . morph ( from , to )
154138}
155139
@@ -175,34 +159,29 @@ export function morphInner(from: ChildNode, to: ChildNode | string, options: Opt
175159 }
176160 }
177161
178- if (
179- from . nodeType === ELEMENT_NODE_TYPE &&
180- to . nodeType === ELEMENT_NODE_TYPE &&
181- ( from as Element ) . localName === ( to as Element ) . localName
182- ) {
183- if ( isParentNode ( from ) ) flagDirtyInputs ( from )
184- new Morph ( options ) . visitChildNodes ( from as Element , to as Element )
162+ if ( isElementNode ( from ) && isElementNode ( to ) && from . localName === to . localName ) {
163+ flagDirtyInputs ( from )
164+ new Morph ( options ) . visitChildNodes ( from , to )
185165 } else {
186166 throw new Error ( "[Morphlex] You can only do an inner morph with matching elements." )
187167 }
188168}
189169
190- function flagDirtyInputs ( node : ParentNode ) : void {
191- if ( node . nodeType === ELEMENT_NODE_TYPE ) {
192- const element = node as Element
193- if ( isInputElement ( element ) ) {
194- if ( element . value !== element . defaultValue || element . checked !== element . defaultChecked ) {
195- element . setAttribute ( "morphlex-dirty" , "" )
196- }
197- } else if ( isOptionElement ( element ) ) {
198- if ( element . selected !== element . defaultSelected ) {
199- element . setAttribute ( "morphlex-dirty" , "" )
200- }
201- } else if ( element . localName === "textarea" ) {
202- const textarea = element as HTMLTextAreaElement
203- if ( textarea . value !== textarea . defaultValue ) {
204- textarea . setAttribute ( "morphlex-dirty" , "" )
205- }
170+
171+
172+ function flagDirtyInputs ( node : Element ) : void {
173+ if ( isInputElement ( node ) ) {
174+ if ( node . value !== node . defaultValue || node . checked !== node . defaultChecked ) {
175+ node . setAttribute ( "morphlex-dirty" , "" )
176+ }
177+ } else if ( isOptionElement ( node ) ) {
178+ if ( node . selected !== node . defaultSelected ) {
179+ node . setAttribute ( "morphlex-dirty" , "" )
180+ }
181+ } else if ( node . localName === "textarea" ) {
182+ const textarea = node as HTMLTextAreaElement
183+ if ( textarea . value !== textarea . defaultValue ) {
184+ textarea . setAttribute ( "morphlex-dirty" , "" )
206185 }
207186 }
208187
@@ -238,9 +217,13 @@ function parseDocument(string: string): Document {
238217}
239218
240219function moveBefore ( parent : ParentNode , node : ChildNode , insertionPoint : ChildNode | null ) : void {
220+ /* v8 ignore if -- @preserve */
241221 if ( node === insertionPoint ) return
222+ /* v8 ignore if -- @preserve */
242223 if ( node . parentNode === parent ) {
224+ /* v8 ignore if -- @preserve */
243225 if ( node . nextSibling === insertionPoint ) return
226+ /* v8 ignore if -- @preserve */
244227 if ( SUPPORTS_MOVE_BEFORE ) {
245228 ; ( parent as NodeWithMoveBefore ) . moveBefore ( node , insertionPoint )
246229 return
@@ -260,15 +243,17 @@ class Morph {
260243 }
261244
262245 morph ( from : ChildNode , to : ChildNode | NodeListOf < ChildNode > ) : void {
263- if ( isParentNode ( from ) ) {
246+ if ( isElementNode ( from ) ) {
264247 this . #mapIdSets( from )
265248 }
266249
267250 if ( to instanceof NodeList ) {
268251 this . #mapIdArraysForEach( to )
269252 this . #morphOneToMany( from , to )
270- } else if ( isParentNode ( to ) ) {
271- this . #mapIdArrays( to )
253+ } else {
254+ if ( isElementNode ( to ) ) {
255+ this . #mapIdArrays( to )
256+ }
272257 this . #morphOneToOne( from , to )
273258 }
274259 }
@@ -280,15 +265,17 @@ class Morph {
280265 this . #removeNode( from )
281266 } else if ( length === 1 ) {
282267 this . #morphOneToOne( from , to [ 0 ] ! )
283- } else if ( length > 1 ) {
268+ } else {
284269 const newNodes = [ ...to ]
285270 this . #morphOneToOne( from , newNodes . shift ( ) ! )
286271 const insertionPoint = from . nextSibling
287- const parent = from . parentNode || document
272+ const parent = from . parentNode
273+ const callbackParent = parent || document
288274
289275 for ( let i = 0 ; i < newNodes . length ; i ++ ) {
290276 const newNode = newNodes [ i ] !
291- if ( this . #options. beforeNodeAdded ?.( parent , newNode , insertionPoint ) ?? true ) {
277+ if ( this . #options. beforeNodeAdded ?.( callbackParent , newNode , insertionPoint ) ?? true ) {
278+ if ( ! parent ) continue
292279 parent . insertBefore ( newNode , insertionPoint )
293280 this . #options. afterNodeAdded ?.( newNode )
294281 }
@@ -340,9 +327,7 @@ class Morph {
340327 if ( ! ( this . #options. beforeNodeVisited ?.( from , to ) ?? true ) ) return
341328
342329 if ( from . nodeType === to . nodeType && from . nodeValue !== null && to . nodeValue !== null ) {
343- if ( from . nodeValue !== to . nodeValue ) {
344- from . nodeValue = to . nodeValue
345- }
330+ from . nodeValue = to . nodeValue
346331 } else {
347332 this . #replaceNode( from , to )
348333 }
@@ -512,7 +497,6 @@ class Morph {
512497 // Match elements by isEqualNode
513498 for ( let i = 0 ; i < unmatchedElementIndices . length ; i ++ ) {
514499 const unmatchedIndex = unmatchedElementIndices [ i ] !
515- if ( ! unmatchedElementActive [ unmatchedIndex ] ) continue
516500
517501 const localName = localNameMap [ unmatchedIndex ]
518502 const element = toChildNodes [ unmatchedIndex ] as Element
@@ -685,7 +669,6 @@ class Morph {
685669 // Match nodes by isEqualNode (skip whitespace-only text nodes)
686670 for ( let i = 0 ; i < unmatchedNodeIndices . length ; i ++ ) {
687671 const unmatchedIndex = unmatchedNodeIndices [ i ] !
688- if ( ! unmatchedNodeActive [ unmatchedIndex ] ) continue
689672
690673 const node = toChildNodes [ unmatchedIndex ] !
691674 for ( let c = 0 ; c < candidateNodeIndices . length ; c ++ ) {
@@ -787,13 +770,15 @@ class Morph {
787770 }
788771
789772 #replaceNode( node : ChildNode , newNode : ChildNode ) : void {
790- const parent = node . parentNode || document
773+ const parent = node . parentNode
774+ const callbackParent = parent || document
791775 const insertionPoint = node
792776 // Check if both removal and addition are allowed before starting the replacement
793777 if (
794778 ( this . #options. beforeNodeRemoved ?.( node ) ?? true ) &&
795- ( this . #options. beforeNodeAdded ?.( parent , newNode , insertionPoint ) ?? true )
779+ ( this . #options. beforeNodeAdded ?.( callbackParent , newNode , insertionPoint ) ?? true )
796780 ) {
781+ if ( ! parent ) return
797782 parent . insertBefore ( newNode , insertionPoint )
798783 this . #options. afterNodeAdded ?.( newNode )
799784 node . remove ( )
@@ -810,21 +795,19 @@ class Morph {
810795
811796 #mapIdArraysForEach( nodeList : NodeList ) : void {
812797 for ( const childNode of nodeList ) {
813- if ( isParentNode ( childNode ) ) {
798+ if ( isElementNode ( childNode ) ) {
814799 this . #mapIdArrays( childNode )
815800 }
816801 }
817802 }
818803
819804 // For each node with an ID, push that ID into the IdArray on the IdArrayMap, for each of its parent elements.
820- #mapIdArrays( node : ParentNode ) : void {
805+ #mapIdArrays( node : Element ) : void {
821806 const idArrayMap = this . #idArrayMap
822807
823808 forEachDescendantElementWithId ( node , ( element ) => {
824809 const id = element . id
825810
826- if ( id === "" ) return
827-
828811 let currentElement : Element | null = element
829812
830813 while ( currentElement ) {
@@ -841,14 +824,12 @@ class Morph {
841824 }
842825
843826 // For each node with an ID, add that ID into the IdSet on the IdSetMap, for each of its parent elements.
844- #mapIdSets( node : ParentNode ) : void {
827+ #mapIdSets( node : Element ) : void {
845828 const idSetMap = this . #idSetMap
846829
847830 forEachDescendantElementWithId ( node , ( element ) => {
848831 const id = element . id
849832
850- if ( id === "" ) return
851-
852833 let currentElement : Element | null = element
853834
854835 while ( currentElement ) {
@@ -865,12 +846,8 @@ class Morph {
865846 }
866847}
867848
868- function forEachDescendantElementWithId ( node : ParentNode , callback : ( element : Element ) => void ) : void {
869- const root = node as Node
870- const ownerDocument = root . nodeType === 9 ? ( root as Document ) : root . ownerDocument
871- if ( ! ownerDocument ) return
872-
873- const walker = ownerDocument . createTreeWalker ( root , TREE_WALKER_SHOW_ELEMENT )
849+ function forEachDescendantElementWithId ( node : Element , callback : ( element : Element ) => void ) : void {
850+ const walker = node . ownerDocument ! . createTreeWalker ( node , TREE_WALKER_SHOW_ELEMENT )
874851 let current = walker . nextNode ( )
875852
876853 while ( current ) {
@@ -907,6 +884,10 @@ function isWhitespaceTextNode(node: Node): boolean {
907884 return true
908885}
909886
887+ function isElementNode ( node : Node ) : node is Element {
888+ return node . nodeType === ELEMENT_NODE_TYPE
889+ }
890+
910891function isInputElement ( element : Element ) : element is HTMLInputElement {
911892 return element . localName === "input"
912893}
@@ -925,10 +906,6 @@ function isOptionElement(element: Element): element is HTMLOptionElement {
925906 return element . localName === "option"
926907}
927908
928- function isParentNode ( node : Node ) : node is ParentNode {
929- return ! ! IS_PARENT_NODE_TYPE [ node . nodeType ]
930- }
931-
932909// Find longest increasing subsequence to minimize moves during reordering
933910// Returns the indices in the sequence that form the LIS
934911function longestIncreasingSubsequence ( sequence : Array < number | undefined > ) : Array < number > {
@@ -962,8 +939,6 @@ function longestIncreasingSubsequence(sequence: Array<number | undefined>): Arra
962939 if ( left === lisLength ) lisLength ++
963940 }
964941
965- if ( lisLength === 0 ) return [ ]
966-
967942 const result = new Array < number > ( lisLength )
968943 let curr = indices [ lisLength - 1 ] !
969944
0 commit comments