/// <summary>
        /// Parses a Knockout begin containerless comment
        /// </summary>
        /// <param name="commentText">Comment text</param>
        /// <param name="expressionHandler">Binding expression handler</param>
        public static void ParseBeginContainerlessComment(string commentText,
			ExpressionDelegate expressionHandler)
        {
            Match koBeginContainerlessCommentMatch = _koBeginContainerlessCommentRegex.Match(commentText);

            if (koBeginContainerlessCommentMatch.Success)
            {
                var innerContext = new InnerMarkupParsingContext(commentText);
                var context = new MarkupParsingContext(innerContext);

                Group expressionGroup = koBeginContainerlessCommentMatch.Groups["expression"];
                int expressionPosition = expressionGroup.Index;
                string expression = expressionGroup.Value.TrimEnd();

                innerContext.IncreasePosition(expressionPosition);

                if (expressionHandler != null)
                {
                    expressionHandler(context, expression);
                }
            }
        }
Example #2
0
        /// <summary>
        /// Processing instruction handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="instructionName">Instruction name</param>
        /// <param name="attributes">List of attributes</param>
        private void ProcessingInstructionHandler(MarkupParsingContext context, string instructionName,
			IList<XmlAttribute> attributes)
        {
            _currentNodeType = XmlNodeType.ProcessingInstruction;

            if (_settings.MinifyWhitespace)
            {
                RemoveLastWhitespaceBufferItems();
            }

            _buffer.Add("<?");
            _buffer.Add(instructionName);
            RenderAttributes(attributes);
            _buffer.Add("?>");
        }
Example #3
0
        /// <summary>
        /// Empty tags handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="tagName">Tag name</param>
        /// <param name="attributes">List of attributes</param>
        private void EmptyTagHandler(MarkupParsingContext context, string tagName,
			IList<XmlAttribute> attributes)
        {
            XmlNodeType previousNodeType = _currentNodeType;

            _currentNodeType = XmlNodeType.EmptyTag;
            _currentText = string.Empty;

            if (_settings.MinifyWhitespace && previousNodeType == XmlNodeType.Text
                && (_startTagBeforeText || _endTagBeforeText || _emptyTagBeforeText))
            {
                RemoveLastWhitespaceBufferItems();
            }

            _buffer.Add("<");
            _buffer.Add(tagName);
            RenderAttributes(attributes);
            _buffer.Add(_settings.RenderEmptyTagsWithSpace ? " />" : "/>");
        }
Example #4
0
        /// <summary>
        /// Comments handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="commentText">Comment text</param>
        private void CommentHandler(MarkupParsingContext context, string commentText)
        {
            if (!_settings.RemoveXmlComments)
            {
                _currentNodeType = XmlNodeType.Comment;

                _buffer.Add("<!--");
                _buffer.Add(commentText);
                _buffer.Add("-->");
            }
        }
