/// <summary> /// Create a markup pointer positioned within the container. /// </summary> /// <returns></returns> public MarkupPointer CreateMarkupPointer(POSITION initialPosition) { MarkupPointer p = MarkupServices.CreateMarkupPointer(); switch (initialPosition) { case POSITION.DOCUMENT_START: p.MoveToContainer(this, true); break; case POSITION.BODY_START: p.MoveAdjacentToElement(Document.body, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); break; case POSITION.BODY_END: p.MoveAdjacentToElement(Document.body, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); break; case POSITION.DOCUMENT_END: p.MoveToContainer(this, false); break; } return(p); }
/// <summary> /// Return the parent element that 2 pointers share in common /// </summary> /// <returns></returns> private IHTMLElement GetSharedParent(MarkupPointer start, MarkupPointer end) { IHTMLElement startCurrentScope = start.CurrentScope; IHTMLElement endCurrentScope = end.CurrentScope; if (startCurrentScope == endCurrentScope) { //the start/end points share the same current scope, so return that element as the parent. return(startCurrentScope); } else { //find the parent element that these 2 pointers share in common //by locating the first parent endtag that the rangeEnd pointer //is contained within. MarkupPointer parentStart = MarkupServices.CreateMarkupPointer(); MarkupPointer parentEnd = MarkupServices.CreateMarkupPointer(); IHTMLElement sharedParent = startCurrentScope; if (sharedParent != null) { parentEnd.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); while (sharedParent != null && parentEnd.IsLeftOf(end)) { sharedParent = sharedParent.parentElement; parentEnd.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } } return(sharedParent); } }
/// <summary> /// Walk through the markup range in reverse, letting the walker visit each position. /// </summary> /// <param name="walker">the delegate walking navigating the the markup range</param> /// <param name="inScopeElementsOnly">if true, enter/exit notifications about out-of-scope elements will be suppressed.</param> /// <returns></returns> public void WalkRangeReverse(MarkupRangeWalker walker, bool inScopeContextsOnly) { MarkupPointer p1 = MarkupServices.CreateMarkupPointer(End); MarkupPointer p2 = MarkupServices.CreateMarkupPointer(End); p1.Cling = false; p2.Cling = false; MarkupContext context = new MarkupContext(); bool continueWalking = true; MarkupRange currentRange = null; while (continueWalking && p2.IsRightOf(Start)) { string text = null; bool isInScope = true; p2.Left(true, context); currentRange = new MarkupRange(p2.Clone(), p1.Clone(), MarkupServices); if (inScopeContextsOnly) { if (context.Element != null) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { p1.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); isInScope = InRange(p1); } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { p1.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); isInScope = InRange(p1); } } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text) { // It's possible part of the text is out of scope, so only return the in-scope text. if (currentRange.Start.IsLeftOf(Start)) { currentRange.Start.MoveToPointer(Start); } } } if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text) { text = currentRange.Text; } if (!inScopeContextsOnly || isInScope) { continueWalking = walker(currentRange, context, text); } p1.MoveToPointer(p2); } }
public static void PerformImageBreakout(MarkupPointer p) { Direction dir = ImageBreakout(p); if (dir == Direction.Right) { p.MoveAdjacentToElement(p.CurrentScope, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } if (dir == Direction.Left) { p.MoveAdjacentToElement(p.CurrentScope, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); } }
public static void MoveUnitBounded(MarkupPointer p, MoveDirection direction, MarkupPointerAdjacency stopRule, IHTMLElement boundary) { MarkupPointer pBoundary = p.Clone(); pBoundary.MoveAdjacentToElement(boundary, direction == MoveDirection.LEFT ? _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin : _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); MoveUnitBounded(p, direction, stopRule, pBoundary); }
/// <summary> /// Creates an instance of the IMarkupPointer object with an initial position /// adjacent to the specified HTML element. /// </summary> /// <param name="e"></param> /// <param name="eAdj"></param> /// <returns></returns> public MarkupPointer CreateMarkupPointer(IHTMLElement e, _ELEMENT_ADJACENCY eAdj) { MarkupPointer pointer = CreateMarkupPointer(); pointer.MoveAdjacentToElement(e, eAdj); return(pointer); }
public static void SplitBlockForInsertionOrBreakout(MshtmlMarkupServices markupServices, MarkupRange bounds, MarkupPointer insertAt) { IHTMLElement currentBlock = insertAt.GetParentElement(ElementFilters.BLOCK_OR_TABLE_CELL_ELEMENTS); if (currentBlock == null) { return; } if (ElementFilters.IsBlockQuoteElement(currentBlock) || ElementFilters.IsTableCellElement(currentBlock)) { return; } MarkupPointer blockStart = markupServices.CreateMarkupPointer(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); MarkupPointer blockEnd = markupServices.CreateMarkupPointer(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (bounds != null && (blockStart.IsLeftOf(bounds.Start) || blockEnd.IsRightOf(bounds.End))) { return; } // Don't split if at the beginning or end of the visible content in the block. // Instead just move the insertion point outside the block. MarkupRange testRange = markupServices.CreateMarkupRange(); testRange.Start.MoveToPointer(insertAt); testRange.End.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); if (testRange.IsEmptyOfContent()) { insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); return; } testRange.Start.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); testRange.End.MoveToPointer(insertAt); if (testRange.IsEmptyOfContent()) { insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); return; } MarkupPointer moveTarget = markupServices.CreateMarkupPointer(blockEnd); markupServices.Move(insertAt, blockEnd, moveTarget); insertAt.MoveAdjacentToElement(currentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); }
/// <summary> /// Condenses this range into the smallest well-formed state that still contains the same /// text markup. /// </summary> /// <returns></returns> public bool Trim() { MarkupPointer newStart = MarkupServices.CreateMarkupPointer(Start); MarkupPointer newEnd = MarkupServices.CreateMarkupPointer(End); MarkupContext context = new MarkupContext(); //set newStart adjacent to the first text element to its right newStart.Right(true, context); while (!HasContentBetween(Start, newStart) && newStart.IsLeftOf(End)) { newStart.Right(true, context); } if (HasContentBetween(Start, newStart)) { newStart.Left(true); //we overstepped the text, so back up one step } //set newEnd adjacent to the first text element to its left newEnd.Left(true, context); while (!HasContentBetween(newEnd, End) && newEnd.IsRightOf(Start)) { newEnd.Left(true, context); } if (HasContentBetween(newEnd, End)) { newEnd.Right(true); //we overstepped the text, so back up one step } IHTMLElement sharedParent = GetSharedParent(newStart, newEnd); //span the start and end pointers as siblings by finding the parents of start and end //pointers that are direct children of the sharedParent IHTMLElement child = GetOuterMostChildOfParent(newStart, true, sharedParent); if (child != null) { newStart.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); } child = GetOuterMostChildOfParent(newEnd, false, sharedParent); if (child != null) { newEnd.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } if (!HasContentBetween(newStart, Start) && !HasContentBetween(End, newEnd) && !(Start.IsEqualTo(newStart) && End.IsEqualTo(newEnd))) { Start.MoveToPointer(newStart); End.MoveToPointer(newEnd); return(true); } else { //the range didn't change, so return false. return(false); } }
//this is similar to the GetTopLevelElements except will also return table cells if correct filter // is set and recurse is equal to true public IHTMLElement[] GetTopLevelBlocksAndCells(IHTMLElementFilter filter, bool recurse) { ArrayList list = new ArrayList(); Hashtable usedElements = new Hashtable(); MarkupPointer p = MarkupServices.CreateMarkupPointer(Start); MarkupContext context = p.Right(false); //move p through the range to locate each of the top level elements while (p.IsLeftOf(End)) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope) { p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (usedElements[context.Element] == null) { if (p.IsLeftOfOrEqualTo(End) && (filter == null || filter(context.Element))) { list.Add(context.Element); } //special case--inside of a table element, want to get out the cells inside else if (recurse && ElementFilters.TABLE_ELEMENTS(context.Element)) { MarkupRange newRange = MarkupServices.CreateMarkupRange(context.Element); newRange.Start.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); if (newRange.Start.IsLeftOf(Start)) { newRange.Start.MoveToPointer(Start); } if (newRange.End.IsRightOf(End)) { newRange.End.MoveToPointer(End); } //recursively check inside table element for table cells list.AddRange(newRange.GetTopLevelBlocksAndCells(filter, true)); } //cache the fact that we've already tested this element. usedElements[context.Element] = context.Element; } } p.Right(true, context); } return(HTMLElementHelper.ToElementArray(list)); }
/// <summary> /// Safely removes the content within this range without leaving the document badly formed. /// </summary> public void RemoveContent() { //delete the selection by moving a delete range right (from the start). //Each time that a tag that does not entirely exist within this selection //is encountered, the range content will be deleted, the deleteRange will //skip over the element. MarkupRange deleteRange = this.Clone(); Trace.Assert(deleteRange.Start.Positioned, "Trying to remove content from selection that contains pointers that are not positioned."); deleteRange.End.MoveToPointer(deleteRange.Start); MarkupPointer p = MarkupServices.CreateMarkupPointer(); MarkupPointer previousPosition = MarkupServices.CreateMarkupPointer(deleteRange.End); MarkupContext context = new MarkupContext(); deleteRange.End.Right(true, context); while (deleteRange.End.IsLeftOf(End)) { if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (p.IsRightOf(End)) { //this element does not exist entirely in this selection, so we need to //ignore it in the delete. //save this position so that the delete range can be repositioned here p.MoveToPointer(deleteRange.End); //move the end left since we overstepped the valid delete range deleteRange.End.MoveToPointer(previousPosition); //delete the content in the deleteRange, and move it back to this position deleteRangeContentAndMoveToPosition(deleteRange, p); } else { //this element exists entirely in this selection, so skip to its end (since //we know it can be deleted) deleteRange.End.MoveToPointer(p); } } else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { p.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (p.IsLeftOf(Start)) { //this element does not exist entirely in this selection, so we need to //ignore it in the delete. //save this position so that the delete range can be repositioned here p.MoveToPointer(deleteRange.End); //move the end left since we overstepped the valid delete range deleteRange.End.MoveToPointer(previousPosition); //delete the content in the deleteRange, and move it back to this position deleteRangeContentAndMoveToPosition(deleteRange, p); } else { //this element exists entirely in this selection, so skip to its end (since //we know it can be deleted) deleteRange.End.MoveToPointer(p); } } previousPosition.MoveToPointer(deleteRange.End); deleteRange.End.Right(true, context); } //delete the last part of the range deleteRange.End.MoveToPointer(End); if (!deleteRange.Start.Equals(deleteRange.End)) { MarkupServices.Remove(deleteRange.Start, deleteRange.End); } }
/// <summary> /// <font><span>aaa[splitPoint]bbb</span></font> --> <font><span>aaa</span></font><font>[splitPoint]<span>bbb</span></font> /// </summary> private void SplitInlineTags(MarkupPointer splitPoint) { Debug.Assert(splitPoint.Positioned); IHTMLElement currentElement = splitPoint.GetParentElement(ElementFilters.CreateElementPassFilter()); while (currentElement != null) { if (!ElementFilters.IsInlineElement(currentElement)) return; IHTMLElement parentElement = currentElement.parentElement; MarkupRange currentElementRange = _markupServices.CreateMarkupRange(currentElement, false); MarkupRange leftRange = _markupServices.CreateMarkupRange(); IHTMLElement leftElement = _markupServices.CreateElement(_markupServices.GetElementTagId(currentElement), null); HTMLElementHelper.CopyAttributes(currentElement, leftElement); leftRange.MoveToPointers(currentElementRange.Start, splitPoint); _markupServices.InsertElement(leftElement, leftRange.Start, leftRange.End); splitPoint.MoveAdjacentToElement(leftElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); MarkupRange rightRange = _markupServices.CreateMarkupRange(); IHTMLElement rightElement = _markupServices.CreateElement(_markupServices.GetElementTagId(currentElement), null); HTMLElementHelper.CopyAttributes(currentElement, rightElement); rightRange.MoveToPointers(splitPoint, currentElementRange.End); #if DEBUG // Verify that the right range does not overlap the left *element* MarkupRange leftElementRange = _markupServices.CreateMarkupRange(leftElement, true); Debug.Assert(leftElementRange.End.IsLeftOfOrEqualTo(rightRange.Start), "Your right range overlaps the left element that you just created!"); #endif _markupServices.InsertElement(rightElement, rightRange.Start, rightRange.End); splitPoint.MoveAdjacentToElement(rightElement, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); _markupServices.RemoveElement(currentElement); currentElement = parentElement; } }
public static void PerformImageBreakout(MarkupPointer p) { Direction dir = ImageBreakout(p); if (dir == Direction.Right) { p.MoveAdjacentToElement(p.CurrentScope, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } if (dir == Direction.Left) { p.MoveAdjacentToElement(p.CurrentScope, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); } }