/// <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);
            }
        }
示例#2
0
        /// <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);
        }
示例#3
0
        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);
        }
示例#4
0
        /// <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. &lt;!--StartFragment--&gt; ).</param>
        /// <param name="endMarker">The comment text that marks the end of the fragment
        /// (e.g. &lt;!--EndFragment--&gt; ).</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);
        }