/// <summary> /// Find the most logic direction to move the cursor so that it matches the where has clicked /// </summary> /// <param name="Selection"></param> /// <returns></returns> public static Direction FindSelectionToLogicalPosition(MarkupRange Selection, IHTMLElement body, bool?forward) { // There is a selection, not just a click if (!Selection.Start.IsEqualTo(Selection.End)) { return(Direction.None); } Direction dir = ImageBreakout(Selection.Start); if (dir != Direction.None) { return(dir); } MarkupContext contextRight = Selection.Start.Right(false); MarkupContext contextLeft = Selection.Start.Left(false); // there is text or some other type of content around the click if (!((contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) && (contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope))) { return(Direction.None); } // The click is not between two block elements, so it should be fine where it is if (!ElementFilters.IsBlockElement(contextLeft.Element) || !ElementFilters.IsBlockElement(contextRight.Element)) { return(Direction.None); } // </blockElement>|</postBody> if (contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextLeft) && contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope && contextRight.Element.id == body.id) { return(Direction.Left); } // <postBody>|<blockElement> if (contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextRight) && contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope && contextLeft.Element.id == body.id) { return(Direction.Right); } // </blockElement>|<blockElement> if (contextLeft.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextLeft) && contextRight.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && !IsSmartContent(contextRight)) { return(forward == null || forward == true ? Direction.Right : Direction.Left); } return(Direction.None); }
/// <summary> /// Returns true if this range is composes entirely of non-visible elements. /// </summary> /// <param name="inScopeContextsOnly">flag to ignore out of scope element /// (use false unless you absolutely want to ignore visible content in cases like /// [start]<p>[end]</p>)</param> /// <returns></returns> public bool IsEmptyOfContent(bool inScopeContextsOnly) { try { bool isEmptyOfContent = true; WalkRange( delegate(MarkupRange currentRange, MarkupContext context, string text) { text = text ?? string.Empty; if (!String.IsNullOrEmpty(text.Trim())) { isEmptyOfContent = false; return(false); } if (context.Element != null && ElementFilters.IsVisibleEmptyElement(context.Element)) { isEmptyOfContent = false; return(false); } // Continue walking the range. return(true); }, inScopeContextsOnly); return(isEmptyOfContent); } catch (Exception) { return(false); } }
public static MarkupRange GetEditableRange(IHTMLElement e, MshtmlMarkupServices markupServices) { IHTMLElement3 editableElement = null; while (e != null) { if (((IHTMLElement3)e).isContentEditable) { editableElement = (IHTMLElement3)e; if (ElementFilters.IsBlockElement(e)) { break; } } else { break; } e = e.parentElement; } if (editableElement != null) { return(markupServices.CreateMarkupRange((IHTMLElement)editableElement, false)); } else { return(null); } }
private static MoveFilterResult StopBeforeExitBlock(MarkupContext mc) { if (mc.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope && ElementFilters.IsBlockElement(mc.Element)) { return(MoveFilterResult.STOP_BACK); } return(MoveFilterResult.CONTINUE); }
private static MoveFilterResult StopAfterEnterBlock(MarkupContext mc) { if (mc.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && ElementFilters.IsBlockElement(mc.Element)) { return(MoveFilterResult.STOP); } return(MoveFilterResult.CONTINUE); }
/// <summary> /// Retrieves the block element that this pointer is positioned within. /// </summary> public IHTMLElement CurrentBlockScope() { IHTMLElement parent = CurrentScope; while (parent != null && !ElementFilters.IsBlockElement(parent)) { parent = parent.parentElement; } return(parent); }
private static MoveFilterResult StopBeforeVisible(MarkupContext mc) { if (mc.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text) { return(MoveFilterResult.STOP_BACK); } if (mc.Element != null && ElementFilters.IsInlineElement(mc.Element) && !ElementFilters.IsVisibleEmptyElement(mc.Element)) { return(MoveFilterResult.CONTINUE); } return(MoveFilterResult.STOP_BACK); }
public void RemoveElementsByTagId(_ELEMENT_TAG_ID tagId, bool onlyIfNoAttributes) { if (tagId == _ELEMENT_TAG_ID.TAGID_NULL) { return; } // Remove the tagId up the parent chain IHTMLElement currentElement = ParentElement(); while (currentElement != null) { if (MarkupServices.GetElementTagId(currentElement) == tagId && (!onlyIfNoAttributes || !HTMLElementHelper.HasMeaningfulAttributes(currentElement))) { try { MarkupServices.RemoveElement(currentElement); } catch (COMException e) { Trace.Fail(String.Format("Failed to remove element ({0}) with error: {1}", currentElement.outerHTML, // {0} e // {1} )); } } currentElement = currentElement.parentElement; } // Remove any other instances IHTMLElement[] elements = GetElements(ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(tagId)), false); foreach (IHTMLElement e in elements) { if (MarkupServices.GetElementTagId(e) == tagId && (!onlyIfNoAttributes || !HTMLElementHelper.HasMeaningfulAttributes(e))) { try { MarkupServices.RemoveElement(e); } catch (COMException ex) { Trace.Fail(String.Format("Failed to remove element ({0}) with error: {1}", e.outerHTML, // {0} ex // {1} )); } } } }
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); }
//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> /// Determines if a range has a particular _ELEMENT_TAG_ID applied. /// </summary> /// <param name="tagId"></param> /// <param name="partially">If true, then IsTagId will return true if any part of it is contained within a tagId element. /// If false, then IsTagId will return true only if the range is entirely contained within a tagId element.</param> /// <returns></returns> public bool IsTagId(_ELEMENT_TAG_ID tagId, bool partially) { // This first block of code will return true if the range is entirely contained within an element with the given tagId. IHTMLElement currentElement = ParentElement(); while (currentElement != null) { if (MarkupServices.GetElementTagId(currentElement) == tagId) { return(true); } currentElement = currentElement.parentElement; } // This second block of code will return true if the range is partially contained within an element with the given tagId. if (partially) { IHTMLElement[] elements = GetElements(ElementFilters.CreateTagIdFilter(MarkupServices.GetNameForTagId(tagId)), false); return(elements.Length > 0); } return(false); }