internal bool RemoveCenteringNode() { MshtmlMarkupServices MarkupServices = new MshtmlMarkupServices(_element.document as IMarkupServicesRaw); IHTMLElement element = FindCenteringNode(); // We couldnt find a parent, so nothing to remove if (element == null) { return(false); } MarkupPointer start = MarkupServices.CreateMarkupPointer(); MarkupPointer end = MarkupServices.CreateMarkupPointer(); MarkupPointer target = MarkupServices.CreateMarkupPointer(); // Move the stuff inside the smart content container ouside of itself start.MoveAdjacentToElement(element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); end.MoveAdjacentToElement(element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd); target.MoveAdjacentToElement(element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); MarkupServices.Move(start, end, target); // remove the empty smart content container start.MoveAdjacentToElement(element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); end.MoveAdjacentToElement(element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); MarkupServices.Remove(start, end); return(true); }
/// <summary> /// Searches through the provided document for a start and end comment marker and then returns the fragment as /// a MarkupRange. /// </summary> /// <param name="document">The document to search.</param> /// <param name="startMarker">The comment text that marks the start of the fragment /// (e.g. <!--StartFragment--> ).</param> /// <param name="endMarker">The comment text that marks the end of the fragment /// (e.g. <!--EndFragment--> ).</param> /// <returns>The fragment as a MarkupRange or null if no valid fragment was found.</returns> private MarkupRange FindMarkedFragment(IHTMLDocument2 document, string startMarker, string endMarker) { MarkupPointer startFragment = null; MarkupPointer endFragment = null; MshtmlMarkupServices markupServices = new MshtmlMarkupServices((IMarkupServicesRaw)document); // Look for the markers in the document. foreach (IHTMLElement element in document.all) { if (element is IHTMLCommentElement && ((IHTMLCommentElement)element).text == startMarker) { startFragment = markupServices.CreateMarkupPointer(element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } else if (element is IHTMLCommentElement && ((IHTMLCommentElement)element).text == endMarker) { endFragment = markupServices.CreateMarkupPointer(element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); } } if (startFragment == null || endFragment == null || !startFragment.Positioned || !endFragment.Positioned || startFragment.IsRightOf(endFragment)) { Trace.WriteLine("Unable to find fragment or invalid fragment!"); return(null); } // WinLive 251786: IE (and most other browsers) allow HTML like the following: // <p>This is a paragraph[cursor] // <p>This is a paragraph // However, when we use MarkupPointers to walk through this HTML, IE pretends there is a </p> at the end // of each of the above lines. This can cause issues when we copy part of this HTML somewhere else (e.g // everything after the [cursor]) and attempt to walk through both copies (e.g. during paste with keep // source formatting) at the same time. This holds true for some other elements, such as <li>s and <td>s. MarkupContext startContext = startFragment.Right(false); if (startFragment.IsLeftOf(endFragment) && startContext.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope && startContext.Element != null && ElementFilters.IsEndTagOptional(startContext.Element) && !Regex.IsMatch(startContext.Element.outerHTML, String.Format(CultureInfo.InvariantCulture, @"</{0}(\s[^>]*)?>\s*$", startContext.Element.tagName), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)) { startFragment.Right(true); } return(markupServices.CreateMarkupRange(startFragment, endFragment)); }
/// <summary> /// Makes sure that whole (not parts of) lists are included in the source of a paste. /// </summary> /// <param name="range">The original source range. The range may be modified.</param> /// <param name="markupServices">MarkupServices for the range.</param> private void ExpandToIncludeLists(MarkupRange range, MshtmlMarkupServices markupServices) { MarkupPointer pointer = markupServices.CreateMarkupPointer(); IHTMLElementFilter listFilter = ElementFilters.CreateCompoundElementFilter(ElementFilters.LIST_ELEMENTS, ElementFilters.LIST_ITEM_ELEMENTS); IHTMLElement[] listElements = range.GetElements(listFilter, false); foreach (IHTMLElement element in listElements) { IHTMLElement parentList = element; while (parentList != null && !ElementFilters.IsListElement(parentList)) { parentList = parentList.parentElement; } if (parentList != null) { pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (range.Start.IsRightOf(pointer)) { range.Start.MoveToPointer(pointer); } pointer.MoveAdjacentToElement(parentList, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (range.End.IsLeftOf(pointer)) { range.End.MoveToPointer(pointer); } } } }
/// <summary> /// Makes sure that whole (not parts of) tables are included in the source of a paste. /// </summary> /// <param name="range">The original source range. The range may be modified.</param> /// <param name="markupServices">MarkupServices for the range.</param> private void ExpandToIncludeTables(MarkupRange range, MshtmlMarkupServices markupServices) { MarkupPointer pointer = markupServices.CreateMarkupPointer(); IHTMLElement[] tableElements = range.GetElements(ElementFilters.TABLE_ELEMENTS, false); foreach (IHTMLElement element in tableElements) { IHTMLElement parentTable = element; while (parentTable != null && markupServices.GetElementTagId(parentTable) != _ELEMENT_TAG_ID.TAGID_TABLE) { parentTable = parentTable.parentElement; } if (parentTable != null) { pointer.MoveAdjacentToElement(parentTable, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); if (range.Start.IsRightOf(pointer)) { range.Start.MoveToPointer(pointer); } pointer.MoveAdjacentToElement(parentTable, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); if (range.End.IsLeftOf(pointer)) { range.End.MoveToPointer(pointer); } } } }
private IHTMLElement CreateNodeForCentering() { // Create markup services using the element's document that we are analyzing MshtmlMarkupServices MarkupServices = new MshtmlMarkupServices(_element.document as IMarkupServicesRaw); MarkupPointer end = MarkupServices.CreateMarkupPointer(); MarkupPointer start = MarkupServices.CreateMarkupPointer(); // Find the element that we will want to wrap. IHTMLElement elementToEncapsulate = _element; // If the elements parent is an A, we will also want to // wrap the A and not just the image inside if (_element.parentElement.tagName == "A") { elementToEncapsulate = _element.parentElement; } // Move the starting pointer to before the begining of the element we want to wrap start.MoveAdjacentToElement(elementToEncapsulate, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); // Find this elements parent IHTMLElement3 currentBlockScope = start.CurrentBlockScope() as IHTMLElement3; // If its parent is also the div that is around the post // we need to actually create a new div and just put it around the element // If it is splittable block, split it // e.g "<DIV>Blah<IMG/>Blah</DIV>" => "<DIV>Blah</DIV><DIV><IMG/></DIV><DIV>Blah</DIV>" if (!IsBodyElement(currentBlockScope)) { // We are in a block that can be split so split it at the begining and end MarkupHelpers.SplitBlockForInsertionOrBreakout(MarkupServices, null, start); end.MoveAdjacentToElement(elementToEncapsulate, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); MarkupHelpers.SplitBlockForInsertionOrBreakout(MarkupServices, null, end); // Position start back to the beginning of our element start.MoveAdjacentToElement(elementToEncapsulate, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); } // Now we can wrap it in an P tag (centering node) end.MoveAdjacentToElement(elementToEncapsulate, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); IHTMLElement centeringElement = MarkupServices.CreateElement(_ELEMENT_TAG_ID.TAGID_P, string.Empty); MarkupServices.InsertElement(centeringElement, start, end); return(centeringElement); }
/// <summary> /// Disambiguates a set of title regions to determine which should be editable based on proximity to the main post body element. /// </summary> /// <param name="bodyElement"></param> /// <param name="doc"></param> /// <param name="titleElements"></param> /// <returns>The title region in closest proximity to the post body element.</returns> protected static IHTMLElement GetPrimaryEditableTitleElement(IHTMLElement bodyElement, IHTMLDocument doc, IHTMLElement[] titleElements) { IHTMLDocument2 doc2 = (IHTMLDocument2)doc; IHTMLElement titleElement = titleElements[0]; if (titleElements.Length > 1) { try { MshtmlMarkupServices markupServices = new MshtmlMarkupServices((IMarkupServicesRaw)doc2); MarkupRange bodyRange = markupServices.CreateMarkupRange(bodyElement, true); MarkupPointer titlePointer = null; MarkupPointer tempPointer = markupServices.CreateMarkupPointer(); foreach (IHTMLElement title in titleElements) { tempPointer.MoveAdjacentToElement(title, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); if (titlePointer == null) { titlePointer = markupServices.CreateMarkupPointer(title, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); } else { tempPointer.MoveAdjacentToElement(title, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); if (tempPointer.IsLeftOf(bodyRange.End) && tempPointer.IsRightOf(titlePointer)) { //the temp pointer is closer to the body element, so assume it is more appropriate //to use as the title. titleElement = title; titlePointer.MoveToPointer(tempPointer); } } } } catch (COMException ex) { Trace.WriteLine("Failed to differentiate between multiple nodes with title text, using the first node. Exception: " + ex); } catch (InvalidCastException ex) { Trace.WriteLine("Failed to differentiate between multiple nodes with title text, using the first node. Exception: " + ex); } } return(titleElement); }
public MisspelledWordInfo FindSegment(MshtmlMarkupServices markupServices, IMarkupPointerRaw current) { //binary search int start = 0; int end = list.Count - 1; int i = Middle(start, end); while (-1 != i) { SegmentDef x = (SegmentDef)list.GetByIndex(i); bool startTest; current.IsRightOfOrEqualTo(x.startPtr, out startTest); if (startTest) { bool endTest; current.IsLeftOfOrEqualTo(x.endPtr, out endTest); if (endTest) { MarkupPointer pStart = markupServices.CreateMarkupPointer(x.startPtr); MarkupPointer pEnd = markupServices.CreateMarkupPointer(x.endPtr); MarkupRange range = markupServices.CreateMarkupRange(pStart, pEnd); //this could be a "phantom" range...no more content due to uncommitted damage or other reasons //if it is phantom, remove it from the tracker and return null if (range.Text == null) { list.RemoveAt(i); return(null); } return(new MisspelledWordInfo(range, x.word)); } start = i + 1; } else { end = i - 1; } i = Middle(start, end); } return(null); }
//iterates through a word range checking for spelling errors //return: whether the word range is finished (true) or not private bool ProcessWordRange(MshtmlWordRange wordRange) { if (wordRange.CurrentWordRange.Positioned) { //track where we will need to clear; MarkupPointer start = _markupServices.CreateMarkupPointer(); start.MoveToPointer(wordRange.CurrentWordRange.End); ArrayList highlightwords = new ArrayList(NUMBER_OF_WORDS_TO_CHECK); int i = 0; //to do....the word range is losing its place when it stays in the queue while (wordRange.HasNext() && i < NUMBER_OF_WORDS_TO_CHECK) { // advance to the next word wordRange.Next(); // check the spelling int offset, length; if (ProcessWord(wordRange, out offset, out length)) { MarkupRange highlightRange = wordRange.CurrentWordRange.Clone(); MarkupHelpers.AdjustMarkupRange(ref stagingTextRange, highlightRange, offset, length); //note: cannot just push the current word range here, as it moves before we get to the highlighting step highlightwords.Add(highlightRange); } i++; } MarkupPointer end = wordRange.CurrentWordRange.End; //got our words, clear the checked range and then add the misspellings ClearRange(start, end); foreach (MarkupRange word in highlightwords) { HighlightWordRange(word); } return(!wordRange.HasNext()); } else { return(true); } }
internal IHTMLElement FindCenteringNode() { // Create markup services using the element's document that we are analyzing MshtmlMarkupServices MarkupServices = new MshtmlMarkupServices(_element.document as IMarkupServicesRaw); // Create a pointer and move it to before the begining of its opening tag MarkupPointer start = MarkupServices.CreateMarkupPointer(); start.MoveAdjacentToElement(_element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); // Find the block parent of this node. IHTMLElement blockParent = start.CurrentBlockScope(); // Check to see if the block parent is actually a centering node. if (IsBodyElement((IHTMLElement3)blockParent) || !IsCenteringNode(blockParent)) { blockParent = null; } // Make sure that if we do have a block parents, we are the only thing inside it. // Since we are going to edit the block, we dont want other stuff in there that // will also be changed if (blockParent != null) { string innerHtml = ((IHTMLElement)blockParent).innerText ?? ""; if (!string.IsNullOrEmpty(innerHtml.Trim())) { blockParent = null; } else { int numElements = CountVisibleElements((IHTMLElement)blockParent); if (numElements != 1) { blockParent = null; } } } return(blockParent); }
public static void InsertContentIntoElement(string content, ISmartContent sContent, IContentSourceSidebarContext contentSourceContext, IHTMLElement element) { MshtmlMarkupServices MarkupServices = new MshtmlMarkupServices((IMarkupServicesRaw)element.document); //Note: undo/redo disabled for smart content since undo causes the HTML to get out of sync //with the inserter's settings state, so undo changes will be blown away the next time the //the inserter's HTML is regenerated. Also note that making this insertion without wrapping it //in an undo clears the undo/redo stack, which is what we want for beta. //string undoId = Guid.NewGuid().ToString(); MarkupRange htmlRange = MarkupServices.CreateMarkupRange(element, false); htmlRange.Start.PushCling(true); htmlRange.End.PushCling(true); MarkupServices.Remove(htmlRange.Start, htmlRange.End); htmlRange.Start.PopCling(); htmlRange.End.PopCling(); element.style.padding = ToPaddingString(sContent.Layout); if (sContent.Layout.Alignment == Alignment.None || sContent.Layout.Alignment == Alignment.Right || sContent.Layout.Alignment == Alignment.Left) { element.style.display = "inline"; element.style.marginLeft = "0px"; element.style.marginRight = "0px"; element.style.styleFloat = sContent.Layout.Alignment.ToString().ToLower(CultureInfo.InvariantCulture); } else if (sContent.Layout.Alignment == Alignment.Center) { element.style.styleFloat = Alignment.None.ToString().ToLower(CultureInfo.InvariantCulture); element.style.display = "block"; element.style.marginLeft = "auto"; element.style.marginRight = "auto"; } // Clear out any width on the overall smart content block, if the element is centered, we will add the width back in later // after we calcuate it from the childern, the current width value is stale. element.style.width = ""; //Note: we use MarkupServices to insert the content so that IE doesn't try to fix up URLs. //Element.insertAdjacentHTML() is a no-no because it rewrites relaive URLs to include //the fullpath from the local filesytem. //MarkupServices.ParseString() doesn't attempt to fix up URLs, so its safe to use. //We will now stage the new content into a MarkupContainer, and then move it into //the working document. MarkupPointer sc1 = MarkupServices.CreateMarkupPointer(); MarkupPointer sc2 = MarkupServices.CreateMarkupPointer(); //Create a temporary document from the html and set the start/end pointers to the //start and end of the document. MarkupServices.ParseString(content, sc1, sc2); IHTMLDocument2 doc = sc1.GetDocument(); MarkupRange stagingRange = MarkupServices.CreateMarkupRange(sc1, sc2); stagingRange.MoveToElement(doc.body, false); //IE7 hack: fixes bug 305512. Note that this will destroy the inner content of the element, //so make sure it is called before the refreshed content is inserted. BeforeInsertInvalidateHackForIE7(element); //move the content from the staging area into the actual insertion point. MarkupServices.Move(stagingRange.Start, stagingRange.End, htmlRange.End); if (sContent.Layout.Alignment == Alignment.Center) { MarkupContext mc = htmlRange.End.Right(false); MarkupRange range = MarkupServices.CreateMarkupRange(mc.Element, false); IHTMLElement[] childern = range.GetTopLevelElements(MarkupRange.FilterNone); int maxWidth = 0; foreach (IHTMLElement child in childern) { maxWidth = Math.Max(maxWidth, child.offsetWidth); } if (maxWidth != 0) { mc.Element.style.width = maxWidth; } } // Let the context provider know the smart content was edited. string contentSourceId, contentId; ContentSourceManager.ParseContainingElementId(element.id, out contentSourceId, out contentId); contentSourceContext.OnSmartContentEdited(contentId); }
private MarkupRange ApplyInlineTag(_ELEMENT_TAG_ID tagId, string attributes, MarkupRange selection) { MarkupRange newSelection = _markupServices.CreateMarkupRange(); // If the selection is empty, then just insert the tag if (selection.IsEmpty()) { newSelection.MoveToElement(WrapRangeInSpanElement(tagId, attributes, selection), false); return(newSelection); } // Start at the beginning of the selection move forward until you hit a block start/exit context or the end of the selection bool keepApplying = true; MarkupContext contextStart = new MarkupContext(); MarkupRange blockFreeRange = _markupServices.CreateMarkupRange(selection.Start.Clone(), selection.Start.Clone()); MarkupPointer currentPointer = _markupServices.CreateMarkupPointer(blockFreeRange.Start); while (keepApplying) { // Check if moving right would be beyond the bounds of the selection. if (currentPointer.IsRightOfOrEqualTo(selection.End)) { // We've hit the end of the selection, so we're done. keepApplying = false; Debug.Assert(blockFreeRange.Start.IsLeftOfOrEqualTo(selection.End)); blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer); if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange)) { newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange)); } break; } // Check if the next context is entering or exiting a block. currentPointer.Right(false, contextStart); if (contextStart.Element != null && ElementFilters.IsBlockElement(contextStart.Element)) { switch (contextStart.Context) { case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope: case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope: { blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer); if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange)) { newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange)); } if (contextStart.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope) { blockFreeRange.Start.MoveAdjacentToElement(contextStart.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); } else if (contextStart.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope) { blockFreeRange.Start.MoveAdjacentToElement(contextStart.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); } blockFreeRange.Collapse(true); } break; case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None: { keepApplying = false; blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer); if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange)) { newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange)); } } break; default: break; } } // Finally, move our pointer currentPointer.Right(true); } if (newSelection.Positioned) { newSelection.Trim(); } return(newSelection); }
public MarkupServicesWordHelper(MshtmlMarkupServices markupServices) { MarkupServices = markupServices; _p = MarkupServices.CreateMarkupPointer(); _p2 = MarkupServices.CreateMarkupPointer(); }
/// <summary> /// Takes the source HTML and makes necessary modifications to keep the source formatting as if it were to be /// pasted into the destination range. /// </summary> /// <param name="sourceRange">The range containing the HTML that is being copied.</param> /// <param name="destinationRange">The range that the source HTML will be copied to.</param> /// <returns>A serialized string of the source HTML with necessary modifications to keep the source formatting /// or null if unsuccessful.</returns> private string KeepSourceFormatting(MarkupRange sourceRange, MarkupRange destinationRange) { Debug.Assert(sourceRange.Start.Container.GetOwningDoc() == destinationRange.Start.Container.GetOwningDoc(), "Ranges must share an owning document!"); // We will temporarily add comments to the destination document to mark the destinationRange. IHTMLElement startComment = null; IHTMLElement endComment = null; try { // This is our true destination document. IHTMLDocument2 destinationDocument = destinationRange.Start.Container.Document; MshtmlMarkupServices destinationMarkupServices = new MshtmlMarkupServices((IMarkupServicesRaw)destinationDocument); // However, we'll use a temp destination because we don't want to paste anything into the real // document yet as it could fail, it would fire events, images would start loading, etc. MarkupContainer temporaryDestinationContainer = destinationMarkupServices.CreateMarkupContainer(); MarkupPointer temporaryDestinationPointer = destinationMarkupServices.CreateMarkupPointer(); temporaryDestinationPointer.MoveToContainer(temporaryDestinationContainer, true); // We add in comments to the destination document so that when we copy this range over to the fake // destination we'll be able to find the range again. destinationRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left; destinationRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right; string startMarker = string.Format(CultureInfo.InvariantCulture, "<!--{0}-->", Guid.NewGuid()); destinationMarkupServices.InsertHtml(startMarker, destinationRange.Start); startComment = destinationRange.Start.Right(false).Element; string endMarker = string.Format(CultureInfo.InvariantCulture, "<!--{0}-->", Guid.NewGuid()); destinationMarkupServices.InsertHtml(endMarker, destinationRange.End); endComment = destinationRange.End.Left(false).Element; try { // Copy over the entire destination document into the fake destination document. MarkupRange destinationAll = SelectAll(destinationDocument); destinationMarkupServices.Copy(destinationAll.Start, destinationAll.End, temporaryDestinationPointer); // Find the original destination range in this copy. MarkupRange temporaryDestinationRange = FindMarkedFragment(temporaryDestinationContainer.Document, startMarker, endMarker); if (temporaryDestinationRange != null) { // Do the work to keep the source formatting. MarkupRange inlinedRange = new KeepSourceFormatting(sourceRange, temporaryDestinationRange).Execute(); if (inlinedRange != null) { return(inlinedRange.HtmlText); } } } finally { // WinLive 249077: Clear the temporary destination container, otherwise behaviors may // inadvertently attach to elements in the MarkupContainer. temporaryDestinationContainer.Document.body.innerHTML = String.Empty; } } catch (Exception e) { // I really dont want some funky html on the clipboard that causes a problem with this code // to prevent a paste from going through. Trace.Fail("Failed to get inline css for selection: " + e); } finally { Debug.Assert(startComment is IHTMLCommentElement, "Didn't find start comment or it wasn't created."); if (startComment is IHTMLCommentElement) { HTMLElementHelper.RemoveElement(startComment); } Debug.Assert(endComment is IHTMLCommentElement, "Didn't find end comment or it wasn't created."); if (endComment is IHTMLCommentElement) { HTMLElementHelper.RemoveElement(endComment); } } return(null); }