Example #1
0
        public override void Invoke(MarkupContext context)
        {
            // Avoid parsing Html if required
            if (MightContainBlockquotes())
            {
                Console.WriteLine($"Formatting blockquotes: {context.Html.Path}");

                var htmlDoc = new HtmlDocument();
                htmlDoc.LoadHtml(context.Html.Content);

                foreach (var quote in GetBlockquotes(htmlDoc))
                {
                    quote.AddClass("alert");
                    quote.AddClass("alert-primary");
                    quote.Attributes.Add("role", "alert");
                }

                context.Html.Content = htmlDoc.DocumentNode.OuterHtml;
            }


            // Simple text check
            // Can return false positives but not false negatives
            bool MightContainBlockquotes() => context.Html.Content.Contains("<blockquote");

            HtmlNodeCollection GetBlockquotes(HtmlDocument htmlDoc)
            {
                // Markdig converts quotes to: <blockquote><p>**Html-Content-Here**</p></blockquote>
                return(htmlDoc.DocumentNode.SelectNodes("//blockquote/p"));
            }
        }
Example #2
0
        /// <summary>
        /// Inspects the range if a new font tag need to be added to apply formatting for the range.
        /// Call this only for ranges that are inside a header element
        /// </summary>
        private void WrapRangeInFontIfNecessary(MarkupRange currentRange, bool turnBold)
        {
            // Check if there is an existing font/span tag that completely wraps this range,
            // we can just use that instead of inserting a new one
            MarkupContext workingContext = new MarkupContext();
            bool          wrapFont       = true;
            MarkupPointer start          = currentRange.Start.Clone();

            start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
            start.Right(false, workingContext); // Look to the right to see what we have there
            if (workingContext.Element != null && workingContext.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope &&
                IsFontableElement(workingContext.Element))
            {
                start.MoveAdjacentToElement(workingContext.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                if (currentRange.End.IsEqualTo(start))
                {
                    // There is an existing <FONT>/<SPAN> enclosing the range, no need to wrap again
                    wrapFont = false;

                    // set its font-weight
                    workingContext.Element.style.fontWeight = turnBold ? "bold" : "normal";
                }
            }

            if (wrapFont)
            {
                string weightAttribute = String.Format(CultureInfo.InvariantCulture, "style=\"font-weight: {0}\"", turnBold ? "bold" : "normal");
                HtmlStyleHelper.WrapRangeInElement(markupServices, currentRange, _ELEMENT_TAG_ID.TAGID_FONT, weightAttribute);
            }
        }
        private IHTMLElement GetNextElement(MarkupPointer start, MarkupRange boundaries, IHTMLElementFilter filter, bool forward)
        {
            start = start.Clone();
            MarkupPointer        boundary    = forward ? boundaries.End : boundaries.Start;
            MarkupContext        moveResult  = new MarkupContext();
            _MARKUP_CONTEXT_TYPE skipContext = _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope;

            //advance the pointer
            if (forward)
            {
                start.Right(true, moveResult);
            }
            else
            {
                start.Left(true, moveResult);
            }

            while (forward ? start.IsLeftOf(boundary) : start.IsRightOf(boundary))
            {
                if (moveResult.Element != null && moveResult.Context != skipContext && filter(moveResult.Element))
                {
                    return(moveResult.Element);
                }
                //advance the pointer
                if (forward)
                {
                    start.Right(true, moveResult);
                }
                else
                {
                    start.Left(true, moveResult);
                }
            }
            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);
        }
Example #5
0
        private static void NormalizeBounds(ref MarkupRange bounds)
        {
            bool cloned = false;

            if (bounds.Start.IsRightOf(bounds.End))
            {
                if (!cloned)
                {
                    cloned = true;
                    bounds = bounds.Clone();
                }
                bounds.Normalize();
            }

            MarkupContext ctx = bounds.Start.Right(false);

            while (ctx.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope &&
                   ElementFilters.IsBlockElement(ctx.Element))
            {
                if (!cloned)
                {
                    cloned = true;
                    bounds = bounds.Clone();
                }
                bounds.Start.Right(true);
                bounds.Start.Right(false, ctx);
            }
        }
Example #6
0
        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);
            }
        }
