public static MarkupRange GetEditableRange(IHTMLElement e, MshtmlMarkupServices markupServices)
        {
            IHTMLElement3 editableElement = null;

            while (e != null)
            {
                if (((IHTMLElement3)e).isContentEditable)
                {
                    editableElement = (IHTMLElement3)e;
                    if (ElementFilters.IsBlockElement(e))
                    {
                        break;
                    }
                }
                else
                {
                    break;
                }
                e = e.parentElement;
            }

            if (editableElement != null)
            {
                return(markupServices.CreateMarkupRange((IHTMLElement)editableElement, false));
            }
            else
            {
                return(null);
            }
        }
        private string GrowToAnchorParent(HTMLData htmlData)
        {
            if (htmlData.OnlyImageElement == null)
                return null;

            string html;
            // Load up the html document from the clipboard to a document to examine the html about to be inserted
            MshtmlMarkupServices markupServices = new MshtmlMarkupServices(htmlData.HTMLDocument as IMarkupServicesRaw);
            MarkupRange range = markupServices.CreateMarkupRange(htmlData.OnlyImageElement, true);

            // look to see if this is a case where the inserted html is <a>|<img>|</a>
            MarkupContext markupContextStart = range.Start.Left(true);
            MarkupContext markupContextEnd = range.End.Right(true);

            // if that is the cause, change the html about to be inserted to |<a><img></a>|
            if (markupContextStart.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope &&
                markupContextEnd.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope &&
                markupContextStart.Element.tagName == "A" &&
                markupContextEnd.Element.tagName == "A")
            {
                html = markupContextStart.Element.outerHTML;
            }
            else
            {
                html = htmlData.HTMLSelection;
            }

            return html;
        }
        /// <summary>
        /// Initialize word range for the specified markup-range within the document
        /// </summary>
        public MshtmlWordRange(IHTMLDocument document, bool useDocumentSelectionRange, MarkupRangeFilter filter, DamageFunction damageFunction)
        {
            MshtmlMarkupServices markupServices = new MshtmlMarkupServices((IMarkupServicesRaw)document);
            IHTMLDocument2 document2 = (IHTMLDocument2)document;
            MarkupRange markupRange;
            if (useDocumentSelectionRange)
            {
                markupRange = markupServices.CreateMarkupRange(document2.selection);
            }
            else
            {
                // TODO: Although this works fine, it would be better to only spellcheck inside editable regions.
                markupRange = markupServices.CreateMarkupRange(document2.body, false);
            }

            Init(document, markupServices, markupRange, filter, damageFunction, useDocumentSelectionRange);
        }
        private static void PrintHtml(HtmlWriter writer, MshtmlMarkupServices MarkupServices, MarkupRange bounds)
        {
            //create a range to span a single position while walking the doc
            MarkupRange range = MarkupServices.CreateMarkupRange();
            range.Start.MoveToPointer(bounds.Start);
            range.End.MoveToPointer(bounds.Start);

            //create a context that can be reused while walking the document.
            MarkupContext context = new MarkupContext();

            //move the range.End to the right and print out each element along the way
            range.End.Right(true, context);
            while (context.Context != _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None && range.Start.IsLeftOf(bounds.End))
            {
                string text = null;
                if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text)
                {
                    //if this is a text context, then get the text that is between the start and end points.
                    text = range.HtmlText;

                    //the range.HtmlText operation sometimes returns the outer tags for a text node,
                    //so we need to strip the tags.
                    //FIXME: if the Right/Left operations returned the available text value, this wouldn't be necessary.
                    if (text != null)
                        text = StripSurroundingTags(text);
                }
                else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)
                {
                    string htmlText = range.HtmlText;
                    if (context.Element.innerHTML == null && htmlText != null && htmlText.IndexOf("&nbsp;") != -1)
                    {
                        //HACK: Under these conditions, there was a was an invisible NBSP char in the
                        //document that is not detectable by walking through the document with MarkupServices.
                        //So, we force the text of the element to be the &nbsp; char to ensure that the
                        //whitespace that was visible in the editor is visible in the final document.
                        text = "&nbsp;";
                    }
                }

                //print the context.
                printContext(writer, context, text, range);

                //move the start element to the spot where the end currently is so tht there is
                //only ever a single difference in position
                range.Start.MoveToPointer(range.End);

                //move the end to the next position
                range.End.Right(true, context);
            }
        }
