/// <summary> /// Calculates a absolute node coordinates /// </summary> /// <param name="baseNodeCoordinates">Base node coordinates</param> /// <param name="lineBreakCount">Number of line breaks</param> /// <param name="charRemainderCount">Number of characters left</param> /// <returns>Absolute node coordinates</returns> public static SourceCodeNodeCoordinates CalculateAbsoluteNodeCoordinates( SourceCodeNodeCoordinates baseNodeCoordinates, int lineBreakCount, int charRemainderCount) { int absoluteLineNumber; int absoluteColumnNumber; if (!baseNodeCoordinates.IsEmpty) { int baseLineNumber = baseNodeCoordinates.LineNumber; int baseColumnNumber = baseNodeCoordinates.ColumnNumber; if (lineBreakCount > 0) { absoluteLineNumber = baseLineNumber + lineBreakCount; absoluteColumnNumber = charRemainderCount + 1; } else { absoluteLineNumber = baseLineNumber; absoluteColumnNumber = baseColumnNumber + charRemainderCount; } } else { absoluteLineNumber = lineBreakCount + 1; absoluteColumnNumber = charRemainderCount + 1; } var absoluteNodeCoordinates = new SourceCodeNodeCoordinates(absoluteLineNumber, absoluteColumnNumber); return(absoluteNodeCoordinates); }
/// <summary> /// Gets a source fragment /// </summary> /// <param name="sourceCode">Source code</param> /// <param name="nodePosition">Current node position</param> /// <param name="tabSize">Number of spaces in the tab</param> /// <param name="maxFragmentLength">Maximum length of the source fragment</param> /// <returns>Source fragment</returns> public static string GetSourceFragment(string sourceCode, int nodePosition, byte tabSize = DEFAULT_TAB_SIZE, int maxFragmentLength = DEFAULT_MAX_FRAGMENT_LENGTH) { SourceCodeNodeCoordinates nodeCoordinates = CalculateNodeCoordinates(sourceCode, nodePosition); return(GetSourceFragment(sourceCode, nodeCoordinates, tabSize)); }
/// <summary> /// Constructs instance of HTML attribute /// </summary> /// <param name="name">Name</param> /// <param name="value">Value</param> /// <param name="type">Type</param> /// <param name="nameCoordinates">Coordinates of name</param> /// <param name="valueCoordinates">Coordinates of value</param> public HtmlAttribute(string name, string value, HtmlAttributeType type, SourceCodeNodeCoordinates nameCoordinates, SourceCodeNodeCoordinates valueCoordinates) { Name = name; Value = value; Type = type; NameCoordinates = nameCoordinates; ValueCoordinates = valueCoordinates; }
/// <summary> /// Calculates a node coordinates /// </summary> /// <param name="sourceCode">Source code</param> /// <param name="nodePosition">Current node position</param> /// <returns>Node coordinates</returns> public static SourceCodeNodeCoordinates CalculateNodeCoordinates(string sourceCode, int nodePosition) { if (string.IsNullOrEmpty(sourceCode) || nodePosition >= sourceCode.Length) { return(SourceCodeNodeCoordinates.Empty); } int fragmentLength = nodePosition + 1; int lineBreakCount; int charRemainderCount; CalculateLineBreakCount(sourceCode, 0, fragmentLength, out lineBreakCount, out charRemainderCount); var nodeCoordinates = new SourceCodeNodeCoordinates(lineBreakCount + 1, charRemainderCount + 1); return(nodeCoordinates); }
/// <summary> /// Calculates a absolute node coordinates /// </summary> /// <param name="baseNodeCoordinates">Base node coordinates</param> /// <param name="relativeNodeCoordinates">Relative node coordinates</param> /// <returns>Absolute node coordinates</returns> public static SourceCodeNodeCoordinates CalculateAbsoluteNodeCoordinates( SourceCodeNodeCoordinates baseNodeCoordinates, SourceCodeNodeCoordinates relativeNodeCoordinates) { int relativeLineNumber = relativeNodeCoordinates.LineNumber; int relativeColumnNumber = relativeNodeCoordinates.ColumnNumber; int absoluteLineNumber; int absoluteColumnNumber; if (!baseNodeCoordinates.IsEmpty) { int baseLineNumber = baseNodeCoordinates.LineNumber; int baseColumnNumber = baseNodeCoordinates.ColumnNumber; absoluteLineNumber = baseLineNumber; absoluteColumnNumber = baseColumnNumber; if (relativeLineNumber > 0) { if (relativeLineNumber == 1) { if (relativeColumnNumber > 0) { absoluteColumnNumber = baseColumnNumber + relativeColumnNumber - 1; } } else { absoluteLineNumber = baseLineNumber + relativeLineNumber - 1; absoluteColumnNumber = relativeColumnNumber; } } } else { absoluteLineNumber = relativeLineNumber; absoluteColumnNumber = relativeColumnNumber; } var absoluteNodeCoordinates = new SourceCodeNodeCoordinates(absoluteLineNumber, absoluteColumnNumber); return absoluteNodeCoordinates; }
/// <summary> /// Calculates a absolute node coordinates /// </summary> /// <param name="baseNodeCoordinates">Base node coordinates</param> /// <param name="relativeNodeCoordinates">Relative node coordinates</param> /// <returns>Absolute node coordinates</returns> public static SourceCodeNodeCoordinates CalculateAbsoluteNodeCoordinates( SourceCodeNodeCoordinates baseNodeCoordinates, SourceCodeNodeCoordinates relativeNodeCoordinates) { int relativeLineNumber = relativeNodeCoordinates.LineNumber; int relativeColumnNumber = relativeNodeCoordinates.ColumnNumber; int absoluteLineNumber; int absoluteColumnNumber; if (!baseNodeCoordinates.IsEmpty) { int baseLineNumber = baseNodeCoordinates.LineNumber; int baseColumnNumber = baseNodeCoordinates.ColumnNumber; absoluteLineNumber = baseLineNumber; absoluteColumnNumber = baseColumnNumber; if (relativeLineNumber > 0) { if (relativeLineNumber == 1) { if (relativeColumnNumber > 0) { absoluteColumnNumber = baseColumnNumber + relativeColumnNumber - 1; } } else { absoluteLineNumber = baseLineNumber + relativeLineNumber - 1; absoluteColumnNumber = relativeColumnNumber; } } } else { absoluteLineNumber = relativeLineNumber; absoluteColumnNumber = relativeColumnNumber; } var absoluteNodeCoordinates = new SourceCodeNodeCoordinates(absoluteLineNumber, absoluteColumnNumber); return(absoluteNodeCoordinates); }
/// <summary> /// Calculates a absolute node coordinates /// </summary> /// <param name="baseNodeCoordinates">Base node coordinates</param> /// <param name="additionalContent">Additional content</param> /// <returns>Absolute node coordinates</returns> public static SourceCodeNodeCoordinates CalculateAbsoluteNodeCoordinates( SourceCodeNodeCoordinates baseNodeCoordinates, string additionalContent) { int lineBreakCount = 0; int charRemainderCount = 0; if (!string.IsNullOrEmpty(additionalContent)) { CalculateLineBreakCount(additionalContent, out lineBreakCount, out charRemainderCount); } int absoluteLineNumber; int absoluteColumnNumber; if (!baseNodeCoordinates.IsEmpty) { int baseLineNumber = baseNodeCoordinates.LineNumber; int baseColumnNumber = baseNodeCoordinates.ColumnNumber; if (lineBreakCount > 0) { absoluteLineNumber = baseLineNumber + lineBreakCount; absoluteColumnNumber = charRemainderCount + 1; } else { absoluteLineNumber = baseLineNumber; absoluteColumnNumber = baseColumnNumber + charRemainderCount; } } else { absoluteLineNumber = lineBreakCount + 1; absoluteColumnNumber = charRemainderCount + 1; } var absoluteNodeCoordinates = new SourceCodeNodeCoordinates(absoluteLineNumber, absoluteColumnNumber); return(absoluteNodeCoordinates); }
/// <summary> /// Calculates a absolute node coordinates /// </summary> /// <param name="baseNodeCoordinates">Base node coordinates</param> /// <param name="additionalContent">Additional content</param> /// <returns>Absolute node coordinates</returns> public static SourceCodeNodeCoordinates CalculateAbsoluteNodeCoordinates( SourceCodeNodeCoordinates baseNodeCoordinates, string additionalContent) { int lineBreakCount = 0; int charRemainderCount = 0; if (!string.IsNullOrEmpty(additionalContent)) { CalculateLineBreakCount(additionalContent, out lineBreakCount, out charRemainderCount); } int absoluteLineNumber; int absoluteColumnNumber; if (!baseNodeCoordinates.IsEmpty) { int baseLineNumber = baseNodeCoordinates.LineNumber; int baseColumnNumber = baseNodeCoordinates.ColumnNumber; if (lineBreakCount > 0) { absoluteLineNumber = baseLineNumber + lineBreakCount; absoluteColumnNumber = charRemainderCount + 1; } else { absoluteLineNumber = baseLineNumber; absoluteColumnNumber = baseColumnNumber + charRemainderCount; } } else { absoluteLineNumber = lineBreakCount + 1; absoluteColumnNumber = charRemainderCount + 1; } var absoluteNodeCoordinates = new SourceCodeNodeCoordinates(absoluteLineNumber, absoluteColumnNumber); return absoluteNodeCoordinates; }
/// <summary> /// Initializes a new instance of the WebMarkupMin.Core.CodeProcessingException class /// with a specified error message, node coordinates, source fragment /// and reference to the inner exception that is the cause of this exception /// </summary> /// <param name="message">Error message that explains the reason for the exception</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="sourceFragment">Source fragment</param> /// <param name="innerException">Exception that is the cause of the current exception</param> protected CodeProcessingException(string message, SourceCodeNodeCoordinates nodeCoordinates, string sourceFragment, Exception innerException) : this(message, nodeCoordinates.LineNumber, nodeCoordinates.ColumnNumber, sourceFragment, innerException) { }
/// <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); }
/// <summary> /// Gets a source fragment /// </summary> /// <param name="sourceCode">Source code</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="tabSize">Number of spaces in the tab</param> /// <param name="maxFragmentLength">Maximum length of the source fragment</param> /// <returns>Source fragment</returns> public static string GetSourceFragment(string sourceCode, SourceCodeNodeCoordinates nodeCoordinates, byte tabSize = DEFAULT_TAB_SIZE, int maxFragmentLength = DEFAULT_MAX_FRAGMENT_LENGTH) { string sourceFragment = string.Empty; int lineNumber = nodeCoordinates.LineNumber; int columnNumber = nodeCoordinates.ColumnNumber; if (!string.IsNullOrEmpty(sourceCode)) { int previousLineNumber = lineNumber - 1; int currentLineNumber = lineNumber; int nextLineNumber = lineNumber + 1; string previousLine = string.Empty; string currentLine = string.Empty; string nextLine = string.Empty; int lineCount = 0; int lineBreakPosition = int.MinValue; int lineBreakLength = 0; do { string line; int startLinePosition = (lineBreakPosition == int.MinValue) ? 0 : lineBreakPosition + lineBreakLength; FindNextLineBreak(sourceCode, startLinePosition, out lineBreakPosition, out lineBreakLength); if (lineBreakPosition != -1) { line = sourceCode.Substring(startLinePosition, lineBreakPosition - startLinePosition); } else { line = sourceCode.Substring(startLinePosition); } lineCount++; if (lineCount == previousLineNumber) { previousLine = line; } else if (lineCount == currentLineNumber) { currentLine = line; } else if (lineCount == nextLineNumber) { nextLine = line; } } while (lineBreakPosition != -1 && lineCount <= nextLineNumber); int lineNumberSize = (nextLineNumber).ToString(CultureInfo.InvariantCulture).Length; if (currentLineNumber == lineCount) { lineNumberSize = currentLineNumber.ToString(CultureInfo.InvariantCulture).Length; } int fragmentStartPosition; int fragmentLength; CalculateCutPositions(currentLine, columnNumber, maxFragmentLength, out fragmentStartPosition, out fragmentLength); StringBuilder sourceFragmentBuilder = StringBuilderPool.GetBuilder(); if (currentLine.Length > 0) { if (previousLine.Length > 0) { sourceFragmentBuilder.AppendLine(FormatSourceCodeLine(previousLine, new SourceCodeNodeCoordinates(previousLineNumber, 0), lineNumberSize, fragmentStartPosition, fragmentLength, tabSize)); } sourceFragmentBuilder.AppendLine(FormatSourceCodeLine(currentLine, new SourceCodeNodeCoordinates(currentLineNumber, columnNumber), lineNumberSize, fragmentStartPosition, fragmentLength, tabSize)); if (nextLine.Length > 0) { sourceFragmentBuilder.AppendLine(FormatSourceCodeLine(nextLine, new SourceCodeNodeCoordinates(nextLineNumber, 0), lineNumberSize, fragmentStartPosition, fragmentLength, tabSize)); } } sourceFragment = sourceFragmentBuilder.ToString(); StringBuilderPool.ReleaseBuilder(sourceFragmentBuilder); } return sourceFragment; }
/// <summary> /// Calculates a node coordinates /// </summary> /// <param name="sourceCode">Source code</param> /// <param name="nodePosition">Current node position</param> /// <returns>Node coordinates</returns> public static SourceCodeNodeCoordinates CalculateNodeCoordinates(string sourceCode, int nodePosition) { if (string.IsNullOrEmpty(sourceCode) || nodePosition >= sourceCode.Length) { return SourceCodeNodeCoordinates.Empty; } int fragmentLength = nodePosition + 1; int lineBreakCount; int charRemainderCount; CalculateLineBreakCount(sourceCode, 0, fragmentLength, out lineBreakCount, out charRemainderCount); var nodeCoordinates = new SourceCodeNodeCoordinates(lineBreakCount + 1, charRemainderCount + 1); return nodeCoordinates; }
/// <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)); } }
/// <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; }
/// <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> /// Initializes a new instance of the WebMarkupMin.Core.CodeProcessingException class /// with a specified error message, node coordinates and source fragment /// </summary> /// <param name="message">Error message that explains the reason for the exception</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="sourceFragment">Source fragment</param> protected CodeProcessingException(string message, SourceCodeNodeCoordinates nodeCoordinates, string sourceFragment) : this(message, nodeCoordinates, sourceFragment, null) { }
/// <summary> /// Calculates a absolute node coordinates /// </summary> /// <param name="baseNodeCoordinates">Base node coordinates</param> /// <param name="lineBreakCount">Number of line breaks</param> /// <param name="charRemainderCount">Number of characters left</param> /// <returns>Absolute node coordinates</returns> public static SourceCodeNodeCoordinates CalculateAbsoluteNodeCoordinates( SourceCodeNodeCoordinates baseNodeCoordinates, int lineBreakCount, int charRemainderCount) { int absoluteLineNumber; int absoluteColumnNumber; if (!baseNodeCoordinates.IsEmpty) { int baseLineNumber = baseNodeCoordinates.LineNumber; int baseColumnNumber = baseNodeCoordinates.ColumnNumber; if (lineBreakCount > 0) { absoluteLineNumber = baseLineNumber + lineBreakCount; absoluteColumnNumber = charRemainderCount + 1; } else { absoluteLineNumber = baseLineNumber; absoluteColumnNumber = baseColumnNumber + charRemainderCount; } } else { absoluteLineNumber = lineBreakCount + 1; absoluteColumnNumber = charRemainderCount + 1; } var absoluteNodeCoordinates = new SourceCodeNodeCoordinates(absoluteLineNumber, absoluteColumnNumber); return absoluteNodeCoordinates; }
private static SourceCodeNodeCoordinates CalculateAbsoluteInlineCodeErrorCoordinates( SourceCodeNodeCoordinates tagCoordinates, SourceCodeNodeCoordinates attributeCoordinates, SourceCodeNodeCoordinates relativeErrorCoordinates) { SourceCodeNodeCoordinates absoluteErrorCoordinates; if (!attributeCoordinates.IsEmpty) { absoluteErrorCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates( attributeCoordinates, relativeErrorCoordinates); } else { absoluteErrorCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates( tagCoordinates, relativeErrorCoordinates); } return absoluteErrorCoordinates; }
/// <summary> /// Parses a start tag /// </summary> /// <param name="tagName">Tag name</param> /// <param name="tagNameInLowercase">Tag name in lowercase</param> /// <param name="attributesString">String representation of the attribute list</param> /// <param name="attributesCoordinates">Attributes coordinates</param> /// <param name="isEmptyTag">Flag that tag is empty</param> private void ParseStartTag(string tagName, string tagNameInLowercase, string attributesString, SourceCodeNodeCoordinates attributesCoordinates, bool isEmptyTag) { HtmlTagFlags tagFlags = GetTagFlagsByName(tagNameInLowercase); if (tagFlags.HasFlag(HtmlTagFlags.Optional)) { HtmlTag lastStackedTag = _tagStack.LastOrDefault(); if (lastStackedTag != null && lastStackedTag.NameInLowercase == tagNameInLowercase) { ParseEndTag(lastStackedTag.Name, lastStackedTag.NameInLowercase); } else { if (tagNameInLowercase == "body" && _tagStack.Any(t => t.NameInLowercase == "head")) { HtmlTag headTag = _tagStack.Single(t => t.NameInLowercase == "head"); ParseEndTag(headTag.Name, headTag.NameInLowercase); } } } if (tagFlags.HasFlag(HtmlTagFlags.Empty)) { isEmptyTag = true; } else if (isEmptyTag) { tagFlags |= HtmlTagFlags.Empty; } var attributes = ParseAttributes(tagName, tagNameInLowercase, tagFlags, attributesString, attributesCoordinates); var tag = new HtmlTag(tagName, tagNameInLowercase, attributes, tagFlags); if (!isEmptyTag) { if (_conditionalCommentOpened) { HtmlConditionalComment lastConditionalComment = _conditionalCommentStack.Peek(); HtmlConditionalCommentType lastConditionalCommentType = lastConditionalComment.Type; if (tagFlags.HasFlag(HtmlTagFlags.EmbeddedCode) || lastConditionalCommentType == HtmlConditionalCommentType.RevealedValidating || lastConditionalCommentType == HtmlConditionalCommentType.RevealedValidatingSimplified) { _tagStack.Add(tag); } } else { _tagStack.Add(tag); } } if (_handlers.StartTag != null) { _handlers.StartTag(_context, tag); } if (tagFlags.HasFlag(HtmlTagFlags.Xml) && !tagFlags.HasFlag(HtmlTagFlags.NonIndependent)) { _xmlTagStack.Push(tagNameInLowercase); } }
/// <summary> /// Constructs instance of inner markup parsing context /// </summary> /// <param name="sourceCode">Source code</param> public InnerMarkupParsingContext(string sourceCode) { _sourceCode = sourceCode; _position = 0; _nodeCoordinates = new SourceCodeNodeCoordinates(1, 1); }
/// <summary> /// Formats a line of source code /// </summary> /// <param name="line">Line content</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="lineNumberSize">Number of symbols in the line number caption</param> /// <param name="fragmentStartPosition">Start position of source fragment</param> /// <param name="fragmentLength">Length of source fragment</param> /// <param name="tabSize">Number of spaces in the tab</param> /// <returns>Formatted line</returns> private static string FormatSourceCodeLine(string line, SourceCodeNodeCoordinates nodeCoordinates, int lineNumberSize, int fragmentStartPosition = 0, int fragmentLength = 0, byte tabSize = 4) { const string ellipsisSymbol = "…"; const byte leftPaddingSize = 7; int lineNumber = nodeCoordinates.LineNumber; int columnNumber = nodeCoordinates.ColumnNumber; int lineLength = line.Length; string processedLine; if ((fragmentStartPosition == 0 && fragmentLength == lineLength)) { processedLine = line; } else if (fragmentStartPosition >= lineLength) { processedLine = string.Empty; } else { int fragmentEndPosition = fragmentStartPosition + fragmentLength - 1; bool beginningCutOff = (fragmentStartPosition > 0); bool endingCutOff = (fragmentEndPosition <= lineLength); if (fragmentEndPosition + 1 == lineLength) { endingCutOff = false; } if (fragmentEndPosition >= lineLength) { endingCutOff = false; fragmentEndPosition = lineLength - 1; fragmentLength = fragmentEndPosition - fragmentStartPosition + 1; } processedLine = line.Substring(fragmentStartPosition, fragmentLength); if (beginningCutOff) { processedLine = ellipsisSymbol + processedLine; } if (endingCutOff) { processedLine = processedLine + ellipsisSymbol; } } string result = string.Format("Line {0}: {1}", lineNumber.ToString(CultureInfo.InvariantCulture).PadLeft(lineNumberSize), processedLine.TabsToSpaces(tabSize)); if (columnNumber > 0) { int cursorOffset = columnNumber - fragmentStartPosition; if (fragmentStartPosition > 0) { cursorOffset++; } result += Environment.NewLine + string.Empty .PadRight(processedLine.Substring(0, cursorOffset - 1) .TabsToSpaces(tabSize).Length + lineNumberSize + leftPaddingSize) .Replace(" ", "-") + "^" ; } return result; }
/// <summary> /// Constructs instance of stacked XML tag /// </summary> /// <param name="name">Name</param> /// <param name="coordinates">Coordinates of tag</param> public StackedXmlTag(string name, SourceCodeNodeCoordinates coordinates) { Name = name; Coordinates = coordinates; }
/// <summary> /// Parses a attributes /// </summary> /// <param name="tagName">Tag name</param> /// <param name="tagNameInLowercase">Tag name in lowercase</param> /// <param name="tagFlags">Tag flags</param> /// <param name="attributesString">String representation of the attribute list</param> /// <param name="attributesCoordinates">Attributes coordinates</param> /// <returns>List of attributes</returns> private IList<HtmlAttribute> ParseAttributes(string tagName, string tagNameInLowercase, HtmlTagFlags tagFlags, string attributesString, SourceCodeNodeCoordinates attributesCoordinates) { var attributes = new List<HtmlAttribute>(); if (string.IsNullOrWhiteSpace(attributesString)) { return attributes; } SourceCodeNodeCoordinates currentAttributesCoordinates = attributesCoordinates; int currentPosition = 0; MatchCollection attributeMatches = _attributeRegex.Matches(attributesString); foreach (Match attributeMatch in attributeMatches) { GroupCollection groups = attributeMatch.Groups; Group attributeNameGroup = groups["attributeName"]; Group attributeEqualSignGroup = groups["attributeEqualSign"]; Group attributeValueGroup = groups["attributeValue"]; string attributeName = attributeNameGroup.Value; string attributeNameInLowercase = attributeName; if (Utils.ContainsUppercaseCharacters(attributeName)) { attributeNameInLowercase = attributeName.ToLowerInvariant(); } string attributeValue = null; if (attributeEqualSignGroup.Success) { if (attributeValueGroup.Success) { attributeValue = attributeValueGroup.Value; if (!string.IsNullOrWhiteSpace(attributeValue)) { attributeValue = HtmlAttribute.HtmlAttributeDecode(attributeValue); } } else { attributeValue = string.Empty; } } var attributeNameCoordinates = SourceCodeNodeCoordinates.Empty; int attributeNamePosition = -1; if (attributeNameGroup.Success) { attributeNamePosition = attributeNameGroup.Index; } if (attributeNamePosition != -1) { int lineBreakCount; int charRemainderCount; SourceCodeNavigator.CalculateLineBreakCount(attributesString, currentPosition, attributeNamePosition - currentPosition, out lineBreakCount, out charRemainderCount); attributeNameCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates( currentAttributesCoordinates, lineBreakCount, charRemainderCount); currentAttributesCoordinates = attributeNameCoordinates; currentPosition = attributeNamePosition; } var attributeValueCoordinates = SourceCodeNodeCoordinates.Empty; int attributeValuePosition = -1; if (attributeValueGroup.Success) { attributeValuePosition = attributeValueGroup.Index; } if (attributeValuePosition != -1) { int lineBreakCount; int charRemainderCount; SourceCodeNavigator.CalculateLineBreakCount(attributesString, currentPosition, attributeValuePosition - currentPosition, out lineBreakCount, out charRemainderCount); attributeValueCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates( currentAttributesCoordinates, lineBreakCount, charRemainderCount); currentAttributesCoordinates = attributeValueCoordinates; currentPosition = attributeValuePosition; } HtmlAttributeType attributeType = GetAttributeType(tagNameInLowercase, tagFlags, attributeNameInLowercase, attributes); attributes.Add(new HtmlAttribute(attributeName, attributeNameInLowercase, attributeValue, attributeType, attributeNameCoordinates, attributeValueCoordinates)); } return attributes; }
/// <summary> /// Minify a Angular binding expression /// </summary> /// <param name="context">Markup parsing context</param> /// <param name="attributeCoordinates">Coordinates of attribute value</param> /// <param name="expression">Binding expression</param> /// <returns>Minified binding expression</returns> private string MinifyAngularBindingExpression(MarkupParsingContext context, SourceCodeNodeCoordinates attributeCoordinates, string expression) { return MinifyAngularBindingExpression(context, attributeCoordinates, SourceCodeNodeCoordinates.Empty, expression); }
/// <summary> /// Increases a current parsing position /// </summary> /// <param name="increment">Increment</param> public void IncreasePosition(int increment) { int oldPosition = _position; int newPosition = oldPosition + increment; int fragmentStartPosition = oldPosition; int fragmentLength = increment; int lineBreakCount; int charRemainderCount; SourceCodeNavigator.CalculateLineBreakCount(_sourceCode, fragmentStartPosition, fragmentLength, out lineBreakCount, out charRemainderCount); SourceCodeNodeCoordinates currentNodeCoordinates = _nodeCoordinates; _nodeCoordinates = SourceCodeNavigator.CalculateAbsoluteNodeCoordinates(currentNodeCoordinates, lineBreakCount, charRemainderCount); _position = newPosition; }
/// <summary> /// Gets a source fragment /// </summary> /// <param name="sourceCode">Source code</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="tabSize">Number of spaces in the tab</param> /// <param name="maxFragmentLength">Maximum length of the source fragment</param> /// <returns>Source fragment</returns> public static string GetSourceFragment(string sourceCode, SourceCodeNodeCoordinates nodeCoordinates, byte tabSize = DEFAULT_TAB_SIZE, int maxFragmentLength = DEFAULT_MAX_FRAGMENT_LENGTH) { string sourceFragment = string.Empty; int lineNumber = nodeCoordinates.LineNumber; int columnNumber = nodeCoordinates.ColumnNumber; if (!string.IsNullOrEmpty(sourceCode)) { int previousLineNumber = lineNumber - 1; int currentLineNumber = lineNumber; int nextLineNumber = lineNumber + 1; string previousLine = string.Empty; string currentLine = string.Empty; string nextLine = string.Empty; int lineCount = 0; int lineBreakPosition = int.MinValue; int lineBreakLength = 0; do { string line; int startLinePosition = (lineBreakPosition == int.MinValue) ? 0 : lineBreakPosition + lineBreakLength; FindNextLineBreak(sourceCode, startLinePosition, out lineBreakPosition, out lineBreakLength); if (lineBreakPosition != -1) { line = sourceCode.Substring(startLinePosition, lineBreakPosition - startLinePosition); } else { line = sourceCode.Substring(startLinePosition); } lineCount++; if (lineCount == previousLineNumber) { previousLine = line; } else if (lineCount == currentLineNumber) { currentLine = line; } else if (lineCount == nextLineNumber) { nextLine = line; } }while (lineBreakPosition != -1 && lineCount <= nextLineNumber); int lineNumberSize = (nextLineNumber).ToString(CultureInfo.InvariantCulture).Length; if (currentLineNumber == lineCount) { lineNumberSize = currentLineNumber.ToString(CultureInfo.InvariantCulture).Length; } int fragmentStartPosition; int fragmentLength; CalculateCutPositions(currentLine, columnNumber, maxFragmentLength, out fragmentStartPosition, out fragmentLength); var sourceFragmentBuilder = new StringBuilder(); if (currentLine.Length > 0) { if (previousLine.Length > 0) { sourceFragmentBuilder.AppendLine(FormatSourceCodeLine(previousLine, new SourceCodeNodeCoordinates(previousLineNumber, 0), lineNumberSize, fragmentStartPosition, fragmentLength, tabSize)); } sourceFragmentBuilder.AppendLine(FormatSourceCodeLine(currentLine, new SourceCodeNodeCoordinates(currentLineNumber, columnNumber), lineNumberSize, fragmentStartPosition, fragmentLength, tabSize)); if (nextLine.Length > 0) { sourceFragmentBuilder.AppendLine(FormatSourceCodeLine(nextLine, new SourceCodeNodeCoordinates(nextLineNumber, 0), lineNumberSize, fragmentStartPosition, fragmentLength, tabSize)); } } sourceFragment = sourceFragmentBuilder.ToString(); } return(sourceFragment); }
/// <summary> /// Initializes a new instance of the <see cref="MarkupParsingException"/> class /// with a specified error message, node coordinates and source fragment /// </summary> /// <param name="message">Error message that explains the reason for the exception</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="sourceFragment">Source fragment</param> public MarkupParsingException(string message, SourceCodeNodeCoordinates nodeCoordinates, string sourceFragment) : base(message, nodeCoordinates, sourceFragment) { }
/// <summary> /// Formats a line of source code /// </summary> /// <param name="line">Line content</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="lineNumberSize">Number of symbols in the line number caption</param> /// <param name="fragmentStartPosition">Start position of source fragment</param> /// <param name="fragmentLength">Length of source fragment</param> /// <param name="tabSize">Number of spaces in the tab</param> /// <returns>Formatted line</returns> private static string FormatSourceCodeLine(string line, SourceCodeNodeCoordinates nodeCoordinates, int lineNumberSize, int fragmentStartPosition = 0, int fragmentLength = 0, byte tabSize = 4) { const string ellipsisSymbol = "…"; const byte leftPaddingSize = 7; int lineNumber = nodeCoordinates.LineNumber; int columnNumber = nodeCoordinates.ColumnNumber; int lineLength = line.Length; string processedLine; if ((fragmentStartPosition == 0 && fragmentLength == lineLength)) { processedLine = line; } else if (fragmentStartPosition >= lineLength) { processedLine = string.Empty; } else { int fragmentEndPosition = fragmentStartPosition + fragmentLength - 1; bool beginningCutOff = (fragmentStartPosition > 0); bool endingCutOff = (fragmentEndPosition <= lineLength); if (fragmentEndPosition + 1 == lineLength) { endingCutOff = false; } if (fragmentEndPosition >= lineLength) { endingCutOff = false; fragmentEndPosition = lineLength - 1; fragmentLength = fragmentEndPosition - fragmentStartPosition + 1; } processedLine = line.Substring(fragmentStartPosition, fragmentLength); if (beginningCutOff) { processedLine = ellipsisSymbol + processedLine; } if (endingCutOff) { processedLine = processedLine + ellipsisSymbol; } } string result = string.Format("Line {0}: {1}", lineNumber.ToString(CultureInfo.InvariantCulture).PadLeft(lineNumberSize), processedLine.TabsToSpaces(tabSize)); if (columnNumber > 0) { int cursorOffset = columnNumber - fragmentStartPosition; if (fragmentStartPosition > 0) { cursorOffset++; } result += Environment.NewLine + string.Empty .PadRight(processedLine.Substring(0, cursorOffset - 1) .TabsToSpaces(tabSize).Length + lineNumberSize + leftPaddingSize) .Replace(" ", "-") + "^" ; } return(result); }
/// <summary> /// Initializes a new instance of the WebMarkupMin.Core.Parsers.XmlParsingException class /// with a specified error message, node coordinates, source fragment /// and reference to the inner exception that is the cause of this exception /// </summary> /// <param name="message">Error message that explains the reason for the exception</param> /// <param name="nodeCoordinates">Node coordinates</param> /// <param name="sourceFragment">Source fragment</param> /// <param name="innerException">Exception that is the cause of the current exception</param> public XmlParsingException(string message, SourceCodeNodeCoordinates nodeCoordinates, string sourceFragment, Exception innerException) : base(message, nodeCoordinates, sourceFragment, innerException) { }
/// <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> private void ProcessEmbeddedStyleContent(MarkupParsingContext context, string content, string contentType) { string code; bool isNotEmpty = false; 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 newLine = string.Empty; string beforeCodeContent = string.Empty; if (_beginCdataSectionRegex.IsMatch(content)) { beforeCodeContent = _beginCdataSectionRegex.Match(content).Value; startPart = "<![CDATA["; endPart = "]]>"; newLine = Environment.NewLine; code = Utils.RemovePrefixAndPostfix(content, _beginCdataSectionRegex, _endCdataSectionRegex); } else if (_styleBeginCdataSectionRegex.IsMatch(content)) { beforeCodeContent = _styleBeginCdataSectionRegex.Match(content).Value; if (!removeCdataSections) { startPart = "/*<![CDATA[*/"; endPart = "/*]]>*/"; } code = Utils.RemovePrefixAndPostfix(content, _styleBeginCdataSectionRegex, _styleEndCdataSectionRegex); } else if (_styleBeginMaxCompatibleCdataSectionRegex.IsMatch(content)) { beforeCodeContent = _styleBeginMaxCompatibleCdataSectionRegex.Match(content).Value; if (!removeCdataSections) { startPart = "<!--/*--><![CDATA[/*><!--*/"; endPart = "/*]]>*/-->"; } code = Utils.RemovePrefixAndPostfix(content, _styleBeginMaxCompatibleCdataSectionRegex, _styleEndMaxCompatibleCdataSectionRegex); } else if (_beginHtmlCommentRegex.IsMatch(content)) { beforeCodeContent = _beginHtmlCommentRegex.Match(content).Value; if (!removeHtmlComments) { startPart = "<!--"; endPart = "-->"; } code = Utils.RemovePrefixAndPostfix(content, _beginHtmlCommentRegex, _endHtmlCommentRegex); } 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(); } if (startPart.Length > 0) { _buffer.Add(startPart); if (newLine.Length > 0) { _buffer.Add(newLine); } isNotEmpty = true; } if (code.Length > 0) { _buffer.Add(code); isNotEmpty = true; } if (endPart.Length > 0) { if (newLine.Length > 0) { _buffer.Add(newLine); } _buffer.Add(endPart); isNotEmpty = true; } _currentText = isNotEmpty ? EMBEDDED_CODE_PLACEHOLDER : string.Empty; }