Example #7
0
        private void MovePointerRightUntilRegionBreak(MarkupPointer p, IHTMLElementFilter regionBreakFilter, MarkupPointer rightBoundary)
        {
            MarkupContext moveContext = new MarkupContext();

            while (p.IsLeftOf(rightBoundary))
            {
                p.Right(true, moveContext);
                if (moveContext.Element != null && regionBreakFilter(moveContext.Element))
                {
                    p.Left(true);
                    return;
                }
            }
        }
Example #8
0
        /*private void DumpBreakRegions(params ElementBreakRegion[] breakRegions)
         * {
         *  foreach(ElementBreakRegion breakRegion in breakRegions)
         *  {
         *      String elementStartName = breakRegion.BreakStartElement != null ? breakRegion.BreakStartElement.tagName : "";
         *      String elementEndName = breakRegion.BreakEndElement != null ? breakRegion.BreakEndElement.tagName : "";
         *      String breakContent = breakRegion.ContentRange.Text;
         *      if(breakContent != null)
         *          breakContent = breakContent.Replace('\r', ' ').Replace('\n', ' ');
         *      else
         *          breakContent = "";
         *      Trace.WriteLine(String.Format("<{0}>{1}<{2}>", elementStartName, breakContent, elementEndName));
         *  }
         * }*/

        /// <summary>
        /// Splits the specified range into regions based on a region break filter.
        /// </summary>
        /// <param name="range"></param>
        /// <returns></returns>
        private ElementBreakRegion[] SplitIntoElementRegions(MarkupRange range, IHTMLElementFilter regionBreakFilter)
        {
            ArrayList   regions    = new ArrayList();
            MarkupRange blockRange = _markupServices.CreateMarkupRange();

            blockRange.Start.MoveToPointer(range.Start);
            blockRange.End.MoveToPointer(range.Start);
            MarkupContext moveContext = new MarkupContext();

            ElementBreakRegion currentRegion = new ElementBreakRegion(blockRange, null, null);

            while (currentRegion.ContentRange.End.IsLeftOf(range.End))
            {
                if (moveContext.Element != null)
                {
                    if (regionBreakFilter(moveContext.Element))
                    {
                        //move the end of the region back before the break element to close this region
                        currentRegion.ContentRange.End.Left(true);

                        //save the closed region and start the next region
                        currentRegion.BreakEndElement = moveContext.Element;
                        regions.Add(currentRegion);
                        currentRegion = new ElementBreakRegion(currentRegion.ContentRange.Clone(), moveContext.Element, null);
                        currentRegion.ContentRange.Start.MoveToPointer(currentRegion.ContentRange.End);

                        //move the region start over the break element
                        currentRegion.ContentRange.Start.Right(true, moveContext);
                        currentRegion.ContentRange.End.MoveToPointer(currentRegion.ContentRange.Start);
                    }
                }
                currentRegion.ContentRange.End.Right(true, moveContext);
            }

            //save the last break region
            if (moveContext.Element != null && regionBreakFilter(moveContext.Element))
            {
                //move the end of the region back before the break element to close this region
                currentRegion.ContentRange.End.Left(true);
            }
            if (currentRegion.ContentRange.End.IsRightOf(range.End))
            {
                currentRegion.ContentRange.End.MoveToPointer(range.End);
            }
            regions.Add(currentRegion);

            return((ElementBreakRegion[])regions.ToArray(typeof(ElementBreakRegion)));
        }
Example #9
0
        /// <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));
        }
        /// <summary>
        /// Returns the markup pointer to the position of the first exit scope of type tagId or type terminatingTagId which follows this markup range.
        /// Returns null if text exists between the range and such an exit scope, or if there is no such exit scope.
        /// </summary>
        /// <param name="terminatingTagId"></param>
        /// <returns></returns>
        internal MarkupPointer NextExitScopeWithoutInterveningText(MarkupRange selection, _ELEMENT_TAG_ID tagId, _ELEMENT_TAG_ID terminatingTagId, out bool primaryTagIdMatch)
        {
            MarkupContext context = new MarkupContext();

            MarkupPointer pointer = selection.End.Clone();

            primaryTagIdMatch = false;

            while (true)
            {
                pointer.Right(true, context);
                if (context.Element == null)
                {
                    return(null);
                }

                switch (context.Context)
                {
                case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None:
                    return(null);

                case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope:
                {
                    if (_markupServices.GetElementTagId(context.Element) == tagId)
                    {
                        primaryTagIdMatch = true;
                        return(pointer);
                    }

                    if (terminatingTagId != _ELEMENT_TAG_ID.TAGID_NULL && terminatingTagId == _markupServices.GetElementTagId(context.Element))
                    {
                        return(pointer);
                    }
                }

                break;

                case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope:
                case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope:
                    break;

                case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text:
                    return(null);
                }
            }
        }