Example #5
0
        /// <summary>
        /// Parses XML content
        /// </summary>
        /// <param name="content">XML content</param>
        public void Parse(string content)
        {
            int contentLength = content.Length;
            if (contentLength == 0)
            {
                return;
            }

            lock (_parsingSynchronizer)
            {
                _innerContext = new InnerMarkupParsingContext(content);
                _context = new MarkupParsingContext(_innerContext);

                int endPosition = contentLength - 1;
                int previousPosition = -1;

                try
                {
                    while (_innerContext.Position <= endPosition)
                    {
                        bool isProcessed = false;
                        int firstCharPosition = _innerContext.Position;
                        char firstCharValue;
                        bool firstCharExist = content.TryGetChar(firstCharPosition, out firstCharValue);

                        if (firstCharExist && firstCharValue == '<')
                        {
                            int secondCharPosition = firstCharPosition + 1;
                            char secondCharValue;
                            bool secondCharExist = content.TryGetChar(secondCharPosition, out secondCharValue);

                            if (secondCharExist)
                            {
                                if (IsTagFirstChar(secondCharValue))
                                {
                                    // Start tag
                                    isProcessed = ProcessStartTag();
                                }
                                else
                                {
                                    int thirdCharPosition = secondCharPosition + 1;
                                    char thirdCharValue;
                                    bool thirdCharExist = content.TryGetChar(thirdCharPosition, out thirdCharValue);

                                    if (thirdCharExist)
                                    {
                                        switch (secondCharValue)
                                        {
                                            case '/':
                                                if (IsTagFirstChar(thirdCharValue))
                                                {
                                                    // End tag
                                                    isProcessed = ProcessEndTag();
                                                }
                                                break;

                                            case '!':
                                                switch (thirdCharValue)
                                                {
                                                    case '-':
                                                        int fourthCharPosition = thirdCharPosition + 1;
                                                        char fourthCharValue;
                                                        bool fourthCharExist = content.TryGetChar(
                                                            fourthCharPosition, out fourthCharValue);

                                                        if (fourthCharExist && fourthCharValue == '-')
                                                        {
                                                            // XML comments
                                                            isProcessed = ProcessComment();
                                                        }
                                                        break;

                                                    case '[':
                                                        // CDATA sections
                                                        isProcessed = ProcessCdataSection();
                                                        break;

                                                    case 'D':
                                                    case 'd':
                                                        // Doctype declaration
                                                        isProcessed = ProcessDoctype();
                                                        break;
                                                }
                                                break;

                                            case '?':
                                                // XML declaration and processing instructions
                                                isProcessed = ProcessProcessingInstruction();
                                                break;
                                        }
                                    }
                                }
                            }
                        }

                        if (!isProcessed)
                        {
                            // Text
                            ProcessText();
                        }

                        if (_innerContext.Position == previousPosition)
                        {
                            throw new MarkupParsingException(
                                string.Format(Strings.ErrorMessage_MarkupParsingFailed, "XML"),
                                _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                        }

                        previousPosition = _innerContext.Position;
                    }

                    // Check whether there were not closed tags
                    if (_tagStack.Count > 0)
                    {
                        StackedXmlTag stackedTag = _tagStack.Pop();

                        throw new MarkupParsingException(
                            string.Format(Strings.ErrorMessage_NotClosedTag, stackedTag.Name),
                            stackedTag.Coordinates,
                            SourceCodeNavigator.GetSourceFragment(_innerContext.SourceCode, stackedTag.Coordinates));
                    }
                }
                catch (MarkupParsingException)
                {
                    throw;
                }
                finally
                {
                    _tagStack.Clear();

                    _context = null;
                    _innerContext = null;
                }
            }
        }
        /// <summary>
        /// Parses a Angular class directive
        /// </summary>
        /// <param name="className">Class name</param>
        /// <param name="directiveNameHandler">Directive name handler</param>
        /// <param name="expressionHandler">Binding expression handler</param>
        /// <param name="semicolonHandler">Semicolon handler</param>
        public static void ParseClassDirective(string className, DirectiveNameDelegate directiveNameHandler,
			ExpressionDelegate expressionHandler, SemicolonDelegate semicolonHandler)
        {
            MatchCollection ngClassDirectiveMatches = _ngClassDirectiveRegex.Matches(className);

            if (ngClassDirectiveMatches.Count > 0)
            {
                var innerContext = new InnerMarkupParsingContext(className);
                var context = new MarkupParsingContext(innerContext);
                int currentPosition = 0;

                foreach (Match ngClassDirectiveMatch in ngClassDirectiveMatches)
                {
                    GroupCollection groups = ngClassDirectiveMatch.Groups;

                    Group directiveNameGroup = groups["directiveName"];
                    int directiveNamePosition = directiveNameGroup.Index;
                    string originalDirectiveName = directiveNameGroup.Value;
                    string normalizedDirectiveName = NormalizeDirectiveName(originalDirectiveName);

                    innerContext.IncreasePosition(directiveNamePosition - currentPosition);
                    currentPosition = directiveNamePosition;

                    if (directiveNameHandler != null)
                    {
                        directiveNameHandler(context, originalDirectiveName, normalizedDirectiveName);
                    }

                    Group expressionGroup = groups["expression"];
                    if (expressionGroup.Success)
                    {
                        int expressionPosition = expressionGroup.Index;
                        string expression = expressionGroup.Value.Trim();

                        innerContext.IncreasePosition(expressionPosition - currentPosition);
                        currentPosition = expressionPosition;

                        if (expressionHandler != null)
                        {
                            expressionHandler(context, expression);
                        }
                    }

                    Group semicolonGroup = groups["semicolon"];
                    if (semicolonGroup.Success)
                    {
                        int semicolonPosition = semicolonGroup.Index;

                        innerContext.IncreasePosition(semicolonPosition - currentPosition);
                        currentPosition = semicolonPosition;

                        if (semicolonHandler != null)
                        {
                            semicolonHandler(context);
                        }
                    }
                }
            }
        }
Example #7
0
        /// <summary>
        /// Parses XML content
        /// </summary>
        /// <param name="content">XML content</param>
        public void Parse(string content)
        {
            int contentLength = content.Length;

            if (contentLength == 0)
            {
                return;
            }

            lock (_parsingSynchronizer)
            {
                _innerContext = new InnerMarkupParsingContext(content);
                _context      = new MarkupParsingContext(_innerContext);

                int endPosition      = contentLength - 1;
                int previousPosition = -1;

                try
                {
                    while (_innerContext.Position <= endPosition)
                    {
                        bool isProcessed = false;

                        if (_innerContext.PeekCurrentChar() == '<')
                        {
                            switch (_innerContext.PeekNextChar())
                            {
                            case char c when IsTagFirstChar(c):
                                // Start tag
                                isProcessed = ProcessStartTag();

                                break;

                            case '/':
                                if (IsTagFirstChar(_innerContext.PeekNextChar()))
                                {
                                    // End tag
                                    isProcessed = ProcessEndTag();
                                }
                                break;

                            case '!':
                                switch (_innerContext.PeekNextChar())
                                {
                                case '-':
                                    if (_innerContext.PeekNextChar() == '-')
                                    {
                                        // XML comments
                                        isProcessed = ProcessComment();
                                    }
                                    break;

                                case '[':
                                    // CDATA sections
                                    isProcessed = ProcessCdataSection();
                                    break;

                                case 'D':
                                    // Doctype declaration
                                    isProcessed = ProcessDoctype();
                                    break;
                                }
                                break;

                            case '?':
                                // XML declaration and processing instructions
                                isProcessed = ProcessProcessingInstruction();
                                break;
                            }
                        }

                        if (!isProcessed)
                        {
                            // Text
                            ProcessText();
                        }

                        if (_innerContext.Position == previousPosition)
                        {
                            throw new MarkupParsingException(
                                      string.Format(Strings.ErrorMessage_MarkupParsingFailed, "XML"),
                                      _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                        }

                        previousPosition = _innerContext.Position;
                    }

                    // Check whether there were not closed tags
                    if (_tagStack.Count > 0)
                    {
                        StackedXmlTag stackedTag = _tagStack.Pop();

                        throw new MarkupParsingException(
                                  string.Format(Strings.ErrorMessage_NotClosedTag, stackedTag.Name),
                                  stackedTag.Coordinates,
                                  SourceCodeNavigator.GetSourceFragment(_innerContext.SourceCode, stackedTag.Coordinates));
                    }
                }
                catch (MarkupParsingException)
                {
                    throw;
                }
                finally
                {
                    _tagStack.Clear();

                    _context      = null;
                    _innerContext = null;
                }
            }
        }
Example #8
0
        /// <summary>
        /// Parses HTML content
        /// </summary>
        /// <param name="content">HTML content</param>
        public void Parse(string content)
        {
            int contentLength = content.Length;
            if (contentLength == 0)
            {
                return;
            }

            lock (_parsingSynchronizer)
            {
                _innerContext = new InnerMarkupParsingContext(content);
                _context = new MarkupParsingContext(_innerContext);

                int endPosition = contentLength - 1;
                int previousPosition = -1;

                try
                {
                    while (_innerContext.Position <= endPosition)
                    {
                        bool isProcessed = false;
                        HtmlTag lastStackedTag = _tagStack.LastOrDefault();

                        // Make sure we're not in a tag, that contains embedded code
                        if (lastStackedTag == null || !lastStackedTag.Flags.HasFlag(HtmlTagFlags.EmbeddedCode))
                        {
                            int firstCharPosition = _innerContext.Position;
                            char firstCharValue;
                            bool firstCharExist = content.TryGetChar(firstCharPosition, out firstCharValue);

                            if (firstCharExist && firstCharValue == '<')
                            {
                                int secondCharPosition = firstCharPosition + 1;
                                char secondCharValue;
                                bool secondCharExist = content.TryGetChar(secondCharPosition, out secondCharValue);

                                if (secondCharExist)
                                {
                                    if (secondCharValue.IsAlphaNumeric())
                                    {
                                        // Start tag
                                        isProcessed = ProcessStartTag();
                                    }
                                    else
                                    {
                                        int thirdCharPosition = secondCharPosition + 1;
                                        char thirdCharValue;
                                        bool thirdCharExist = content.TryGetChar(thirdCharPosition, out thirdCharValue);

                                        if (thirdCharExist)
                                        {
                                            switch (secondCharValue)
                                            {
                                                case '/':
                                                    if (thirdCharValue.IsAlphaNumeric())
                                                    {
                                                        isProcessed = ProcessEndTag();
                                                    }
                                                    break;

                                                case '!':
                                                    switch (thirdCharValue)
                                                    {
                                                        case '-':
                                                            int fourthCharPosition = thirdCharPosition + 1;
                                                            char fourthCharValue;
                                                            bool fourthCharExist = content.TryGetChar(fourthCharPosition, out fourthCharValue);

                                                            if (fourthCharExist && fourthCharValue == '-')
                                                            {
                                                                // Comments
                                                                int fifthCharPosition = fourthCharPosition + 1;
                                                                char fifthCharValue;
                                                                bool fifthCharExist = content.TryGetChar(fifthCharPosition, out fifthCharValue);

                                                                if (fifthCharExist)
                                                                {
                                                                    if (fifthCharValue == '[')
                                                                    {
                                                                        // Revealed validating If conditional comments
                                                                        // (e.g. <!--[if ... ]><!--> or <!--[if ... ]>-->)
                                                                        isProcessed = ProcessRevealedValidatingIfComment();

                                                                        if (!isProcessed)
                                                                        {
                                                                            // Hidden If conditional comments (e.g. <!--[if ... ]>)
                                                                            isProcessed = ProcessHiddenIfComment();
                                                                        }
                                                                    }
                                                                    else
                                                                    {
                                                                        // Revealed validating End If conditional comments
                                                                        // (e.g. <!--<![endif]-->)
                                                                        isProcessed = ProcessRevealedValidatingEndIfComment();
                                                                    }
                                                                }

                                                                if (!isProcessed)
                                                                {
                                                                    // HTML comments
                                                                    isProcessed = ProcessComment();
                                                                }
                                                            }
                                                            break;

                                                        case '[':
                                                            // Remaining conditional comments

                                                            // Hidden End If conditional comment (e.g. <![endif]-->)
                                                            isProcessed = ProcessHiddenEndIfComment();

                                                            if (!isProcessed)
                                                            {
                                                                // Revealed If conditional comment (e.g. <![if ... ]>)
                                                                isProcessed = ProcessRevealedIfComment();
                                                            }

                                                            if (!isProcessed)
                                                            {
                                                                // Revealed End If conditional comment (e.g. <![endif]>)
                                                                isProcessed = ProcessRevealedEndIfComment();
                                                            }
                                                            break;

                                                        case 'D':
                                                        case 'd':
                                                            // Doctype declaration
                                                            isProcessed = ProcessDoctype();
                                                            break;
                                                    }
                                                    break;

                                                case '?':
                                                    // XML declaration
                                                    isProcessed = ProcessXmlDeclaration();
                                                    break;
                                            }
                                        }
                                    }
                                }
                            }

                            if (!isProcessed)
                            {
                                // Text
                                ProcessText();
                            }
                        }
                        else
                        {
                            // Embedded code
                            ProcessEmbeddedCode();
                        }

                        if (_innerContext.Position == previousPosition)
                        {
                            throw new MarkupParsingException(
                                string.Format(Strings.ErrorMessage_MarkupParsingFailed, "HTML"),
                                _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                        }

                        previousPosition = _innerContext.Position;
                    }

                    // Clean up any remaining tags
                    ParseEndTag();

                    // Check whether there were not closed conditional comment
                    if (_conditionalCommentStack.Count > 0)
                    {
                        throw new MarkupParsingException(
                            Strings.ErrorMessage_NotClosedConditionalComment,
                            _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                    }
                }
                catch (MarkupParsingException)
                {
                    throw;
                }
                finally
                {
                    _tagStack.Clear();
                    _htmlTagFlagsCache.Clear();
                    _customHtmlTagFlagsCache.Clear();
                    _conditionalCommentStack.Clear();
                    _conditionalCommentOpened = false;
                    _xmlTagStack.Clear();
                    _context = null;
                    _innerContext = null;
                }
            }
        }
        /// <summary>
        /// Processes a inline script content
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="attribute">Attribute</param>
        /// <returns>Processed inline script content</returns>
        private string ProcessInlineScriptContent(MarkupParsingContext context, HtmlAttribute attribute)
        {
            string scriptContent = attribute.Value;
            bool forHrefAttribute = (attribute.Name == "href");

            string result = scriptContent;

            if (_settings.MinifyInlineJsCode && _jsMinifier.IsInlineCodeMinificationSupported)
            {
                bool isJavascriptProtocolRemoved = false;
                if (scriptContent.StartsWith(JS_PROTOCOL, StringComparison.OrdinalIgnoreCase))
                {
                    result = _jsProtocolRegex.Replace(result, string.Empty);
                    isJavascriptProtocolRemoved = true;
                }

                CodeMinificationResult minificationResult = _jsMinifier.Minify(result, true);
                if (minificationResult.Errors.Count == 0)
                {
                    result = minificationResult.MinifiedContent ?? string.Empty;
                }

                if (minificationResult.Errors.Count > 0 || minificationResult.Warnings.Count > 0)
                {
                    string sourceCode = context.SourceCode;
                    SourceCodeNodeCoordinates tagCoordinates = context.NodeCoordinates;
                    SourceCodeNodeCoordinates attributeCoordinates = attribute.ValueCoordinates;

                    foreach (MinificationErrorInfo error in minificationResult.Errors)
                    {
                        var relativeErrorCoordinates = new SourceCodeNodeCoordinates(error.LineNumber, error.ColumnNumber);
                        SourceCodeNodeCoordinates absoluteErrorCoordinates = CalculateAbsoluteInlineCodeErrorCoordinates(
                            tagCoordinates, attributeCoordinates, relativeErrorCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteErrorCoordinates);
                        string message = error.Message.Trim();

                        WriteError(LogCategoryConstants.JsMinificationError, message, _fileContext,
                            absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                    }

                    foreach (MinificationErrorInfo warning in minificationResult.Warnings)
                    {
                        var relativeErrorCoordinates = new SourceCodeNodeCoordinates(warning.LineNumber, warning.ColumnNumber);
                        SourceCodeNodeCoordinates absoluteErrorCoordinates = CalculateAbsoluteInlineCodeErrorCoordinates(
                            tagCoordinates, attributeCoordinates, relativeErrorCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteErrorCoordinates);
                        string message = warning.Message.Trim();

                        WriteWarning(LogCategoryConstants.JsMinificationWarning, message, _fileContext,
                            absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                    }
                }

                if (isJavascriptProtocolRemoved
                    && (forHrefAttribute || !_settings.RemoveJsProtocolFromAttributes))
                {
                    result = JS_PROTOCOL + result;
                }
            }
            else
            {
                result = result.Trim();

                if (!forHrefAttribute && _settings.RemoveJsProtocolFromAttributes)
                {
                    result = _jsProtocolRegex.Replace(result, string.Empty);
                }
            }

            result = Utils.RemoveEndingSemicolon(result);

            return result;
        }
        /// <summary>
        /// Processes a embedded SVG content
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="svgContent">Embedded SVG content</param>
        /// <returns>Processed embedded SVG content</returns>
        private string ProcessEmbeddedSvgContent(MarkupParsingContext context, string svgContent)
        {
            string result = string.Empty;

            XmlMinifier innerXmlMinifier = GetInnerXmlMinifierInstance();
            MarkupMinificationResult minificationResult = innerXmlMinifier.Minify(svgContent);

            if (minificationResult.Errors.Count == 0)
            {
                result = minificationResult.MinifiedContent ?? string.Empty;
            }
            else
            {
                string sourceCode = context.SourceCode;
                var documentCoordinates = context.NodeCoordinates;

                foreach (MinificationErrorInfo error in minificationResult.Errors)
                {
                    var xmlNodeCoordinates = new SourceCodeNodeCoordinates(error.LineNumber, error.ColumnNumber);
                    var absoluteNodeCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                        documentCoordinates, xmlNodeCoordinates);

                    string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                        sourceCode, absoluteNodeCoordinates);
                    string message = string.Format(Strings.ErrorMessage_MarkupMinificationFailed,
                        "SVG", error.Message);

                    WriteError(LogCategoryConstants.HtmlMinificationError, message, _fileContext,
                        absoluteNodeCoordinates.LineNumber, absoluteNodeCoordinates.ColumnNumber, sourceFragment);
                }
            }

            return result;
        }
        /// <summary>
        /// Processes a embedded style content
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="content">Embedded style content</param>
        /// <param name="contentType">Content type (MIME type) of the style</param>
        /// <returns>Processed embedded style content</returns>
        private string ProcessEmbeddedStyleContent(MarkupParsingContext context, string content, string contentType)
        {
            string processedContentType = (!string.IsNullOrWhiteSpace(contentType)) ?
                contentType.Trim().ToLowerInvariant() : CSS_CONTENT_TYPE;
            bool minifyWhitespace = (_settings.WhitespaceMinificationMode != WhitespaceMinificationMode.None);
            bool removeHtmlComments = _settings.RemoveHtmlCommentsFromScriptsAndStyles;
            bool removeCdataSections = _settings.RemoveCdataSectionsFromScriptsAndStyles;

            string startPart = string.Empty;
            string endPart = string.Empty;
            string code;
            string beforeCodeContent = string.Empty;

            if (_styleBeginCdataSectionRegex.IsMatch(content))
            {
                beforeCodeContent = _styleBeginCdataSectionRegex.Match(content).Value;

                if (!removeCdataSections)
                {
                    startPart = "/*<![CDATA[*/";
                    endPart = "/*]]>*/";
                }

                code = _styleBeginCdataSectionRegex.Replace(content, string.Empty);
                code = _styleEndCdataSectionRegex.Replace(code, string.Empty);
            }
            else if (_styleBeginMaxCompatibleCdataSectionRegex.IsMatch(content))
            {
                beforeCodeContent = _styleBeginMaxCompatibleCdataSectionRegex.Match(content).Value;

                if (!removeCdataSections)
                {
                    startPart = "<!--/*--><![CDATA[/*><!--*/";
                    endPart = "/*]]>*/-->";
                }

                code = _styleBeginMaxCompatibleCdataSectionRegex.Replace(content, string.Empty);
                code = _styleEndMaxCompatibleCdataSectionRegex.Replace(code, string.Empty);
            }
            else if (_styleBeginHtmlCommentRegex.IsMatch(content))
            {
                beforeCodeContent = _styleBeginHtmlCommentRegex.Match(content).Value;

                if (!removeHtmlComments)
                {
                    startPart = "<!--";
                    endPart = "-->";
                }

                code = _styleBeginHtmlCommentRegex.Replace(content, string.Empty);
                code = _styleEndHtmlCommentRegex.Replace(code, string.Empty);
            }
            else
            {
                code = content;
            }

            if (processedContentType == CSS_CONTENT_TYPE && _settings.MinifyEmbeddedCssCode)
            {
                CodeMinificationResult minificationResult = _cssMinifier.Minify(code, false);
                if (minificationResult.Errors.Count == 0)
                {
                    code = minificationResult.MinifiedContent ?? string.Empty;
                }

                if (minificationResult.Errors.Count > 0 || minificationResult.Warnings.Count > 0)
                {
                    string sourceCode = context.SourceCode;
                    var documentNodeCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                        context.NodeCoordinates, beforeCodeContent);

                    foreach (MinificationErrorInfo error in minificationResult.Errors)
                    {
                        var embeddedContentNodeCoordinates = new SourceCodeNodeCoordinates(error.LineNumber, error.ColumnNumber);
                        var absoluteNodeCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                            documentNodeCoordinates, embeddedContentNodeCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteNodeCoordinates);
                        string message = error.Message.Trim();

                        WriteError(LogCategoryConstants.CssMinificationError, message, _fileContext,
                            absoluteNodeCoordinates.LineNumber, absoluteNodeCoordinates.ColumnNumber, sourceFragment);
                    }

                    foreach (MinificationErrorInfo warning in minificationResult.Warnings)
                    {
                        var embeddedContentNodeCoordinates = new SourceCodeNodeCoordinates(warning.LineNumber, warning.ColumnNumber);
                        var absoluteNodeCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                            documentNodeCoordinates, embeddedContentNodeCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteNodeCoordinates);
                        string message = warning.Message.Trim();

                        WriteWarning(LogCategoryConstants.CssMinificationWarning, message, _fileContext,
                            absoluteNodeCoordinates.LineNumber, absoluteNodeCoordinates.ColumnNumber, sourceFragment);
                    }
                }
            }

            if (minifyWhitespace && code.Length > 0)
            {
                code = code.Trim();
            }

            string processedStyleContent;
            if (startPart.Length > 0 || endPart.Length > 0)
            {
                processedStyleContent = string.Concat(startPart, code, endPart);
            }
            else
            {
                processedStyleContent = code;
            }

            return processedStyleContent;
        }
        /// <summary>
        /// Processes a embedded script content
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="content">Embedded script content</param>
        /// <param name="contentType">Content type (MIME type) of the script</param>
        /// <returns>Processed embedded script content</returns>
        private string ProcessEmbeddedScriptContent(MarkupParsingContext context, string content, string contentType)
        {
            string processedScriptContent = content;
            string processedContentType = (!string.IsNullOrWhiteSpace(contentType)) ?
                contentType.Trim().ToLowerInvariant() : JS_CONTENT_TYPE;
            bool isJavaScript = _jsContentTypes.Contains(processedContentType);
            bool isVbScript = (processedContentType == VBS_CONTENT_TYPE);

            bool minifyWhitespace = (_settings.WhitespaceMinificationMode != WhitespaceMinificationMode.None);

            if (isJavaScript || isVbScript)
            {
                bool removeHtmlComments = _settings.RemoveHtmlCommentsFromScriptsAndStyles;
                bool removeCdataSections = _settings.RemoveCdataSectionsFromScriptsAndStyles;

                string startPart = string.Empty;
                string endPart = string.Empty;
                string newLine = Environment.NewLine;
                string code = content;
                string beforeCodeContent = string.Empty;

                if (isJavaScript)
                {
                    // Processing of JavaScript code
                    if (_scriptBeginCdataSectionRegex.IsMatch(content))
                    {
                        beforeCodeContent = _scriptBeginCdataSectionRegex.Match(content).Value;

                        if (!removeCdataSections)
                        {
                            startPart = "//<![CDATA[";
                            endPart = "//]]>";
                        }

                        code = _scriptBeginCdataSectionRegex.Replace(content, string.Empty);
                        code = _scriptEndCdataSectionRegex.Replace(code, string.Empty);
                    }
                    else if (_scriptBeginMaxCompatibleCdataSectionRegex.IsMatch(content))
                    {
                        beforeCodeContent = _scriptBeginMaxCompatibleCdataSectionRegex.Match(content).Value;

                        if (!removeCdataSections)
                        {
                            startPart = "<!--//--><![CDATA[//><!--";
                            endPart = "//--><!]]>";
                        }

                        code = _scriptBeginMaxCompatibleCdataSectionRegex.Replace(content, string.Empty);
                        code = _scriptEndMaxCompatibleCdataSectionRegex.Replace(code, string.Empty);
                    }
                    else if (_scriptBeginHtmlCommentRegex.IsMatch(content))
                    {
                        beforeCodeContent = _scriptBeginHtmlCommentRegex.Match(content).Value;

                        if (!removeHtmlComments)
                        {
                            startPart = "<!--";
                            endPart = "//-->";
                        }

                        code = _scriptBeginHtmlCommentRegex.Replace(content, string.Empty);
                        code = _scriptEndHtmlCommentRegex.Replace(code, string.Empty);
                    }

                    if (_settings.MinifyEmbeddedJsCode)
                    {
                        CodeMinificationResult minificationResult = _jsMinifier.Minify(code, false);
                        if (minificationResult.Errors.Count == 0)
                        {
                            code = minificationResult.MinifiedContent ?? string.Empty;
                        }

                        if (minificationResult.Errors.Count > 0 || minificationResult.Warnings.Count > 0)
                        {
                            string sourceCode = context.SourceCode;
                            var documentCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                                context.NodeCoordinates, beforeCodeContent);

                            foreach (MinificationErrorInfo error in minificationResult.Errors)
                            {
                                var relativeErrorCoordinates = new SourceCodeNodeCoordinates(error.LineNumber, error.ColumnNumber);
                                var absoluteErrorCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                                    documentCoordinates, relativeErrorCoordinates);
                                string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                                    sourceCode, absoluteErrorCoordinates);
                                string message = error.Message.Trim();

                                WriteError(LogCategoryConstants.JsMinificationError, message, _fileContext,
                                    absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                            }

                            foreach (MinificationErrorInfo warning in minificationResult.Warnings)
                            {
                                var relativeErrorCoordinates = new SourceCodeNodeCoordinates(warning.LineNumber, warning.ColumnNumber);
                                var absoluteErrorCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                                    documentCoordinates, relativeErrorCoordinates);
                                string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                                    sourceCode, absoluteErrorCoordinates);
                                string message = warning.Message.Trim();

                                WriteWarning(LogCategoryConstants.JsMinificationWarning, message, _fileContext,
                                    absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                            }
                        }
                    }
                }
                else
                {
                    // Processing of VBScript code
                    if (_scriptBeginCdataSectionRegex.IsMatch(content))
                    {
                        if (!removeCdataSections)
                        {
                            startPart = "<![CDATA[";
                            endPart = "]]>";
                        }

                        code = _scriptBeginCdataSectionRegex.Replace(content, string.Empty);
                        code = _scriptEndCdataSectionRegex.Replace(code, string.Empty);
                    }
                    else if (_scriptBeginHtmlCommentRegex.IsMatch(content))
                    {
                        if (!removeHtmlComments)
                        {
                            startPart = "<!--";
                            endPart = "-->";
                        }

                        code = _scriptBeginHtmlCommentRegex.Replace(content, string.Empty);
                        code = _scriptEndHtmlCommentRegex.Replace(code, string.Empty);
                    }
                }

                if (minifyWhitespace && code.Length > 0)
                {
                    code = code.Trim();
                }

                if (startPart.Length > 0 || endPart.Length > 0)
                {
                    processedScriptContent = string.Concat(startPart, newLine, code, newLine, endPart);
                }
                else
                {
                    processedScriptContent = code;
                }
            }
            else if (_processableScriptTypes.Contains(processedContentType))
            {
                // Processing of JavaScript template
                GenericHtmlMinifier innerHtmlMinifier = GetInnerHtmlMinifierInstance();
                MarkupMinificationResult minificationResult = innerHtmlMinifier.Minify(processedScriptContent, false);

                if (minificationResult.Errors.Count == 0)
                {
                    processedScriptContent = minificationResult.MinifiedContent ?? string.Empty;
                }

                if (minificationResult.Errors.Count > 0 || minificationResult.Warnings.Count > 0)
                {
                    string sourceCode = context.SourceCode;
                    var documentCoordinates = context.NodeCoordinates;

                    foreach (MinificationErrorInfo error in minificationResult.Errors)
                    {
                        var relativeErrorCoordinates = new SourceCodeNodeCoordinates(error.LineNumber, error.ColumnNumber);
                        var absoluteErrorCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                            documentCoordinates, relativeErrorCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteErrorCoordinates);
                        string message = error.Message.Trim();

                        WriteError(LogCategoryConstants.JsTemplateMinificationError, message, _fileContext,
                            absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                    }

                    foreach (MinificationErrorInfo warning in minificationResult.Warnings)
                    {
                        var relativeErrorCoordinates = new SourceCodeNodeCoordinates(warning.LineNumber, warning.ColumnNumber);
                        var absoluteErrorCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                            documentCoordinates, relativeErrorCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteErrorCoordinates);
                        string message = warning.Message.Trim();

                        WriteWarning(LogCategoryConstants.JsTemplateMinificationWarning, message, _fileContext,
                            absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                    }
                }

                if (minifyWhitespace && processedScriptContent.Length > 0)
                {
                    processedScriptContent = processedScriptContent.Trim();
                }
            }

            return processedScriptContent;
        }
        /// <summary>
        /// Minify a Knockout binding expression
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="attributeCoordinates">Coordinates of attribute value</param>
        /// <param name="expressionCoordinates">Coordinates of expression</param>
        /// <param name="expression">Binding expression</param>
        /// <returns>Minified binding expression</returns>
        private string MinifyKnockoutBindingExpression(MarkupParsingContext context,
			SourceCodeNodeCoordinates attributeCoordinates, SourceCodeNodeCoordinates expressionCoordinates,
			string expression)
        {
            if (string.IsNullOrWhiteSpace(expression))
            {
                return string.Empty;
            }

            string result = expression;
            CrockfordJsMinifier innerCrockfordJsMinifier = GetInnerCrockfordJsMinifierInstance();
            CodeMinificationResult minificationResult = innerCrockfordJsMinifier.Minify(
                JsonHelpers.WrapStringInCurlyBraces(result), true);
            IList<MinificationErrorInfo> errors = minificationResult.Errors;

            if (errors.Count == 0)
            {
                result = minificationResult.MinifiedContent ?? string.Empty;
            }
            else
            {
                SourceCodeNodeCoordinates absoluteErrorCoordinates = CalculateAbsoluteInlineCodeErrorCoordinates(
                    context.NodeCoordinates, attributeCoordinates, expressionCoordinates);
                string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                    context.SourceCode, absoluteErrorCoordinates);
                string errorMessage = errors[0].Message;

                WriteError(LogCategoryConstants.JsTemplateMinificationError,
                    string.Format(Strings.ErrorMessage_BindingExpressionMinificationFailed,
                        "Knockout", errorMessage.TrimEnd('.')),
                    _fileContext,
                    absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber,
                    sourceFragment);
            }

            result = JsonHelpers.UnwrapStringInCurlyBraces(result);

            return result;
        }
        /// <summary>
        /// Minify a Knockout binding expression
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="expressionCoordinates">Coordinates of expression</param>
        /// <param name="expression">Binding expression</param>
        /// <returns>Minified binding expression</returns>
        private string MinifyKnockoutBindingExpression(MarkupParsingContext context,
			SourceCodeNodeCoordinates expressionCoordinates, string expression)
        {
            return MinifyKnockoutBindingExpression(context, SourceCodeNodeCoordinates.Empty, expressionCoordinates,
                expression);
        }
