Theory of Operation for #spgetDOMHTML
1) Create a temp table and populate via #spgetDOM. This allows us to leverage the functionality of this procedure that produces HUID and SortHUID values.
2) Populate a Sequence column--which is just a ROW_NUMBER generated over SortHUID
3) Populate an HasChild column by joining Sequence to Sequence + 1
The basic idea is that we will use a cursor to walk through the the temporary table in Sequence order. This allows us to emit tags as as we encounter them: they are in the correct order to render, without respect to hierachy.
The challenges:
We need to know whether to render a > or a /> at the end of a tag (i.e. whether we need to close the tag immediately. If the node has a child, we render a >. IF the node does not have a child, we render a />. This is simple since we have the IsChild field we populated above.
We need to keep track of the CurrentParent node. The nodes that we render will have this node as their parent. Keeping track of the CurrentParent will require use of a stack. The CurrentParent will change:
Any time a node is added, the CurrentParent will FIRST (before the node is rendered) be set to the least-common ancestor. Any nodes between the current (before the new node is rendered) node and the least-common ancestor must be closed.
Any time a HasChild node is added, CurrentParent will be set to the newly added node AFTER the node is rendered. As this CurrentParent change occurs, the new node will be pushed to the stack.
Consider this example:
<html>
<body>
Goodbye
1) <html> / parent = null / HasChild / render tag Opened / push stack / CurParent = html
2) <body> / parent = html / HasChild / render tag Opened / push stack / CurParent = body
3)
4) Hello / parent = div / NO child / render text / NO push / CurParent unchanged
5) POP /
6)
7) Item1 / parent = li / NO child / render text / NO push / CurParent unchanged
8) POP /
9) Item2 / parent = ul / NO child / render text / NO push / CurParent unchanged
10) POP /
11)
12)
13) Item3 / parent = li / NO child / render text / NO push / CurParent unchanged
14) POP /
/ parent -> body / HasChild / render tag Opened / push stack / CurParent = p
15) goodbye/ parent = p / NO child / render text / NO push / CurParent unchanged
16) POP /
Later / parent -> body / NO child / render text / NO push / CurParent unchanged17) POP / </body>
18) POP / </html>
The pop's are the tricky part.
Pops go like this:
5) We know parent of ul != CurParent (div), so we pop until we find CurParent (DEID for div)
Pop 1 = div, close ... CurParent -> body [DEID match]
Inspect: CurParent = Parent
8) We know parent of li != CurParent (li)
Pop 1 = li, close ... CurParent -> ul [DEID match]
10) We know parent of br != CurParent (li)
Pop 1 = li, close .. CurParent -> ul [no match]
Pop 2 = ul, close .. CurParent -> body [DEID match]
14) We know parent of p != CurParent (li)
Pop 1 = li, close .. CurParent -> ol [no match]
Pop 2 = ol, close .. CurParent -> body [DEID match]
16) We know parent of body != curParent (body)
Pop 1 = p, close .. CurParen -> body
Pop 2 = body, close .. CurParent -> html
Pop 3 = html, close .. CurParent -> null
Review:
1) Always test to see if parent needs to change before rendering node
1a) Will know this is the case if @ParentDEID <> @CurParentDEID
1b) Pop until @ParentDEID = @CurParentDEID. Close tag after each pop. @CurParentDEID is removed from stack--will be re-pushed if needed.
2) Render tag, with proper ending (> or />)
3) If IsChild, push @CurParentDEID and set @CurParentDEID = newly-rendered node