Example #11
0
        /// <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);
        }
Example #12
0
        private void ApplyBlockStyleToRange(_ELEMENT_TAG_ID styleTagId, MarkupRange range, MarkupRange maximumBounds)
        {
            //update the range cling and gravity so it will stick with the re-arranged block content
            range.Start.PushCling(false);
            range.Start.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Left);
            range.End.PushCling(false);
            range.End.PushGravity(_POINTER_GRAVITY.POINTER_GRAVITY_Right);

            try
            {
                MarkupPointer deeperPoint    = GetDeeperPoint(range.Start, range.End);
                MarkupPointer insertionPoint = _markupServices.CreateMarkupPointer(deeperPoint);
                insertionPoint.Cling = false;

                //if the insertion point parent block contains content, split the block.  If the parent
                //block is now empty, then just delete the parent block.
                IHTMLElement parentBlock =
                    insertionPoint.GetParentElement(
                        ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS,
                                                                   new IHTMLElementFilter(IsSplitStopElement)));

                //temporarily stage the range content at the end of the document so that the split
                //operation doesn't damage the original range content
                MarkupRange stagedBlockContent = _markupServices.CreateMarkupRange();
                stagedBlockContent.Start.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Left;
                stagedBlockContent.End.Gravity   = _POINTER_GRAVITY.POINTER_GRAVITY_Right;
                stagedBlockContent.Start.MoveAdjacentToElement(GetBodyElement(), _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd);
                stagedBlockContent.End.MoveToPointer(stagedBlockContent.Start);

                MarkupPointer stagedBlockInsertionPoint = _markupServices.CreateMarkupPointer(GetBodyElement(), _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd);

                stagedBlockInsertionPoint.Gravity = _POINTER_GRAVITY.POINTER_GRAVITY_Right;

                // Pass over all opening elements between parent's adj_beforeend and the start of selection (range.start)
                // Any element (_enterscope) that is not closed before the close of selection is essentially
                // containing the selection completely, and needs to be copied into the staging area.
                // ex:   <p><a href="http://msn.com">abc[selection]def</a></p>
                // Here, the <a> element encloses completely the selection and needs to be explicitly copied to the
                // staging area.
                for (MarkupPointer i = _markupServices.CreateMarkupPointer(parentBlock, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin);
                     i.IsLeftOf(range.Start); i.Right(true))
                {
                    MarkupContext iContext = i.Right(false);

                    if (iContext.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && iContext.Element is IHTMLAnchorElement)
                    {
                        MarkupPointer j = _markupServices.CreateMarkupPointer(iContext.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd);
                        if (j.IsRightOfOrEqualTo(range.End))
                        {
                            // Copy the tag at posn. i to location stagedBlockInsertionPoint
                            // This is openning tag. Closing tag will be
                            // automatically added by MSHTML.
                            MarkupPointer i1 = i.Clone();
                            i1.Right(true);
                            _markupServices.Copy(i, i1, stagedBlockInsertionPoint);
                            // Skip over the closing tag, so stagedBlockInsertionPoint points between the openning and the closing
                            stagedBlockInsertionPoint.Left(true);
                        }
                        j.Unposition();
                    }
                }

                //move the range content into the staged position
                _markupServices.Move(range.Start, range.End, stagedBlockInsertionPoint); stagedBlockInsertionPoint.Unposition();

                bool splitBlock = !RemoveEmptyParentBlock(range.Clone(), parentBlock, maximumBounds);

                // If the range endpoint NOT chosen as the insertion point lies in a different block element, and
                // that parent block element is now empty, then just delete the parent block.
                MarkupPointer shallowerPoint    = (deeperPoint.IsEqualTo(range.Start)) ? range.End : range.Start;
                MarkupPointer nonInsertionPoint = _markupServices.CreateMarkupPointer(shallowerPoint);

                IHTMLElement otherParentBlock =
                    nonInsertionPoint.GetParentElement(
                        ElementFilters.CreateCompoundElementFilter(ElementFilters.BLOCK_ELEMENTS,
                                                                   new IHTMLElementFilter(IsSplitStopElement)));
                if (otherParentBlock.sourceIndex != parentBlock.sourceIndex)
                {
                    RemoveEmptyParentBlock(range.Clone(), otherParentBlock, maximumBounds);
                }

                if (splitBlock)
                {
                    //split the block at the insertion point
                    SplitBlockForApplyingBlockStyles(insertionPoint, maximumBounds);
                }

                //move the staged block content back to the insertion point and setup the range pointers
                //to wrap the re-inserted block content.
                range.Start.MoveToPointer(insertionPoint);
                range.End.MoveToPointer(insertionPoint);
                _markupServices.Move(stagedBlockContent.Start, stagedBlockContent.End, insertionPoint);

                //Note: the range is now re-positioned around the same content, but all of the parent
                //elements have been closed to prepare for getting new parent block elements around the
                //range

                //convert the range's content into block regions and remove block elements that were
                //parenting the regions.
                InnerBlockRegion[] blockRegions = GetNormalizedBlockContentRegions(range);

                //update all of the block regions with the desired new block style element
                foreach (InnerBlockRegion blockRegion in blockRegions)
                {
                    IHTMLElement newParentElement = WrapRangeInBlockElement(blockRegion.ContentRange, styleTagId);

                    // The old parent element may have had an alignment set on it.
                    IHTMLElement oldParentElement = blockRegion.OldParentElement ?? parentBlock;
                    if (oldParentElement != null)
                    {
                        string oldAlignment = oldParentElement.getAttribute("align", 2) as string;
                        if (!String.IsNullOrEmpty(oldAlignment))
                        {
                            newParentElement.setAttribute("align", oldAlignment, 0);
                        }
                    }
                }
            }
            finally
            {
                range.Start.PopCling();
                range.Start.PopGravity();
                range.End.PopCling();
                range.End.PopGravity();
            }
        }
        public static void InsertContentIntoElement(string content, ISmartContent sContent, IContentSourceSidebarContext contentSourceContext, IHTMLElement element)
        {
            MshtmlMarkupServices MarkupServices = new MshtmlMarkupServices((IMarkupServicesRaw)element.document);

            //Note: undo/redo disabled for smart content since undo causes the HTML to get out of sync
            //with the inserter's settings state, so undo changes will be blown away the next time the
            //the inserter's HTML is regenerated.  Also note that making this insertion without wrapping it
            //in an undo clears the undo/redo stack, which is what we want for beta.
            //string undoId = Guid.NewGuid().ToString();

            MarkupRange htmlRange = MarkupServices.CreateMarkupRange(element, false);

            htmlRange.Start.PushCling(true);
            htmlRange.End.PushCling(true);
            MarkupServices.Remove(htmlRange.Start, htmlRange.End);
            htmlRange.Start.PopCling();
            htmlRange.End.PopCling();

            element.style.padding = ToPaddingString(sContent.Layout);

            if (sContent.Layout.Alignment == Alignment.None ||
                sContent.Layout.Alignment == Alignment.Right ||
                sContent.Layout.Alignment == Alignment.Left)
            {
                element.style.display     = "inline";
                element.style.marginLeft  = "0px";
                element.style.marginRight = "0px";
                element.style.styleFloat  = sContent.Layout.Alignment.ToString().ToLower(CultureInfo.InvariantCulture);
            }
            else if (sContent.Layout.Alignment == Alignment.Center)
            {
                element.style.styleFloat  = Alignment.None.ToString().ToLower(CultureInfo.InvariantCulture);
                element.style.display     = "block";
                element.style.marginLeft  = "auto";
                element.style.marginRight = "auto";
            }

            // Clear out any width on the overall smart content block, if the element is centered, we will add the width back in later
            // after we calcuate it from the childern, the current width value is stale.
            element.style.width = "";

            //Note: we use MarkupServices to insert the content so that IE doesn't try to fix up URLs.
            //Element.insertAdjacentHTML() is a no-no because it rewrites relaive URLs to include
            //the fullpath from the local filesytem.

            //MarkupServices.ParseString() doesn't attempt to fix up URLs, so its safe to use.
            //We will now stage the new content into a MarkupContainer, and then move it into
            //the working document.
            MarkupPointer sc1 = MarkupServices.CreateMarkupPointer();
            MarkupPointer sc2 = MarkupServices.CreateMarkupPointer();

            //Create a temporary document from the html and set the start/end pointers to the
            //start and end of the document.
            MarkupServices.ParseString(content, sc1, sc2);
            IHTMLDocument2 doc          = sc1.GetDocument();
            MarkupRange    stagingRange = MarkupServices.CreateMarkupRange(sc1, sc2);

            stagingRange.MoveToElement(doc.body, false);

            //IE7 hack: fixes bug 305512.  Note that this will destroy the inner content of the element,
            //so make sure it is called before the refreshed content is inserted.
            BeforeInsertInvalidateHackForIE7(element);

            //move the content from the staging area into the actual insertion point.
            MarkupServices.Move(stagingRange.Start, stagingRange.End, htmlRange.End);

            if (sContent.Layout.Alignment == Alignment.Center)
            {
                MarkupContext mc    = htmlRange.End.Right(false);
                MarkupRange   range = MarkupServices.CreateMarkupRange(mc.Element, false);

                IHTMLElement[] childern = range.GetTopLevelElements(MarkupRange.FilterNone);

                int maxWidth = 0;
                foreach (IHTMLElement child in childern)
                {
                    maxWidth = Math.Max(maxWidth, child.offsetWidth);
                }

                if (maxWidth != 0)
                {
                    mc.Element.style.width = maxWidth;
                }
            }

            // Let the context provider know the smart content was edited.
            string contentSourceId, contentId;

            ContentSourceManager.ParseContainingElementId(element.id, out contentSourceId, out contentId);
            contentSourceContext.OnSmartContentEdited(contentId);
        }
        private MarkupRange ApplyInlineTag(_ELEMENT_TAG_ID tagId, string attributes, MarkupRange selection)
        {
            MarkupRange newSelection = _markupServices.CreateMarkupRange();

            // If the selection is empty, then just insert the tag
            if (selection.IsEmpty())
            {
                newSelection.MoveToElement(WrapRangeInSpanElement(tagId, attributes, selection), false);
                return(newSelection);
            }

            // Start at the beginning of the selection move forward until you hit a block start/exit context or the end of the selection

            bool          keepApplying   = true;
            MarkupContext contextStart   = new MarkupContext();
            MarkupRange   blockFreeRange = _markupServices.CreateMarkupRange(selection.Start.Clone(), selection.Start.Clone());
            MarkupPointer currentPointer = _markupServices.CreateMarkupPointer(blockFreeRange.Start);

            while (keepApplying)
            {
                // Check if moving right would be beyond the bounds of the selection.
                if (currentPointer.IsRightOfOrEqualTo(selection.End))
                {
                    // We've hit the end of the selection, so we're done.
                    keepApplying = false;
                    Debug.Assert(blockFreeRange.Start.IsLeftOfOrEqualTo(selection.End));
                    blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer);

                    if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange))
                    {
                        newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange));
                    }
                    break;
                }

                // Check if the next context is entering or exiting a block.
                currentPointer.Right(false, contextStart);
                if (contextStart.Element != null && ElementFilters.IsBlockElement(contextStart.Element))
                {
                    switch (contextStart.Context)
                    {
                    case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope:

                    case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope:
                    {
                        blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer);
                        if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange))
                        {
                            newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange));
                        }
                        if (contextStart.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope)
                        {
                            blockFreeRange.Start.MoveAdjacentToElement(contextStart.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin);
                        }
                        else if (contextStart.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)
                        {
                            blockFreeRange.Start.MoveAdjacentToElement(contextStart.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                        }

                        blockFreeRange.Collapse(true);
                    }
                    break;

                    case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None:
                    {
                        keepApplying = false;
                        blockFreeRange.End.MoveToPointer(selection.End.IsLeftOf(currentPointer) ? selection.End : currentPointer);
                        if (ShouldApplyInlineTagToBlockFreeSelection(blockFreeRange))
                        {
                            newSelection.ExpandToInclude(ApplyInlineTagToBlockFreeSelection(tagId, attributes, blockFreeRange));
                        }
                    }
                    break;

                    default:
                        break;
                    }
                }

                // Finally, move our pointer
                currentPointer.Right(true);
            }

            if (newSelection.Positioned)
            {
                newSelection.Trim();
            }

            return(newSelection);
        }