Example #15
0
        /// <summary>
        /// Text handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="text">Text</param>
        private void TextHandler(MarkupParsingContext context, string text)
        {
            XmlNodeType previousNodeType = _currentNodeType;

            _currentNodeType = XmlNodeType.Text;

            if (previousNodeType == XmlNodeType.Text)
            {
                _currentText = text;
                _buffer.Add(text);

                return;
            }

            _xmlDeclarationBeforeText = false;
            _processingInstructionBeforeText = false;
            _doctypeBeforeText = false;
            _startTagBeforeText = false;
            _endTagBeforeText = false;
            _emptyTagBeforeText = false;

            switch (previousNodeType)
            {
                case XmlNodeType.StartTag:
                    _startTagBeforeText = true;
                    break;
                case XmlNodeType.EndTag:
                    _endTagBeforeText = true;
                    break;
                case XmlNodeType.EmptyTag:
                    _emptyTagBeforeText = true;
                    break;
                case XmlNodeType.XmlDeclaration:
                    _xmlDeclarationBeforeText = true;
                    break;
                case XmlNodeType.ProcessingInstruction:
                    _processingInstructionBeforeText = true;
                    break;
                case XmlNodeType.Doctype:
                    _doctypeBeforeText = true;
                    break;
            }

            if (_settings.MinifyWhitespace)
            {
                if (context.Position == 0)
                {
                    // Processing starting whitespace
                    text = text.TrimStart();
                }
                else if ((context.Position + text.Length) == context.Length)
                {
                    // Processing ending whitespace
                    text = text.TrimEnd();
                }
                else if (_xmlDeclarationBeforeText || _processingInstructionBeforeText || _doctypeBeforeText)
                {
                    // Processing whitespace, that followed after
                    // the XML declaration, processing instruction
                    // or document type declaration
                    if (string.IsNullOrWhiteSpace(text))
                    {
                        text = string.Empty;
                    }
                }
            }

            _currentText = text;

            if (text.Length > 0)
            {
                _buffer.Add(text);
            }
        }
 /// <summary>
 /// Minify a Knockout binding expression
 /// </summary>
 /// <param name="context">Markup parsing context</param>
 /// <param name="attribute">Attribute</param>
 /// <returns>Minified binding expression</returns>
 private string MinifyKnockoutBindingExpression(MarkupParsingContext context, HtmlAttribute attribute)
 {
     return MinifyKnockoutBindingExpression(context, attribute.ValueCoordinates, SourceCodeNodeCoordinates.Empty,
         attribute.Value);
 }
        /// <summary>
        /// Processes a inline style content
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="attribute">Attribute</param>
        /// <returns>Processed inline style content</returns>
        private string ProcessInlineStyleContent(MarkupParsingContext context, HtmlAttribute attribute)
        {
            string styleContent = attribute.Value;

            string result = styleContent;
            if (_settings.MinifyInlineCssCode && _cssMinifier.IsInlineCodeMinificationSupported)
            {
                CodeMinificationResult minificationResult = _cssMinifier.Minify(result, true);
                if (minificationResult.Errors.Count == 0)
                {
                    result = minificationResult.MinifiedContent ?? string.Empty;
                }

                if (minificationResult.Errors.Count > 0 || minificationResult.Warnings.Count > 0)
                {
                    string sourceCode = context.SourceCode;
                    SourceCodeNodeCoordinates tagCoordinates = context.NodeCoordinates;
                    SourceCodeNodeCoordinates attributeCoordinates = attribute.ValueCoordinates;

                    foreach (MinificationErrorInfo error in minificationResult.Errors)
                    {
                        var relativeErrorCoordinates = new SourceCodeNodeCoordinates(error.LineNumber, error.ColumnNumber);
                        SourceCodeNodeCoordinates absoluteErrorCoordinates = CalculateAbsoluteInlineCodeErrorCoordinates(
                            tagCoordinates, attributeCoordinates, relativeErrorCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteErrorCoordinates);
                        string message = error.Message.Trim();

                        WriteError(LogCategoryConstants.CssMinificationError, message, _fileContext,
                            absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                    }

                    foreach (MinificationErrorInfo warning in minificationResult.Warnings)
                    {
                        var relativeErrorCoordinates = new SourceCodeNodeCoordinates(warning.LineNumber, warning.ColumnNumber);
                        SourceCodeNodeCoordinates absoluteErrorCoordinates = CalculateAbsoluteInlineCodeErrorCoordinates(
                            tagCoordinates, attributeCoordinates, relativeErrorCoordinates);
                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteErrorCoordinates);
                        string message = warning.Message.Trim();

                        WriteWarning(LogCategoryConstants.CssMinificationWarning, message, _fileContext,
                            absoluteErrorCoordinates.LineNumber, absoluteErrorCoordinates.ColumnNumber, sourceFragment);
                    }
                }
            }
            else
            {
                result = result.Trim();
            }

            result = Utils.RemoveEndingSemicolon(result);

            return result;
        }
