private void Init(IHTMLDocument document, MshtmlMarkupServices markupServices, MarkupRange selectionRange, MarkupRangeFilter filter, DamageFunction damageFunction, bool expandRange)
        {
            // save references
            this.htmlDocument   = document;
            this.markupServices = markupServices;
            this.selectionRange = selectionRange;
            this.filter         = filter;
            this.damageFunction = damageFunction;

            // If the range is already the body, don't expand it or else it will be the whole document
            if (expandRange)
            {
                ExpandRangeToWordBoundaries(selectionRange);
            }

            // initialize pointer to beginning of selection range
            MarkupPointer wordStart = MarkupServices.CreateMarkupPointer(selectionRange.Start);
            MarkupPointer wordEnd   = MarkupServices.CreateMarkupPointer(selectionRange.Start);

            //create the range for holding the current word.
            //Be sure to set its gravity so that it stays around text that get replaced.
            currentWordRange = MarkupServices.CreateMarkupRange(wordStart, wordEnd);
            currentWordRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
            currentWordRange.End.Gravity   = _POINTER_GRAVITY.POINTER_GRAVITY_Right;

            currentVirtualPosition = currentWordRange.End.Clone();
        }
        /// <summary>
        /// Returns true if the the range contains an element that matches the filter
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public bool ContainsElements(IHTMLElementFilter filter)
        {
            if (!IsEmpty())
            {
                Hashtable     usedElements = new Hashtable();
                MarkupPointer p            = MarkupServices.CreateMarkupPointer(Start);
                MarkupContext context      = p.Right(false);

                //move p through the range to locate each the elements adding elements that pass the filter
                while (p.IsLeftOfOrEqualTo(End))
                {
                    if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope ||
                        context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope ||
                        context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope)
                    {
                        if (usedElements[context.Element] == null)
                        {
                            if (filter(context.Element))
                            {
                                return(true);
                            }
                        }

                        //cache the fact that we've already tested this element.
                        usedElements[context.Element] = context.Element;
                    }
                    p.Right(true, context);
                }
            }
            return(false);
        }
        /// <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);
            }
        }
        /// <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);
            }
        }
        /// <summary>
        /// Creates a clone that spans the same range as this MarkupRange.
        /// Note: The clone can be manipulated without changing the position of this range.
        /// </summary>
        /// <returns></returns>
        public MarkupRange Clone()
        {
            MarkupRange clone = MarkupServices.CreateMarkupRange();

            clone.Start.MoveToPointer(Start);
            clone.Start.Cling   = Start.Cling;
            clone.Start.Gravity = Start.Gravity;
            clone.End.MoveToPointer(End);
            clone.End.Cling   = End.Cling;
            clone.End.Gravity = End.Gravity;
            return(clone);
        }
        //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>
        /// Gets the elements in the range that match the filter.
        /// </summary>
        /// <param name="filter">the delegate testing each element to determine if it should be added to the list of elements to return</param>
        /// <param name="inScopeElementsOnly">if true, the only</param>
        /// <returns></returns>
        public IHTMLElement[] GetElements(IHTMLElementFilter filter, bool inScopeElementsOnly)
        {
            ArrayList list = new ArrayList();

            if (!IsEmpty())
            {
                Hashtable     usedElements = new Hashtable();
                MarkupPointer p            = MarkupServices.CreateMarkupPointer(Start);
                MarkupPointer end          = MarkupServices.CreateMarkupPointer(End);
                MarkupContext context      = p.Right(false);

                //move p through the range to locate each the elements adding elements that pass the filter
                while (p.IsLeftOfOrEqualTo(end))
                {
                    if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope ||
                        context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope ||
                        context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope)
                    {
                        if (usedElements[context.Element] == null)
                        {
                            if ((inScopeElementsOnly && isInScope(context.Element)) || !inScopeElementsOnly)
                            {
                                if (filter(context.Element))
                                {
                                    list.Add(context.Element);
                                }
                            }

                            //cache the fact that we've already tested this element.
                            usedElements[context.Element] = context.Element;
                        }
                    }
                    p.Right(true, context);
                }
            }
            return(HTMLElementHelper.ToElementArray(list));
        }
 /// <summary>
 /// Returns a text range located at the same position as this MarkupRange.
 /// </summary>
 /// <returns></returns>
 public IHTMLTxtRange ToTextRange()
 {
     return(MarkupServices.CreateTextRange(Start, End));
 }
        /// <summary>
        /// Expands this range out to the next parent shared by the start and end points
        /// if there there are no non-empty text elements between them.
        /// </summary>
        /// <returns></returns>
        public bool MoveOutwardIfNo(RangeFilter rangeFilter)
        {
            MarkupRange newRange = MarkupServices.CreateMarkupRange();

            IHTMLElement sharedParent = GetSharedParent(Start, End);

            // If share a common parent, we will take the shared parent's parent so we can see if we want to grab
            // all the html inside of it, unless the shared parent is the body element, in which case we don't want to
            // epxand outward anymore
            if (Start.CurrentScope == sharedParent && End.CurrentScope == sharedParent && !(sharedParent is IHTMLBodyElement))
            {
                sharedParent = sharedParent.parentElement;
            }

            //expand to the inside of the shared parent first.  If this matches the current placement
            //of the pointers, then expand to the outside of the parent. This allows the outter selection
            //to grow incrementally in such a way as to allow the shared parent to be tested between
            //each iteration of this operation.
            newRange.Start.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin);
            newRange.End.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd);
            if (newRange.IsEmpty() || newRange.Start.IsRightOf(Start) || newRange.End.IsLeftOf(End))
            {
                newRange.Start.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                newRange.End.MoveAdjacentToElement(sharedParent, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
            }

            if (!rangeFilter(newRange.Start, Start) && !rangeFilter(End, newRange.End) &&
                !(Start.IsEqualTo(newRange.Start) && End.IsEqualTo(newRange.End)))
            {
                Start.MoveToPointer(newRange.Start);
                End.MoveToPointer(newRange.End);
                return(true);
            }
            else
            {
                //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(Start, true, sharedParent);
                if (child != null)
                {
                    newRange.Start.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin);
                }
                else
                {
                    newRange.Start = Start;
                }

                child = GetOuterMostChildOfParent(End, false, sharedParent);
                if (child != null)
                {
                    newRange.End.MoveAdjacentToElement(child, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                }
                else
                {
                    newRange.End = End;
                }

                if (!rangeFilter(newRange.Start, Start) && !rangeFilter(End, newRange.End) &&
                    !(Start.IsEqualTo(newRange.Start) && End.IsEqualTo(newRange.End)))
                {
                    Start.MoveToPointer(newRange.Start);
                    End.MoveToPointer(newRange.End);
                    return(true);
                }
                else
                {
                    //the range didn't change, so return false.
                    return(false);
                }
            }
        }
 /// <summary>
 /// Move to range.
 /// </summary>
 /// <param name="textRange"></param>
 public void MoveToTextRange(IHTMLTxtRange textRange)
 {
     MarkupServices.MovePointersToRange(textRange, Start, End);
 }
 public bool InRange(IHTMLElement e)
 {
     Debug.Assert(e != null, "Unexpected null element.");
     return(InRange(MarkupServices.CreateMarkupRange(e, true)));
 }
        /// <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);
            }
        }
Exemple #13
0
 /// <summary>
 /// Get current selection pointers
 /// </summary>
 /// <returns>selection pointers</returns>
 public MarkupRange GetSelectedMarkupRange()
 {
     return(MarkupServices.CreateMarkupRange(SelectedRange));
 }