Exemple #5
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);
        }
        /// <summary>
        /// Grabs HTML copied in the clipboard and pastes it into the document (pulls in a copy of embedded content too)
        /// </summary>
        protected override bool DoInsertData(DataAction action, MarkupPointer begin, MarkupPointer end)
        {
            using (new WaitCursor())
            {
                try
                {
                    string baseUrl = UrlHelper.GetBasePathUrl(DataMeister.HTMLData.SourceURL);
                    string html = DataMeister.HTMLData.HTMLSelection;

                    //Check to see if the selection has an incomplete unordered list
                    var finder = new IncompleteListFinder(html);
                    finder.Parse();

                    if ((!EditorContext.CleanHtmlOnPaste) || finder.HasIncompleteList)
                    {
                        using (IUndoUnit undoUnit = EditorContext.CreateInvisibleUndoUnit())
                        {
                            // Create a new MarkupContainer off of EditorContext's document that contains the source HTML
                            // with comments marking the start and end selection.
                            MarkupContainer sourceContainer = EditorContext.MarkupServices.ParseString(DataMeister.HTMLData.HTMLWithMarkers);

                            // MSHTML's ParseString implementation clears all the attributes on the <body> element, so we
                            // have to manually add them back in.
                            CopyBodyAttributes(DataMeister.HTMLData.HTMLWithMarkers, sourceContainer.Document.body);

                            MarkupRange sourceRange = FindMarkedFragment(sourceContainer.Document,
                                HTMLDataObject.START_FRAGMENT_MARKER, HTMLDataObject.END_FRAGMENT_MARKER);
                            MshtmlMarkupServices sourceContainerMarkupServices =
                                new MshtmlMarkupServices((IMarkupServicesRaw)sourceContainer.Document);

                            // Some applications may not add the correct fragment markers (e.g. copying from Fiddler from
                            // the Web Sessions view). We'll just select the entire <body> of the clipboard in this case.
                            if (sourceRange == null)
                            {
                                sourceRange = sourceContainerMarkupServices.CreateMarkupRange(sourceContainer.Document.body, false);
                            }
                            else
                            {
                                // Make sure that we don't try to copy just parts of a table/list. We need to include the
                                // parent table/list.
                                if (!EditorContext.CleanHtmlOnPaste)
                                {
                                    ExpandToIncludeTables(sourceRange, sourceContainerMarkupServices);
                                }
                                ExpandToIncludeLists(sourceRange, sourceContainerMarkupServices);
                            }

                            if (sourceRange != null)
                            {
                                if (!EditorContext.CleanHtmlOnPaste)
                                {
                                    // WinLive 273280: Alignment on a table acts like a float, which can throw off the layout of the rest of
                                    // the document. If there is nothing before or after the table, then we can safely remove the alignment.
                                    RemoveAlignmentIfSingleTable(sourceRange);

                                    // Serialize the source HTML to a string while keeping the source formatting.
                                    MarkupRange destinationRange = EditorContext.MarkupServices.CreateMarkupRange(begin.Clone(), end.Clone());
                                    html = KeepSourceFormatting(sourceRange, destinationRange);
                                }
                                else
                                {
                                    html = sourceRange.HtmlText;
                                }
                            }

                            undoUnit.Commit();
                        }

                        Trace.Assert(html != null, "Inline source CSS failed!");
                    }

                    if (html == null)
                    {
                        html = DataMeister.HTMLData.HTMLSelection;
                    }

                    if (IsPasteFromSharedCanvas(DataMeister))
                    {
                        if (action == DataAction.Copy)
                        {
                            // WinLive 96840 - Copying and pasting images within shared canvas should persist source
                            // decorator settings. "wlCopySrcUrl" is inserted while copy/pasting within canvas.
                            html = EditorContext.FixImageReferences(ImageCopyFixupHelper.FixupSourceUrlForCopy(html),
                                                                    DataMeister.HTMLData.SourceURL);
                        }
                    }
                    else
                    {
                        html = EditorContext.FixImageReferences(html, DataMeister.HTMLData.SourceURL);

                        HtmlCleanupRule cleanupRule = HtmlCleanupRule.Normal;
                        if (IsOfficeHtml(DataMeister.HTMLData))
                            cleanupRule = HtmlCleanupRule.PreserveTables;

                        // In Mail, we want to preserve the style of the html that is on the clipboard
                        // Whereas in Writer we by default want to remove formatting so it looks like your blog theme
                        if (EditorContext.CleanHtmlOnPaste)
                        {
                            // optionally cleanup the html
                            html = EditorContext.HtmlGenerationService.CleanupHtml(html, baseUrl, cleanupRule);
                        }
                        else
                        {
                            html = HtmlCleaner.StripNamespacedTagsAndCommentsAndMarkupDirectives(html);
                        }

                        // standard fixups
                        html = EditorContext.HtmlGenerationService.GenerateHtmlFromHtmlFragment(html, baseUrl);
                    }

                    // insert the content
                    if (EditorContext.MarshalHtmlSupported)
                        EditorContext.InsertHtml(begin, end, html, DataMeister.HTMLData.SourceURL);
                    else if (EditorContext.MarshalTextSupported)
                    {
                        // This is called only in the case that we're attempting to marshal HTML, but only
                        // text is supported. In this case, we should down convert to text and provide that.
                        html = HTMLDocumentHelper.HTMLToPlainText(html);
                        EditorContext.InsertHtml(begin, end, html, DataMeister.HTMLData.SourceURL);
                    }
                    else
                        Debug.Assert(false, "Html being inserted when text or html isn't supported.");

                    // Now select what was just inserted
                    EditorContext.MarkupServices.CreateMarkupRange(begin, end).ToTextRange().select();

                    //place the caret at the end of the inserted content
                    //EditorContext.MoveCaretToMarkupPointer(end, true);
                    return true;
                }
                catch (Exception e)
                {
                    //bugfix 1696, put exceptions into the trace log.
                    Trace.Fail("Exception while inserting HTML: " + e.Message, e.StackTrace);
                    return false;
                }
            }
        }
        /// <summary>
        /// Creates a MarkupRange that contains the entire provided document.
        /// </summary>
        /// <param name="document">The document to select.</param>
        /// <returns>A MarkupRange that contains the entire document.</returns>
        private MarkupRange SelectAll(IHTMLDocument2 document)
        {
            MshtmlMarkupServices markupServices = new MshtmlMarkupServices((IMarkupServicesRaw)document);
            MarkupRange entireDocument = markupServices.CreateMarkupRange(((IHTMLDocument3)document).documentElement, true);

            // Make sure the doctype and anything else outside the root element is selected too.
            MarkupContext context = entireDocument.Start.Left(true);
            while (context.Context != _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None)
            {
                context = entireDocument.Start.Left(true);
            }

            context = entireDocument.End.Right(true);
            while (context.Context != _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None)
            {
                context = entireDocument.End.Right(true);
            }

            return entireDocument;
        }
        /// <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);
        }
        private void UpdateImageLink(string href, IHTMLElement ImgElement, ILinkOptions defaultOptions)
        {
            MshtmlMarkupServices markupServices = new MshtmlMarkupServices((IMarkupServicesRaw)ImgElement.document);
            IHTMLElement parentElement = ImgElement.parentElement;
            if (!(parentElement is IHTMLAnchorElement))
            {
                parentElement = markupServices.CreateElement(_ELEMENT_TAG_ID.TAGID_A, null);
                MarkupRange range = markupServices.CreateMarkupRange();
                range.MoveToElement(ImgElement, true);
                markupServices.InsertElement(parentElement, range.Start, range.End);

                //set the default target attribute for the new element
                string target = defaultOptions.ShowInNewWindow ? "_blank" : null;
                IHTMLAnchorElement htmlAnchorElement = (parentElement as IHTMLAnchorElement);
                if (htmlAnchorElement.target != target) //don't set the target to null if its already null (avoids adding empty target attr)
                    htmlAnchorElement.target = target;

                ImageViewer viewer = DhtmlImageViewers.GetImageViewer(DhtmlImageViewer);
                if (viewer != null)
                {
                    if (defaultOptions.UseImageViewer)
                        viewer.Apply(htmlAnchorElement, defaultOptions.ImageViewerGroupName);
                }
            }
            parentElement.setAttribute("href", href, 0);
        }
        /// <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);
        }
        /// <summary>
        /// Returns true if there is non-empty content between 2 pointers.
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns>true if there is visible content between the pointers</returns>
        private bool HasContentBetween(MarkupPointer start, MarkupPointer end)
        {
            MarkupRange range = MarkupServices.CreateMarkupRange(start, end);

            return(!range.IsEmptyOfContent(false));
        }
        void IContentSourceSidebarContext.OnSmartContentEdited(string contentId)
        {
            IHTMLElement element = FindSmartContentElementByContentId(contentId);

            Debug.Assert(element != null && element.document != null, "Couldn't find the SmartContent specified or it was invalid!");

            if (element != null && element.document != null)
            {
                // When a photo album is updated inline by EditableSmartContent.SaveEditedSmartContent(), there is no
                // notification to the editor that any HTML was changed. However, because the inline edit field is
                // rewritten, we need to spell check it again.
                MshtmlMarkupServices markupServices = new MshtmlMarkupServices((IMarkupServicesRaw)element.document);
                _normalHtmlContentEditor.DamageServices.AddDamage(markupServices.CreateMarkupRange(element, false));
            }
        }
        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;
        }
        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 virtual string GetEditedHtml(bool useXhtml, bool doCleanup)
        {
            try
            {
                if (doCleanup || useXhtml)
                {
                    MshtmlMarkupServices markupServices = new MshtmlMarkupServices(HTMLElement.document as IMarkupServicesRaw);
                    if (doCleanup)
                    {
                        MarkupRange bodyRange = markupServices.CreateMarkupRange(((IHTMLDocument2)HTMLElement.document).body, false);
                        bodyRange.RemoveElementsByTagId(_ELEMENT_TAG_ID.TAGID_FONT, true);
                    }

                    if (useXhtml)
                    {
                        MarkupRange bounds = markupServices.CreateMarkupRange(HTMLElement, false);
                        string xhtml = FormattedHtmlPrinter.ToFormattedHtml(markupServices, bounds);
                        return doCleanup ? CleanupHtml(xhtml, true) : xhtml;
                    }
                }
            }
            catch (Exception e)
            {
                // E.g. this failure case: <pre><b></pre></b>
                Trace.Fail("Exception generating XHTML: " + e.ToString());
            }

            string html = HTMLElement.innerHTML ?? String.Empty;
            if (doCleanup)
                return CleanupHtml(html, false);
            else
                return html;
        }
        public static MarkupRange ApplyInlineTag(MshtmlMarkupServices markupServices, _ELEMENT_TAG_ID tagId, string attributes, MarkupRange selection, bool toggle)
        {
            HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices);

            // Aligning the with the behavior of Word, we will make <SUP> and <SUB> mutually exclusive.
            // That is, if you are applying <SUB> to a selection that has <SUP> applied already, we will first remove the <SUP>, and vice versa.
            // Wait, if empty and we're on the very end of the already existing formatting, then we just want to jump out and apply...

            MarkupRange selectionToApply = selection.Clone();
            if (toggle)
            {
                // If already entirely inside the tag
                //     If empty and just inside of the closing tag, then jump outside the closing tag
                //     Else remove the tag
                // If already entirely outside the tag
                //     If empty, apply the tag and put selection inside
                //     If non-empty, then apply tag and reselect
                // If partially inside the tag
                //     Remove the tag

                _ELEMENT_TAG_ID mutuallyExclusiveTagId = _ELEMENT_TAG_ID.TAGID_NULL;
                switch (tagId)
                {
                    case _ELEMENT_TAG_ID.TAGID_SUP:
                        mutuallyExclusiveTagId = _ELEMENT_TAG_ID.TAGID_SUB;
                        break;
                    case _ELEMENT_TAG_ID.TAGID_SUB:
                        mutuallyExclusiveTagId = _ELEMENT_TAG_ID.TAGID_SUP;
                        break;
                    default:

                        break;
                }

                if (selection.IsEmpty())
                {
                    // If the selection is empty and we're just inside the tagId closing tag (meaning that there is no text before the closing tag),
                    // then we just hop outside of the tagId closing tag.

                    bool exitScopeMatchesTagIdToBeApplied;
                    MarkupPointer pointerOutsideTagIdScope = htmlStyleHelper.NextExitScopeWithoutInterveningText(selection,
                                                                            tagId,
                                                                            mutuallyExclusiveTagId,
                                                                            out exitScopeMatchesTagIdToBeApplied);

                    if (pointerOutsideTagIdScope != null)
                    {
                        selectionToApply = markupServices.CreateMarkupRange(pointerOutsideTagIdScope, pointerOutsideTagIdScope);
                        if (exitScopeMatchesTagIdToBeApplied)
                        {
                            return selectionToApply;
                        }
                        // else we still need to apply tagId
                    }
                }

                // If a mutually exclusive tag is applied, then remove it.
                if (selectionToApply.IsTagId(mutuallyExclusiveTagId, true))
                {
                    selectionToApply.RemoveElementsByTagId(mutuallyExclusiveTagId, false);
                }

                // If this tag is already applied, then remove it and return.
                if (selectionToApply.IsTagId(tagId, true))
                {
                    selectionToApply.RemoveElementsByTagId(tagId, false);
                    return selectionToApply;
                }
            }

            return htmlStyleHelper.ApplyInlineTag(tagId, attributes, selectionToApply);
        }
        public static void ChangeElementTagIds(MshtmlMarkupServices markupServices, MarkupRange selection, _ELEMENT_TAG_ID[] tagBefore, _ELEMENT_TAG_ID tagAfter)
        {
            HtmlStyleHelper htmlStyleHelper = new HtmlStyleHelper(markupServices);

            int parentStartDiff = 0;
            MarkupRange rangeToChange;
            bool selectionStartedEmpty = selection.IsEmpty();
            if (selectionStartedEmpty)
            {
                // Operate on parent block element
                rangeToChange = markupServices.CreateMarkupRange(selection.ParentBlockElement());
                parentStartDiff = selection.Start.MarkupPosition - rangeToChange.Start.MarkupPosition;
            }
            else
            {
                rangeToChange = selection;

                // If expanding the selection would not include any new text, then expand it.
                // <h1>|abc|</h1> --> |<h1>abc</h1>|
                rangeToChange.MoveOutwardIfNoText();
            }

            IHTMLElementFilter[] filters = new IHTMLElementFilter[tagBefore.Length];

            for (int i = 0; i < tagBefore.Length; i++)
                filters[i] = ElementFilters.CreateTagIdFilter(markupServices.GetNameForTagId(tagBefore[i]));

            IHTMLElement[] elements = rangeToChange.GetElements(ElementFilters.CreateCompoundElementFilter(filters), false);
            foreach (IHTMLElement element in elements)
            {
                MarkupRange elementRange = markupServices.CreateMarkupRange(element);

                int startPositionDiff = rangeToChange.Start.MarkupPosition - elementRange.Start.MarkupPosition;
                int endPositionDiff = rangeToChange.End.MarkupPosition - elementRange.End.MarkupPosition;

                // @RIBBON TODO: Appropriately preserve element attributes when changing tag ids?
                MarkupRange newElementRange = markupServices.CreateMarkupRange(htmlStyleHelper.WrapRangeInSpanElement(tagAfter, null, elementRange));
                markupServices.RemoveElement(element);

                MarkupPointer startPointer = rangeToChange.Start.Clone();
                startPointer.MoveToMarkupPosition(startPointer.Container, newElementRange.Start.MarkupPosition + startPositionDiff);
                if (startPointer.IsLeftOf(rangeToChange.Start))
                    rangeToChange.Start.MoveToPointer(startPointer);

                MarkupPointer endPointer = rangeToChange.End.Clone();
                endPointer.MoveToMarkupPosition(endPointer.Container, newElementRange.End.MarkupPosition + endPositionDiff);
                if (endPointer.IsLeftOf(elementRange.End))
                    rangeToChange.End.MoveToPointer(endPointer);
            }

            if (selectionStartedEmpty)
            {
                selection.Start.MoveToMarkupPosition(selection.Start.Container, rangeToChange.Start.MarkupPosition + parentStartDiff);
                selection.Collapse(true);
            }
        }
        protected ContentSelection(IHtmlEditorComponentContext editorComponentContext, IHTMLElement element, SmartContentState contentState)
        {
            _editorComponentContext = editorComponentContext;
            _markupServices = editorComponentContext.MarkupServices;
            _element = element;
            _contentState = contentState;

            _markupRange = _markupServices.CreateMarkupRange(_element, true);
            _markupRange.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
            _markupRange.End.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
            _markupRange.Start.Cling = false;
            _markupRange.End.Cling = false;
        }
 /// <summary>
 /// WinLive 211555: To avoid confusing our users with warning messages, we strip any &lt;noscript&gt; and
 /// &lt;noembed&gt; elements.
 /// </summary>
 private void RemoveNoShowElements(IHTMLDocument2 doc)
 {
     IMarkupServicesRaw rawMarkupServices = (IMarkupServicesRaw)doc;
     MshtmlMarkupServices markupServices = new MshtmlMarkupServices(rawMarkupServices);
     MarkupRange bodyRange = markupServices.CreateMarkupRange(doc.body, false);
     foreach (IHTMLElement noShowElement in bodyRange.GetElements(e => e is IHTMLNoShowElement, true))
     {
         HTMLElementHelper.RemoveElement(noShowElement);
     }
 }