/// <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> /// 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); }
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> /// Returns a copy of this markup pointer that is positioned at the same location. /// </summary> /// <returns></returns> public MarkupPointer Clone() { MarkupPointer p = MarkupServices.CreateMarkupPointer(this); p.Cling = Cling; p.Gravity = Gravity; p.clingStack = (Stack)clingStack.Clone(); p.gravityStack = (Stack)gravityStack.Clone(); return(p); }
public MarkupServicesWordHelper(MshtmlMarkupServices markupServices) { MarkupServices = markupServices; _p = MarkupServices.CreateMarkupPointer(); _p2 = MarkupServices.CreateMarkupPointer(); }
/// <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); } } } }
/// <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; }
/// <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); }
protected internal override BlogEditingTemplate GenerateBlogTemplate(IHTMLDocument3 doc, IHTMLElement titleElement, IHTMLElement[] allTitleElements, IHTMLElement bodyElement) { // if title is containing with a link then strip the link CleanupContainingAnchorTag(titleElement); string templateHtml = ""; StyleBuilder styleBuilder = new StyleBuilder(); IMarkupServicesRaw rawMarkupServices = doc as IMarkupServicesRaw; MshtmlMarkupServices markupServices = new MshtmlMarkupServices(rawMarkupServices); MarkupPointer startPointer = markupServices.CreateMarkupPointer(titleElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); MarkupPointer endPointer = markupServices.CreateMarkupPointer(bodyElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); MarkupRange range = markupServices.CreateMarkupRange(startPointer, endPointer); IHTMLElement stopElement = range.ParentElement(); IHTMLElement currElement; string titleTemplateText = WrapInHiddenHtml(postTitleClass, BlogEditingTemplate.POST_TITLE_MARKER); AddTitleStyles(titleElement, styleBuilder); currElement = titleElement; while (currElement != null && currElement.sourceIndex != stopElement.sourceIndex) { string className = currElement.tagName + currElement.sourceIndex; titleTemplateText = WriteStartTag(currElement, className) + titleTemplateText + WriteEndTag(currElement); AddFrameStyles(currElement, "." + className, styleBuilder); currElement = currElement.parentElement; } string bodyTemplateText = WrapInHiddenHtml(postBodyClass, BlogEditingTemplate.POST_BODY_MARKER); AddBodyStyles(bodyElement, styleBuilder); currElement = bodyElement; while (currElement != null && currElement.sourceIndex != stopElement.sourceIndex) { string className = currElement.tagName + currElement.sourceIndex; bodyTemplateText = WriteStartTag(currElement, className) + bodyTemplateText + WriteEndTag(currElement); AddFrameStyles(currElement, "." + className, styleBuilder); currElement = currElement.parentElement; } templateHtml = titleTemplateText + bodyTemplateText; currElement = range.ParentElement(); while (currElement != null) { string className = null; if (currElement.tagName == "HTML") { MarkupPointer bodyPointer = markupServices.CreateMarkupPointer(((IHTMLDocument2)doc).body, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); MarkupPointer docPointer = markupServices.CreateMarkupPointer(currElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); MarkupRange headRange = markupServices.CreateMarkupRange(docPointer, bodyPointer); IHTMLElement[] elements = headRange.GetTopLevelElements(new IHTMLElementFilter(IsHeadElement)); if (elements.Length > 0) { //string head = elements[0].innerHTML; string head = ""; //string defaultStyles = "<style>p, h1, h2, h3, h4, h5, h6, blockquote, pre{ padding-top: 1px; }</style>"; styleBuilder.Dispose(); head = String.Format(CultureInfo.InvariantCulture, "<head>{0}<style>{1}</style></head>", head, styleBuilder.ToString()); templateHtml = head + templateHtml; } } else { className = currElement.tagName + currElement.sourceIndex; AddFrameStyles(currElement, "." + className, styleBuilder); } templateHtml = WriteStartTag(currElement, className) + templateHtml + WriteEndTag(currElement); currElement = currElement.parentElement; } //prepend the doctype of the document - this prevents styles in the document from rendering improperly string docType = HTMLDocumentHelper.GetSpecialHeaders((IHTMLDocument2)doc).DocType; //string docType = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"[]>"; if (docType != null) templateHtml = docType + "\r\n" + templateHtml; return new BlogEditingTemplate(templateHtml); }
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); }
/// <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; }
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; }
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; }
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; }
private void RemoveParentAnchorFromTitle(IHTMLElement titleElement, MshtmlMarkupServices markupServices) { try { // Look for a parent <A> anchor tag MarkupPointer startPointer = markupServices.CreateMarkupPointer(titleElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); IHTMLElement parentAnchor = startPointer.GetParentElement(ElementFilters.ANCHOR_ELEMENTS); if (parentAnchor != null) { // To make sure the title is readable, we will replace the anchor tag with a new span tag // that has all of the anchor tag properties we believe need to be propogated. This will // keep the final render as close to the original as possible. // Get the next parent block element to compare against the anchor tag IHTMLElement parentBlock = startPointer.GetParentElement(ElementFilters.BLOCK_OR_TABLE_CELL_ELEMENTS); // If no block element was found we will compare against the body if (parentBlock == null) parentBlock = startPointer.GetParentElement(ElementFilters.BODY_ELEMENT); Debug.Assert(parentBlock != null, "parentBlock was unexpectedly null!"); IHTMLElement newAnchor = markupServices.CreateElement(_ELEMENT_TAG_ID.TAGID_SPAN, string.Empty); IHTMLElement2 newAnchor2 = (IHTMLElement2)newAnchor; IHTMLElement2 parentAnchor2 = (IHTMLElement2)parentAnchor; IHTMLElement2 parentBlock2 = (IHTMLElement2)parentBlock; // Only insert the size if it was redefined from the anchor's parent. That way // relative sizes don't layer on each other. if (!(parentAnchor2.currentStyle.fontSize.Equals(parentBlock2.currentStyle.fontSize))) newAnchor2.runtimeStyle.fontSize = parentAnchor2.currentStyle.fontSize; // Copy all other attributes CopyAnchorAttributes(parentAnchor2, newAnchor2); markupServices.ReplaceElement(parentAnchor, newAnchor); } } catch (Exception ex) { // Any failure is ignorable, we will just continue with the parent anchor tag Debug.WriteLine("Failed to remove parent anchor tag from title, " + ex); } }
/// <summary> /// Generates a blog editing template based on the HTML in a document. /// </summary> /// <param name="doc">The full HTML document</param> /// <param name="titleElement">the element in the document that surrounds the post title text</param> /// <param name="bodyElement">the element in the document that surrounds the post body text</param> /// <returns></returns> protected internal override BlogEditingTemplate GenerateBlogTemplate(IHTMLDocument3 doc, IHTMLElement titleElement, IHTMLElement[] allTitleElements, IHTMLElement bodyElement) { // if title is containing with a link then strip the link CleanupContainingAnchorTag(titleElement); IMarkupServicesRaw rawMarkupServices = doc as IMarkupServicesRaw; MshtmlMarkupServices markupServices = new MshtmlMarkupServices(rawMarkupServices); MarkupPointer startPointer = markupServices.CreateMarkupPointer(titleElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); MarkupPointer endPointer = markupServices.CreateMarkupPointer(bodyElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); MarkupRange range = markupServices.CreateMarkupRange(startPointer, endPointer); IHTMLElement stopElement = range.ParentElement(); IHTMLElement currElement; string titleTemplateText = BlogEditingTemplate.POST_TITLE_MARKER; MarkupPointer siblingPointer = markupServices.CreateMarkupPointer(); bool preserveClear = false; currElement = titleElement; IHTMLElement2 currElement2 = (IHTMLElement2)currElement; while (currElement != null && currElement.sourceIndex != stopElement.sourceIndex) { titleTemplateText = WriteStartTag(currElement, null) + titleTemplateText + WriteEndTag(currElement); currElement2 = (IHTMLElement2)currElement; string styleFloat = currElement2.currentStyle.styleFloat; if (!String.IsNullOrEmpty(styleFloat) && !String.IsNullOrEmpty((string)currElement2.currentStyle.width)) { if (String.Compare(styleFloat, "LEFT", StringComparison.OrdinalIgnoreCase) == 0 || String.Compare(styleFloat, "RIGHT", StringComparison.OrdinalIgnoreCase) == 0) { preserveClear = true; } } currElement = currElement.parentElement; } string bodyTemplateText = BlogEditingTemplate.POST_BODY_MARKER; currElement = bodyElement; MarkupRange currElementRange = markupServices.CreateMarkupRange(); while (currElement != null && currElement.sourceIndex != stopElement.sourceIndex) { // Then we need to look for and preserve siblings with "clear" attribute... IHTMLElement parentElement = currElement.parentElement; if (preserveClear && parentElement != null) { IHTMLElementCollection siblings = (IHTMLElementCollection)parentElement.children; foreach (IHTMLElement sibling in siblings) { if (sibling.sourceIndex == currElement.sourceIndex) continue; siblingPointer.MoveAdjacentToElement(sibling, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd); // Does this sibling end before the current element starts? currElementRange.MoveToElement(currElement, true); if (siblingPointer.IsLeftOfOrEqualTo(currElementRange.Start)) { IHTMLElement2 sibling2 = (IHTMLElement2)sibling; string styleClear = sibling2.currentStyle.clear; if (!String.IsNullOrEmpty(styleClear) && String.Compare(styleClear, "NONE", StringComparison.OrdinalIgnoreCase) != 0) { // Then preserve the clear... titleTemplateText = titleTemplateText + WriteStartTag(sibling, String.Format(@"clear: {0}", styleClear)) + WriteEndTag(sibling); } } } } bodyTemplateText = WriteStartTag(currElement, null) + bodyTemplateText + WriteEndTag(currElement); currElement = currElement.parentElement; } string templateHtml = titleTemplateText + bodyTemplateText; currElement = range.ParentElement(); while (currElement != null) { if (currElement.tagName == "HTML") { MarkupPointer bodyPointer = markupServices.CreateMarkupPointer(((IHTMLDocument2)doc).body, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeBegin); MarkupPointer docPointer = markupServices.CreateMarkupPointer(currElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin); MarkupRange headRange = markupServices.CreateMarkupRange(docPointer, bodyPointer); IHTMLElement[] elements = headRange.GetTopLevelElements(new IHTMLElementFilter(IsHeadElement)); if (elements.Length > 0) { string head = elements[0].innerHTML; //string defaultStyles = "<style>p, h1, h2, h3, h4, h5, h6, blockquote, pre{ padding-top: 1px; }</style>"; head = String.Format(CultureInfo.InvariantCulture, "<head>{0}</head>", head); templateHtml = head + templateHtml; } } templateHtml = WriteStartTag(currElement, null) + templateHtml + WriteEndTag(currElement); currElement = currElement.parentElement; } //prepend the doctype of the document - this prevents styles in the document from rendering improperly string docType = HTMLDocumentHelper.GetSpecialHeaders((IHTMLDocument2)doc).DocType; //string docType = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"[]>"; if (docType != null) templateHtml = docType + "\r\n" + templateHtml; return new BlogEditingTemplate(templateHtml); }