Example #15
0
        /// <summary>
        /// Utility for printing the correct XHTML for a given MarkupContext.
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="context"></param>
        /// <param name="text"></param>
        private static void printContext(HtmlWriter writer, MarkupContext context, string text, MarkupRange range)
        {
            switch (context.Context)
            {
            case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope:
                printElementStart(writer, context.Element);
                if (HtmlLinebreakStripper.IsPreserveWhitespaceTag(context.Element.tagName))
                {
                    // <pre> was losing whitespace using the normal markup pointer traversal method

                    writer.WriteString(BalanceHtml(context.Element.innerHTML));
                    printElementEnd(writer, context.Element);
                    range.End.MoveAdjacentToElement(context.Element, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterEnd);
                    break;
                }
                else
                {
                    if (text != null)
                    {
                        writer.WriteString(trimHtmlText(text));
                    }
                    break;
                }

            case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope:
                if (text != null)
                {
                    writer.WriteString(trimHtmlText(text));
                }
                printElementEnd(writer, context.Element);
                break;

            case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_None:
                break;

            case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope:
                if (context.Element is IHTMLCommentElement ||
                    context.Element is IHTMLUnknownElement)
                {
                    //bugfix: 1777 - comments should just be inserted raw.
                    string html = context.Element.outerHTML;
                    // bugfix: 534222 - embed tag markup generation issues
                    if (html != null && html.ToUpper(CultureInfo.InvariantCulture) != "</EMBED>")
                    {
                        writer.WriteString(html);
                    }
                }
                else
                {
                    printElementStart(writer, context.Element);
                    if (text == null && context.Element.innerHTML != null)
                    {
                        //Avoid MSHTML bug: in some cases (like title or script elements), MSHTML improperly
                        //reports a tag as being NoScope, even through it clearly has a start and end tag with
                        //text in between. To cover this case, we look for text in a noscope element, and add
                        //it to the XML stream if it is detected.
                        writer.WriteString(context.Element.innerHTML);
                    }
                    printElementEnd(writer, context.Element);
                }
                break;

            case _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_Text:
                if (text != null)
                {
                    writer.WriteString(trimHtmlText(text));
                }
                break;

            default:
                break;
            }
        }