Example #18
0
        /// <summary>
        /// Parses HTML content
        /// </summary>
        /// <param name="content">HTML content</param>
        public void Parse(string content)
        {
            int contentLength = content.Length;

            if (contentLength == 0)
            {
                return;
            }

            lock (_parsingSynchronizer)
            {
                _innerContext = new InnerMarkupParsingContext(content);
                _context      = new MarkupParsingContext(_innerContext);

                int endPosition      = contentLength - 1;
                int previousPosition = -1;

                try
                {
                    while (_innerContext.Position <= endPosition)
                    {
                        bool    isProcessed    = false;
                        HtmlTag lastStackedTag = _tagStack.LastOrDefault();

                        // Make sure we're not in a tag, that contains embedded code
                        if (lastStackedTag == null || !lastStackedTag.Flags.IsSet(HtmlTagFlags.EmbeddedCode))
                        {
                            if (_innerContext.PeekCurrentChar() == '<')
                            {
                                switch (_innerContext.PeekNextChar())
                                {
                                case char c when c.IsAlphaNumeric():
                                    // Start tag
                                    isProcessed = ProcessStartTag();

                                    break;

                                case '/':
                                    if (_innerContext.PeekNextChar().IsAlphaNumeric())
                                    {
                                        // End tag
                                        isProcessed = ProcessEndTag();
                                    }
                                    break;

                                case '!':
                                    switch (_innerContext.PeekNextChar())
                                    {
                                    case '-':
                                        if (_innerContext.PeekNextChar() == '-')
                                        {
                                            // Comments
                                            if (_innerContext.PeekNextChar() == '[')
                                            {
                                                // Revealed validating If conditional comments
                                                // (e.g. <!--[if ... ]><!--> or <!--[if ... ]>-->)
                                                isProcessed = ProcessRevealedValidatingIfComment();

                                                if (!isProcessed)
                                                {
                                                    // Hidden If conditional comments (e.g. <!--[if ... ]>)
                                                    isProcessed = ProcessHiddenIfComment();
                                                }
                                            }
                                            else
                                            {
                                                // Revealed validating End If conditional comments
                                                // (e.g. <!--<![endif]-->)
                                                isProcessed = ProcessRevealedValidatingEndIfComment();
                                            }

                                            if (!isProcessed)
                                            {
                                                // HTML comments
                                                isProcessed = ProcessComment();
                                            }
                                        }
                                        break;

                                    case '[':
                                        switch (_innerContext.PeekNextChar())
                                        {
                                        case 'i':
                                        case 'I':
                                            // Revealed If conditional comment (e.g. <![if ... ]>)
                                            isProcessed = ProcessRevealedIfComment();
                                            break;

                                        case 'e':
                                        case 'E':
                                            // Hidden End If conditional comment (e.g. <![endif]-->)
                                            isProcessed = ProcessHiddenEndIfComment();

                                            if (!isProcessed)
                                            {
                                                // Revealed End If conditional comment (e.g. <![endif]>)
                                                isProcessed = ProcessRevealedEndIfComment();
                                            }
                                            break;

                                        case 'C':
                                            // CDATA sections
                                            isProcessed = ProcessCdataSection();
                                            break;
                                        }
                                        break;

                                    case 'D':
                                    case 'd':
                                        // Doctype declaration
                                        isProcessed = ProcessDoctype();
                                        break;
                                    }
                                    break;

                                case '?':
                                    // XML declaration
                                    isProcessed = ProcessXmlDeclaration();
                                    break;
                                }
                            }

                            if (!isProcessed)
                            {
                                // Text
                                ProcessText();
                            }
                        }
                        else
                        {
                            // Embedded code
                            ProcessEmbeddedCode();
                        }

                        if (_innerContext.Position == previousPosition)
                        {
                            throw new MarkupParsingException(
                                      string.Format(Strings.ErrorMessage_MarkupParsingFailed, "HTML"),
                                      _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                        }

                        previousPosition = _innerContext.Position;
                    }

                    // Clean up any remaining tags
                    ParseEndTag();

                    // Check whether there were not closed conditional comment
                    if (_conditionalCommentStack.Count > 0)
                    {
                        throw new MarkupParsingException(
                                  Strings.ErrorMessage_NotClosedConditionalComment,
                                  _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                    }
                }
                catch (MarkupParsingException)
                {
                    throw;
                }
                finally
                {
                    _tagStack.Clear();
                    _tempAttributes.Clear();
                    _conditionalCommentStack.Clear();
                    _conditionalCommentOpened = false;
                    _xmlTagStack.Clear();
                    _context      = null;
                    _innerContext = null;
                }
            }
        }
        /// <summary>
        /// Start tags handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="tag">HTML tag</param>
        private void StartTagHandler(MarkupParsingContext context, HtmlTag tag)
        {
            HtmlNodeType previousNodeType = _currentNodeType;
            string previousTagName = string.Empty;
            if (_currentTag != null)
            {
                previousTagName = _currentTag.Name;
            }

            if (_settings.UseMetaCharsetTag && IsMetaContentTypeTag(tag.Name, tag.Attributes))
            {
                tag = UpgradeToMetaCharsetTag(tag);
            }

            _currentNodeType = HtmlNodeType.StartTag;
            _currentTag = tag;
            _currentText = string.Empty;

            string tagName = tag.Name;
            IList<HtmlAttribute> attributes = tag.Attributes;
            HtmlTagFlags tagFlags = tag.Flags;

            // Set whitespace flags for nested tags (for example <span> within a <pre>)
            WhitespaceMinificationMode whitespaceMinificationMode = _settings.WhitespaceMinificationMode;
            if (whitespaceMinificationMode != WhitespaceMinificationMode.None)
            {
                if (_tagsWithNotRemovableWhitespaceQueue.Count == 0)
                {
                    // Processing of whitespace, that followed before the start tag
                    bool allowTrimEnd = false;
                    if (tagFlags.Invisible || tagFlags.NonIndependent)
                    {
                        allowTrimEnd = true;
                    }
                    else
                    {
                        if (whitespaceMinificationMode == WhitespaceMinificationMode.Medium
                            || whitespaceMinificationMode == WhitespaceMinificationMode.Aggressive)
                        {
                            allowTrimEnd = tagFlags.Block;
                        }
                    }

                    if (allowTrimEnd)
                    {
                        TrimEndLastBufferItem();
                    }
                }

                if (!CanMinifyWhitespace(tagName))
                {
                    _tagsWithNotRemovableWhitespaceQueue.Enqueue(tagName);
                }
            }

            if (_settings.RemoveOptionalEndTags
                && previousNodeType != HtmlNodeType.StartTag
                && !IsSafeOptionalEndTag(previousTagName))
            {
                if (CanRemoveOptionalEndTagByNextTagName(previousTagName, tagName))
                {
                    RemoveLastEndTagFromBuffer(previousTagName);
                }

                FlushBuffer();
            }

            _buffer.Add("<");
            _buffer.Add(tagName);

            int attributeCount = attributes.Count;

            for (int attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++)
            {
                _buffer.Add(BuildAttributeString(context, tag, attributes[attributeIndex]));
            }

            if (tagFlags.Empty)
            {
                if (_settings.EmptyTagRenderMode == HtmlEmptyTagRenderMode.Slash)
                {
                    _buffer.Add("/");
                }
                else if (_settings.EmptyTagRenderMode == HtmlEmptyTagRenderMode.SpaceAndSlash)
                {
                    _buffer.Add(" /");
                }
            }
            _buffer.Add(">");
        }
Example #20
0
        /// <summary>
        /// Parses XML content
        /// </summary>
        /// <param name="content">XML content</param>
        public void Parse(string content)
        {
            int contentLength = content.Length;

            if (contentLength == 0)
            {
                return;
            }

            lock (_parsingSynchronizer)
            {
                _innerContext = new InnerMarkupParsingContext(content);
                _context      = new MarkupParsingContext(_innerContext);

                _tagStack = new Stack <StackedXmlTag>();

                int endPosition      = contentLength - 1;
                int previousPosition = -1;

                try
                {
                    while (_innerContext.Position <= endPosition)
                    {
                        bool isProcessed = false;

                        if (content.CustomStartsWith("<", _innerContext.Position, StringComparison.Ordinal))
                        {
                            if (content.CustomStartsWith("</", _innerContext.Position, StringComparison.Ordinal))
                            {
                                // End tag
                                isProcessed = ProcessEndTag();
                            }
                            else if (content.CustomStartsWith("<!", _innerContext.Position, StringComparison.Ordinal))
                            {
                                // XML comments
                                isProcessed = ProcessComment();

                                if (!isProcessed)
                                {
                                    // CDATA sections
                                    isProcessed = ProcessCdataSection();
                                }

                                if (!isProcessed)
                                {
                                    // Doctype declaration
                                    isProcessed = ProcessDoctype();
                                }
                            }
                            else if (content.CustomStartsWith("<?", _innerContext.Position, StringComparison.Ordinal))
                            {
                                // XML declaration and processing instructions
                                isProcessed = ProcessProcessingInstruction();
                            }
                            else
                            {
                                // Start tag
                                isProcessed = ProcessStartTag();
                            }
                        }

                        if (!isProcessed)
                        {
                            // Text
                            ProcessText();
                        }

                        if (_innerContext.Position == previousPosition)
                        {
                            throw new XmlParsingException(
                                      string.Format(Strings.ErrorMessage_MarkupParsingFailed, "XML"),
                                      _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                        }

                        previousPosition = _innerContext.Position;
                    }

                    // Check whether there were not closed tags
                    if (_tagStack.Count > 0)
                    {
                        StackedXmlTag stackedTag = _tagStack.Pop();

                        throw new XmlParsingException(
                                  string.Format(Strings.ErrorMessage_NotClosedTag, stackedTag.Name),
                                  stackedTag.Coordinates,
                                  SourceCodeNavigator.GetSourceFragment(_innerContext.SourceCode, stackedTag.Coordinates));
                    }
                }
                catch (XmlParsingException)
                {
                    throw;
                }
                finally
                {
                    _tagStack.Clear();

                    _context      = null;
                    _innerContext = null;
                }
            }
        }
        /// <summary>
        /// Template tags handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="expression">Expression</param>
        /// <param name="startDelimiter">Start delimiter</param>
        /// <param name="endDelimiter">End delimiter</param>
        private void TemplateTagHandler(MarkupParsingContext context, string expression, string startDelimiter,
			string endDelimiter)
        {
            _currentNodeType = HtmlNodeType.TemplateTag;

            string processedExpression = expression;
            if (_settings.MinifyAngularBindingExpressions && startDelimiter == "{{" && endDelimiter == "}}")
            {
                processedExpression = MinifyAngularBindingExpression(context, expression);
            }

            _buffer.Add(startDelimiter);
            _buffer.Add(processedExpression);
            _buffer.Add(endDelimiter);
        }
        /// <summary>
        /// Parses a Angular comment directive
        /// </summary>
        /// <param name="commentText">Comment text</param>
        /// <param name="directiveNameHandler">Directive name handler</param>
        /// <param name="expressionHandler">Binding expression handler</param>
        public static void ParseCommentDirective(string commentText, DirectiveNameDelegate directiveNameHandler,
			ExpressionDelegate expressionHandler)
        {
            Match ngCommentDirectiveMatch = _ngCommentDirectiveRegex.Match(commentText);

            if (ngCommentDirectiveMatch.Success)
            {
                var innerContext = new InnerMarkupParsingContext(commentText);
                var context = new MarkupParsingContext(innerContext);

                GroupCollection groups = ngCommentDirectiveMatch.Groups;

                Group directiveNameGroup = groups["directiveName"];
                int directiveNamePosition = directiveNameGroup.Index;
                string originalDirectiveName = directiveNameGroup.Value;
                string normalizedDirectiveName = NormalizeDirectiveName(originalDirectiveName);

                innerContext.IncreasePosition(directiveNamePosition);

                if (directiveNameHandler != null)
                {
                    directiveNameHandler(context, originalDirectiveName, normalizedDirectiveName);
                }

                Group expressionGroup = groups["expression"];
                if (expressionGroup.Success)
                {
                    int expressionPosition = expressionGroup.Index;
                    string expression = expressionGroup.Value.Trim();

                    innerContext.IncreasePosition(expressionPosition - directiveNamePosition);

                    if (expressionHandler != null)
                    {
                        expressionHandler(context, expression);
                    }
                }
            }
        }
        /// <summary>
        /// Text handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="text">Text</param>
        private void TextHandler(MarkupParsingContext context, string text)
        {
            HtmlNodeType nodeType = _currentNodeType;
            string tagName;
            HtmlTagFlags tagFlags;
            IList<HtmlAttribute> attributes;
            if (_currentTag != null)
            {
                tagName = _currentTag.Name;
                tagFlags = _currentTag.Flags;
                attributes = _currentTag.Attributes;
            }
            else
            {
                tagName = string.Empty;
                tagFlags = new HtmlTagFlags();
                attributes = new List<HtmlAttribute>();
            }

            WhitespaceMinificationMode whitespaceMinificationMode = _settings.WhitespaceMinificationMode;

            if (nodeType == HtmlNodeType.StartTag && tagFlags.EmbeddedCode)
            {
                switch (tagName)
                {
                    case "script":
                    case "style":
                        string contentType = attributes
                            .Where(a => a.Name == "type")
                            .Select(a => a.Value)
                            .FirstOrDefault()
                            ;

                        if (tagName == "script")
                        {
                            if (string.IsNullOrWhiteSpace(contentType))
                            {
                                string language = attributes
                                    .Where(a => a.Name == "language")
                                    .Select(a => a.Value)
                                    .FirstOrDefault()
                                    ;

                                if (!string.IsNullOrWhiteSpace(language)
                                    && language.Trim().ToLowerInvariant() == "vbscript")
                                {
                                    contentType = VBS_CONTENT_TYPE;
                                }
                            }

                            text = ProcessEmbeddedScriptContent(context, text, contentType);
                        }
                        else if (tagName == "style")
                        {
                            text = ProcessEmbeddedStyleContent(context, text, contentType);
                        }

                        break;
                    case "svg":
                        text = ProcessEmbeddedSvgContent(context, text);
                        break;
                    case "math":
                        text = ProcessEmbeddedMathMlContent(context, text);
                        break;
                }
            }
            else
            {
                if (whitespaceMinificationMode != WhitespaceMinificationMode.None)
                {
                    if (_tagsWithNotRemovableWhitespaceQueue.Count == 0)
                    {
                        if (context.Position == 0)
                        {
                            // Processing of starting whitespace
                            text = text.TrimStart();
                        }
                        else if ((context.Position + text.Length) == context.Length)
                        {
                            // Processing of ending whitespace
                            text = text.TrimEnd();
                        }
                        else if (nodeType == HtmlNodeType.StartTag)
                        {
                            // Processing of whitespace, that followed after the start tag
                            bool allowTrimStart = false;
                            if (tagFlags.Invisible || (tagFlags.NonIndependent && tagFlags.Empty))
                            {
                                allowTrimStart = true;
                            }
                            else
                            {
                                if (whitespaceMinificationMode == WhitespaceMinificationMode.Medium)
                                {
                                    allowTrimStart = tagFlags.Block;
                                }
                                else if (whitespaceMinificationMode == WhitespaceMinificationMode.Aggressive)
                                {
                                    allowTrimStart = (tagFlags.Block
                                        || ((tagFlags.Inline || tagFlags.InlineBlock) && !tagFlags.Empty));
                                }
                            }

                            if (allowTrimStart)
                            {
                                text = text.TrimStart();
                            }
                        }
                        else if (nodeType == HtmlNodeType.EndTag)
                        {
                            // Processing of whitespace, that followed after the end tag
                            bool allowTrimStart = false;
                            if (tagFlags.Invisible || tagFlags.NonIndependent)
                            {
                                allowTrimStart = true;
                            }
                            else
                            {
                                if (whitespaceMinificationMode == WhitespaceMinificationMode.Medium
                                    || whitespaceMinificationMode == WhitespaceMinificationMode.Aggressive)
                                {
                                    allowTrimStart = tagFlags.Block;
                                }
                            }

                            if (allowTrimStart)
                            {
                                text = text.TrimStart();
                            }
                        }
                        else if (nodeType == HtmlNodeType.Doctype || nodeType == HtmlNodeType.XmlDeclaration)
                        {
                            // Processing of whitespace, that followed after the document type declaration
                            // or XML declaration
                            text = text.TrimStart();
                        }

                        if (text.Length > 0)
                        {
                            text = Utils.CollapseWhitespace(text);
                        }
                    }
                    else if (nodeType == HtmlNodeType.StartTag && tagName == "textarea"
                        && string.IsNullOrWhiteSpace(text))
                    {
                        text = string.Empty;
                    }
                }
            }

            _currentNodeType = HtmlNodeType.Text;
            _currentText = text;

            if (text.Length > 0)
            {
                _buffer.Add(text);
            }
        }
Example #24
0
        /// <summary>
        /// CDATA sections handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="cdataText">CDATA text</param>
        private void CdataSectionHandler(MarkupParsingContext context, string cdataText)
        {
            _currentNodeType = XmlNodeType.CdataSection;

            _buffer.Add("<![CDATA[");
            _buffer.Add(cdataText);
            _buffer.Add("]]>");
        }
        /// <summary>
        /// XML declaration handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="xmlDeclaration">XML declaration</param>
        private void XmlDeclarationHandler(MarkupParsingContext context, string xmlDeclaration)
        {
            _currentNodeType = HtmlNodeType.XmlDeclaration;

            WhitespaceMinificationMode whitespaceMinificationMode = _settings.WhitespaceMinificationMode;
            if (whitespaceMinificationMode != WhitespaceMinificationMode.None)
            {
                // Processing of whitespace, that followed before the document type declaration
                TrimEndLastBufferItem();
            }

            if (_settings.UseXhtmlSyntax)
            {
                XmlMinifier innerXmlMinifier = GetInnerXmlMinifierInstance();
                MarkupMinificationResult minificationResult = innerXmlMinifier.Minify(xmlDeclaration);

                if (minificationResult.Errors.Count == 0)
                {
                    _buffer.Add(minificationResult.MinifiedContent);
                }
                else
                {
                    string sourceCode = context.SourceCode;
                    var documentCoordinates = context.NodeCoordinates;

                    foreach (MinificationErrorInfo error in minificationResult.Errors)
                    {
                        var xmlNodeCoordinates = new SourceCodeNodeCoordinates(error.LineNumber, error.ColumnNumber);
                        var absoluteNodeCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(
                            documentCoordinates, xmlNodeCoordinates);

                        string sourceFragment = SourceCodeNavigator.GetSourceFragment(
                            sourceCode, absoluteNodeCoordinates);
                        string message = Strings.ErrorMessage_XmlDeclarationMinificationFailed;

                        WriteError(LogCategoryConstants.HtmlMinificationError, message, _fileContext,
                            absoluteNodeCoordinates.LineNumber, absoluteNodeCoordinates.ColumnNumber, sourceFragment);
                    }
                }
            }
            else
            {
                string sourceCode = context.SourceCode;
                SourceCodeNodeCoordinates xmlDeclarationCoordinates = context.NodeCoordinates;

                WriteWarning(LogCategoryConstants.HtmlMinificationWarning,
                    Strings.WarningMessage_XmlDeclarationNotAllowed, _fileContext,
                    xmlDeclarationCoordinates.LineNumber, xmlDeclarationCoordinates.ColumnNumber,
                    SourceCodeNavigator.GetSourceFragment(sourceCode, xmlDeclarationCoordinates));
            }
        }
Example #26
0
        /// <summary>
        /// Document type declaration handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="doctype">Document type declaration</param>
        private void DoctypeDelegateHandler(MarkupParsingContext context, string doctype)
        {
            _currentNodeType = XmlNodeType.Doctype;

            if (_settings.MinifyWhitespace)
            {
                RemoveLastWhitespaceBufferItems();
            }

            _buffer.Add(Utils.CollapseWhitespace(doctype));
        }
        /// <summary>
        /// Builds a string representation of the attribute
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="attribute">HTML attribute</param>
        /// <param name="tag">HTML tag</param>
        /// <returns>String representation of the attribute</returns>
        private string BuildAttributeString(MarkupParsingContext context, HtmlTag tag, HtmlAttribute attribute)
        {
            string tagName = tag.Name;
            HtmlTagFlags tagFlags = tag.Flags;
            IList<HtmlAttribute> attributes = tag.Attributes;

            string attributeString;
            string attributeName = attribute.Name;
            string attributeValue = attribute.Value;
            bool attributeHasValue = attribute.HasValue;
            HtmlAttributeType attributeType = attribute.Type;
            bool useHtmlSyntax = !_settings.UseXhtmlSyntax;

            if (useHtmlSyntax && IsXmlAttribute(attributeName))
            {
                string sourceCode = context.SourceCode;
                SourceCodeNodeCoordinates attributeCoordinates = attribute.NameCoordinates;

                WriteWarning(LogCategoryConstants.HtmlMinificationWarning,
                    string.Format(Strings.WarningMessage_XmlBasedAttributeNotAllowed, attributeName), _fileContext,
                    attributeCoordinates.LineNumber, attributeCoordinates.ColumnNumber,
                    SourceCodeNavigator.GetSourceFragment(sourceCode, attributeCoordinates));
            }

            if ((_settings.RemoveRedundantAttributes && IsAttributeRedundant(tagName, attributeName, attributeValue, attributes))
                || (_settings.RemoveJsTypeAttributes && IsJavaScriptTypeAttribute(tagName, attributeName, attributeValue))
                || (_settings.RemoveCssTypeAttributes && IsCssTypeAttribute(tagName, attributeName, attributeValue, attributes))
                || (useHtmlSyntax && CanRemoveXmlAttribute(tagName, attributeName)))
            {
                attributeString = string.Empty;
                return attributeString;
            }

            bool isCustomBooleanAttribute = (!attributeHasValue && attributeType == HtmlAttributeType.Text);
            if (isCustomBooleanAttribute && useHtmlSyntax)
            {
                attributeString = " " + attributeName;
                return attributeString;
            }

            if (attributeType == HtmlAttributeType.Boolean)
            {
                if (_settings.CollapseBooleanAttributes)
                {
                    attributeString = " " + attributeName;
                    return attributeString;
                }

                attributeValue = attributeName;
            }
            else if (isCustomBooleanAttribute)
            {
                attributeValue = string.Empty;
            }
            else
            {
                attributeValue = CleanAttributeValue(context, tag, attribute);

                if (_settings.RemoveEmptyAttributes
                    && CanRemoveEmptyAttribute(tagName, attributeName, attributeValue, attributeType))
                {
                    attributeString = string.Empty;
                    return attributeString;
                }
            }

            bool addQuotes = !CanRemoveAttributeQuotes(tagFlags, attributeValue, _settings.AttributeQuotesRemovalMode);
            attributeString = InnerBuildAttributeString(attributeName, attributeValue, addQuotes);

            return attributeString;
        }
Example #28
0
        /// <summary>
        /// End tags handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="tagName">Tag name</param>
        private void EndTagHandler(MarkupParsingContext context, string tagName)
        {
            XmlNodeType previousNodeType = _currentNodeType;
            string previousText = _currentText;

            _currentNodeType = XmlNodeType.EndTag;
            _currentText = string.Empty;

            if (_settings.CollapseTagsWithoutContent && previousNodeType == XmlNodeType.StartTag
                && previousText.Length == 0)
            {
                if (TransformLastStartTagToEmptyTag())
                {
                    FlushBuffer();
                    return;
                }
            }

            if (_settings.MinifyWhitespace && previousNodeType == XmlNodeType.Text
                && (_endTagBeforeText || _emptyTagBeforeText))
            {
                RemoveLastWhitespaceBufferItems();
            }

            // Add end tag to buffer
            _buffer.Add("</");
            _buffer.Add(tagName);
            _buffer.Add(">");

            FlushBuffer();
        }
        /// <summary>
        /// Cleans a attribute value
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="tag">HTML tag</param>
        /// <param name="attribute">HTML attribute</param>
        /// <returns>Processed attribute value</returns>
        private string CleanAttributeValue(MarkupParsingContext context, HtmlTag tag, HtmlAttribute attribute)
        {
            string attributeValue = attribute.Value;
            if (attributeValue.Length == 0)
            {
                return attributeValue;
            }

            string result = attributeValue;
            string tagName = tag.Name;
            IList<HtmlAttribute> attributes = tag.Attributes;
            string attributeName = attribute.Name;
            HtmlAttributeType attributeType = attribute.Type;

            if (attributeType != HtmlAttributeType.Event && MustacheStyleTagHelpers.ContainsMustacheStyleTag(result))
            {
                // Processing of Angular Mustache-style tags
                var attributeValueBuilder = new StringBuilder();

                MustacheStyleTagHelpers.ParseMarkup(result,
                    (localContext, expression, startDelimiter, endDelimiter) =>
                    {
                        string processedExpression = expression;
                        if (_settings.MinifyAngularBindingExpressions && startDelimiter == "{{" && endDelimiter == "}}")
                        {
                            processedExpression = MinifyAngularBindingExpression(context, attribute.ValueCoordinates,
                                localContext.NodeCoordinates, expression);
                        }

                        attributeValueBuilder.Append(startDelimiter);
                        attributeValueBuilder.Append(processedExpression);
                        attributeValueBuilder.Append(endDelimiter);
                    },
                    (localContext, textValue) =>
                    {
                        string processedTextValue = textValue;
                        if (attributeType == HtmlAttributeType.ClassName)
                        {
                            processedTextValue = Utils.CollapseWhitespace(textValue);
                        }

                        attributeValueBuilder.Append(processedTextValue);
                    }
                );

                result = attributeValueBuilder.ToString();
                attributeValueBuilder.Clear();

                switch (attributeType)
                {
                    case HtmlAttributeType.Uri:
                    case HtmlAttributeType.Numeric:
                    case HtmlAttributeType.ClassName:
                        result = result.Trim();
                        break;
                    case HtmlAttributeType.Style:
                        result = result.Trim();
                        result = Utils.RemoveEndingSemicolon(result);
                        break;
                    default:
                        if (_settings.MinifyAngularBindingExpressions)
                        {
                            string elementDirectiveName = AngularHelpers.NormalizeDirectiveName(tagName);
                            if (elementDirectiveName == "ngPluralize" && attributeName == "when")
                            {
                                result = MinifyAngularBindingExpression(context, attribute.ValueCoordinates, result);
                            }
                        }

                        break;
                }
            }
            else
            {
                switch (attributeType)
                {
                    case HtmlAttributeType.Uri:
                        result = result.Trim();

                        if (result.StartsWith(HTTP_PROTOCOL, StringComparison.OrdinalIgnoreCase))
                        {
                            if (_settings.RemoveHttpProtocolFromAttributes && !ContainsRelExternalAttribute(attributes))
                            {
                                int httpProtocolLength = HTTP_PROTOCOL.Length;
                                result = result.Substring(httpProtocolLength);
                            }
                        }
                        else if (result.StartsWith(HTTPS_PROTOCOL, StringComparison.OrdinalIgnoreCase))
                        {
                            if (_settings.RemoveHttpsProtocolFromAttributes && !ContainsRelExternalAttribute(attributes))
                            {
                                int httpsProtocolLength = HTTPS_PROTOCOL.Length;
                                result = result.Substring(httpsProtocolLength);
                            }
                        }
                        else if (result == "href" && result.StartsWith(JS_PROTOCOL, StringComparison.OrdinalIgnoreCase))
                        {
                            result = ProcessInlineScriptContent(context, attribute);
                        }

                        break;
                    case HtmlAttributeType.Numeric:
                        result = result.Trim();
                        break;
                    case HtmlAttributeType.ClassName:
                        if (AngularHelpers.IsClassDirective(result))
                        {
                            // Processing of Angular class directives
                            string ngOriginalDirectiveName = string.Empty;
                            string ngNormalizedDirectiveName = string.Empty;
                            string ngExpression;
                            var ngDirectives = new Dictionary<string, string>();

                            AngularHelpers.ParseClassDirective(result,
                                (localContext, originalDirectiveName, normalizedDirectiveName) =>
                                {
                                    ngOriginalDirectiveName = originalDirectiveName;
                                    ngNormalizedDirectiveName = normalizedDirectiveName;
                                    ngExpression = null;

                                    ngDirectives.Add(ngOriginalDirectiveName, ngExpression);
                                },
                                (localContext, expression) =>
                                {
                                    ngExpression = expression;
                                    if (_settings.MinifyAngularBindingExpressions
                                        && ContainsAngularBindingExpression(ngNormalizedDirectiveName))
                                    {
                                        ngExpression = MinifyAngularBindingExpression(context,
                                            attribute.ValueCoordinates, localContext.NodeCoordinates,
                                            expression);
                                    }

                                    ngDirectives[ngOriginalDirectiveName] = ngExpression;
                                },
                                localContext =>
                                {
                                    if (ngDirectives[ngOriginalDirectiveName] == null)
                                    {
                                        ngDirectives[ngOriginalDirectiveName] = string.Empty;
                                    }
                                }
                            );

                            int directiveCount = ngDirectives.Count;
                            if (directiveCount > 0)
                            {
                                var directiveBuilder = new StringBuilder();
                                int directiveIndex = 0;
                                int lastDirectiveIndex = directiveCount - 1;
                                string previousExpression = null;

                                foreach (var directive in ngDirectives)
                                {
                                    string directiveName = directive.Key;
                                    string expression = directive.Value;

                                    if (directiveIndex > 0 && (expression == null || previousExpression == null))
                                    {
                                        directiveBuilder.Append(" ");
                                    }

                                    directiveBuilder.Append(directiveName);
                                    if (!string.IsNullOrWhiteSpace(expression))
                                    {
                                        directiveBuilder.AppendFormat(":{0}", expression);
                                    }

                                    if (directiveIndex < lastDirectiveIndex && expression != null)
                                    {
                                        directiveBuilder.Append(";");
                                    }

                                    previousExpression = expression;
                                    directiveIndex++;
                                }

                                result = directiveBuilder.ToString();
                                directiveBuilder.Clear();
                            }
                            else
                            {
                                result = string.Empty;
                            }
                        }
                        else
                        {
                            result = result.Trim();
                            result = Utils.CollapseWhitespace(result);
                        }

                        break;
                    case HtmlAttributeType.Style:
                        result = ProcessInlineStyleContent(context, attribute);
                        break;
                    case HtmlAttributeType.Event:
                        result = ProcessInlineScriptContent(context, attribute);
                        break;
                    default:
                        if (attributeName == "data-bind" && _settings.MinifyKnockoutBindingExpressions)
                        {
                            result = MinifyKnockoutBindingExpression(context, attribute);
                        }
                        else if (tagName == "meta" && attributeName == "content"
                            && attributes.Any(a => a.Name == "name" && a.Value.Trim().IgnoreCaseEquals("keywords")))
                        {
                            result = result.Trim();
                            result = Utils.CollapseWhitespace(result);
                            result = _separatingCommaWithSpacesRegex.Replace(result, ",");
                            result = _endingCommaWithSpacesRegex.Replace(result, string.Empty);
                        }
                        else
                        {
                            if (_settings.MinifyAngularBindingExpressions
                                && CanMinifyAngularBindingExpressionInAttribute(tagName, attributeName, attributes))
                            {
                                result = MinifyAngularBindingExpression(context, attribute.ValueCoordinates, result);
                            }
                        }

                        break;
                }
            }

            return result;
        }
Example #30
0
        /// <summary>
        /// Start tags handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="tagName">Tag name</param>
        /// <param name="attributes">List of attributes</param>
        private void StartTagHandler(MarkupParsingContext context, string tagName,
			IList<XmlAttribute> attributes)
        {
            XmlNodeType previousNodeType = _currentNodeType;

            _currentNodeType = XmlNodeType.StartTag;
            _currentText = string.Empty;

            if (_settings.MinifyWhitespace && previousNodeType == XmlNodeType.Text
                && (_startTagBeforeText || _endTagBeforeText || _emptyTagBeforeText
                    || _xmlDeclarationBeforeText || _processingInstructionBeforeText || _doctypeBeforeText))
            {
                RemoveLastWhitespaceBufferItems();
            }

            _buffer.Add("<");
            _buffer.Add(tagName);
            RenderAttributes(attributes);
            _buffer.Add(">");
        }
Example #31
0
        /// <summary>
        /// Ignored fragments handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="fragment">Ignored fragment</param>
        private void IgnoredFragmentHandler(MarkupParsingContext context, string fragment)
        {
            _currentNodeType = XmlNodeType.IgnoredFragment;

            if (_settings.MinifyWhitespace)
            {
                RemoveLastWhitespaceBufferItems();
            }

            if (fragment.Length > 0)
            {
                _buffer.Add(fragment);
            }
        }
Example #32
0
        /// <summary>
        /// XML declaration handler
        /// </summary>
        /// <param name="context">Markup parsing context</param>
        /// <param name="attributes">List of attributes</param>
        private void XmlDeclarationHandler(MarkupParsingContext context, IList<XmlAttribute> attributes)
        {
            _currentNodeType = XmlNodeType.XmlDeclaration;

            if (_settings.MinifyWhitespace)
            {
                RemoveLastWhitespaceBufferItems();
            }

            _buffer.Add("<?xml");
            RenderAttributes(attributes);
            _buffer.Add("?>");
        }
Example #33
0
        /// <summary>
        /// Parses XML content
        /// </summary>
        /// <param name="content">XML content</param>
        public void Parse(string content)
        {
            int contentLength = content.Length;
            if (contentLength == 0)
            {
                return;
            }

            lock (_parsingSynchronizer)
            {
                _innerContext = new InnerMarkupParsingContext(content);
                _context = new MarkupParsingContext(_innerContext);

                _tagStack = new Stack<StackedXmlTag>();

                int endPosition = contentLength - 1;
                int previousPosition = -1;

                try
                {
                    while (_innerContext.Position <= endPosition)
                    {
                        bool isProcessed = false;

                        if (content.CustomStartsWith("<", _innerContext.Position, StringComparison.Ordinal))
                        {
                            if (content.CustomStartsWith("</", _innerContext.Position, StringComparison.Ordinal))
                            {
                                // End tag
                                isProcessed = ProcessEndTag();
                            }
                            else if (content.CustomStartsWith("<!", _innerContext.Position, StringComparison.Ordinal))
                            {
                                // XML comments
                                isProcessed = ProcessComment();

                                if (!isProcessed)
                                {
                                    // CDATA sections
                                    isProcessed = ProcessCdataSection();
                                }

                                if (!isProcessed)
                                {
                                    // Doctype declaration
                                    isProcessed = ProcessDoctype();
                                }
                            }
                            else if (content.CustomStartsWith("<?", _innerContext.Position, StringComparison.Ordinal))
                            {
                                // XML declaration and processing instructions
                                isProcessed = ProcessProcessingInstruction();
                            }
                            else
                            {
                                // Start tag
                                isProcessed = ProcessStartTag();
                            }
                        }

                        if (!isProcessed)
                        {
                            // Text
                            ProcessText();
                        }

                        if (_innerContext.Position == previousPosition)
                        {
                            throw new XmlParsingException(
                                string.Format(Strings.ErrorMessage_MarkupParsingFailed, "XML"),
                                _innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
                        }

                        previousPosition = _innerContext.Position;
                    }

                    // Check whether there were not closed tags
                    if (_tagStack.Count > 0)
                    {
                        StackedXmlTag stackedTag = _tagStack.Pop();

                        throw new XmlParsingException(
                            string.Format(Strings.ErrorMessage_NotClosedTag, stackedTag.Name),
                            stackedTag.Coordinates,
                            SourceCodeNavigator.GetSourceFragment(_innerContext.SourceCode, stackedTag.Coordinates));
                    }
                }
                catch (XmlParsingException)
                {
                    throw;
                }
                finally
                {
                    _tagStack.Clear();

                    _context = null;
                    _innerContext = null;
                }
            }
        }