Example #16
0
 public abstract void Invoke(MarkupContext context);
Example #17
0
        /// <summary>
        /// Navigates the editor's caret to the next editable region.
        /// </summary>
        /// <param name="direction"></param>
        private bool MoveCaretToNextRegion(MOVE_DIRECTION direction)
        {
            IHTMLElement       nextRegion;
            _ELEMENT_ADJACENCY nextRegionAdjacency;
            bool preserveXLocation;

            if (direction == MOVE_DIRECTION.UP || direction == MOVE_DIRECTION.LEFT)
            {
                nextRegion          = PreviousEditableRegion;
                nextRegionAdjacency = _ELEMENT_ADJACENCY.ELEM_ADJ_BeforeEnd;
                preserveXLocation   = direction == MOVE_DIRECTION.UP;
            }
            else if (direction == MOVE_DIRECTION.DOWN || direction == MOVE_DIRECTION.RIGHT)
            {
                nextRegion          = NextEditableRegion;
                nextRegionAdjacency = _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin;
                preserveXLocation   = direction == MOVE_DIRECTION.DOWN;

                if (nextRegion == null)
                {
                    return(false);
                }

                MarkupPointer selectRegion = EditorContext.MarkupServices.CreateMarkupPointer(nextRegion, nextRegionAdjacency);
                MarkupContext mc           = selectRegion.Right(false);
                if (mc.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope && mc.Element is IHTMLElement3 && SmartContentSelection.SelectIfSmartContentElement(EditorContext, mc.Element) != null)
                {
                    return(true);
                }
            }
            else
            {
                throw new ArgumentException("Unsupported move direction detected: " + direction);
            }

            IDisplayServicesRaw displayServices = (IDisplayServicesRaw)HTMLElement.document;
            IDisplayPointerRaw  displayPointer;

            displayServices.CreateDisplayPointer(out displayPointer);
            IHTMLCaretRaw caret = GetCaret();

            caret.MoveDisplayPointerToCaret(displayPointer);

            ILineInfo lineInfo;

            displayPointer.GetLineInfo(out lineInfo);

            if (nextRegion != null)
            {
                MarkupPointer mp = EditorContext.MarkupServices.CreateMarkupPointer(nextRegion, nextRegionAdjacency);

                DisplayServices.TraceMoveToMarkupPointer(displayPointer, mp);
                try
                {
                    caret.MoveCaretToPointer(displayPointer, true, _CARET_DIRECTION.CARET_DIRECTION_SAME);
                    if (preserveXLocation)
                    {
                        POINT caretLocation;
                        caret.GetLocation(out caretLocation, true);
                        caretLocation.x = lineInfo.x;
                        uint hitTestResults;
                        displayPointer.MoveToPoint(caretLocation, _COORD_SYSTEM.COORD_SYSTEM_GLOBAL, nextRegion, 0, out hitTestResults);
                        caret.MoveCaretToPointer(displayPointer, true, _CARET_DIRECTION.CARET_DIRECTION_SAME);
                    }
                    //BEP: using this line causes scrolling	(nextRegion as IHTMLElement2).focus();
                    (nextRegion as IHTMLElement3).setActive();
                    return(true);
                }
                catch (Exception e)
                {
                    Debug.Fail("Unexpected exception in MoveCaretToNextRegion: " + e.ToString());
                }

                caret.MoveCaretToPointer(displayPointer, true, _CARET_DIRECTION.CARET_DIRECTION_SAME);
            }
            return(false);
        }
        /// <summary>
        /// Returns true if shift+tab focused region changing is supported for the current edit location
        /// </summary>
        /// <returns></returns>
        private bool ShiftTabFocusChangeSupported()
        {
            //Shift-tab is only supported if the caret is positioned at the beginning of the post
            //If the selection is currently inside a non-blockquote block element with no visible content
            //to the left of it, then focus change is supported.

            if (!EditorContext.Selection.SelectedMarkupRange.IsEmpty())
            {
                return(false);
            }

            MarkupRange range = ElementRange.Clone();

            range.Start.MoveAdjacentToElement(HTMLElement, _ELEMENT_ADJACENCY.ELEM_ADJ_AfterBegin);
            range.End = EditorContext.Selection.SelectedMarkupRange.Start;

            //if there is any text between the caret and the beginning of the post,
            //then ShiftTab focus changing is not allowed.
            string text = range.Text;

            if (text != null && range.Text.Trim() != String.Empty)
            {
                return(false);
            }

            MarkupContext context = new MarkupContext();

            range.Start.Right(true, context);
            int blockElementDepth = 0;

            while (range.Start.IsLeftOfOrEqualTo(range.End))
            {
                if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_EnterScope || context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_NoScope)
                {
                    string tagName = context.Element.tagName;
                    if (tagName.Equals("BLOCKQUOTE") || ElementFilters.IsListElement(context.Element) || ElementFilters.IsListItemElement(context.Element))
                    {
                        return(false); //ShiftTab in a blockquote or list implies "un-blockquote" or "un-list"
                    }
                    else if (ElementFilters.IsBlockElement(context.Element))
                    {
                        blockElementDepth++;
                        if (blockElementDepth > 1)
                        {
                            return(false); //there are multiple block elements, so this is not the beginning
                        }
                    }
                    else if (ElementFilters.IsVisibleEmptyElement(context.Element))
                    {
                        return(false); //there is a visible empty element (like an image), so this is not the beginning
                    }
                }
                else if (context.Context == _MARKUP_CONTEXT_TYPE.CONTEXT_TYPE_ExitScope)
                {
                    if (ElementFilters.IsVisibleEmptyElement(context.Element))
                    {
                        return(false); //there is a visible empty element (like an image), so this is not the beginning
                    }
                }
                range.Start.Right(true, context);
            }

            return(true);
        }