public string Minify(string content, MelezeWebMinifierState state)
        {
            if (state.InsidePre)
            {
                if (string.IsNullOrEmpty(content))
                {
                    return(string.Empty);
                }
            }
            else
            {
                if (string.IsNullOrWhiteSpace(content))
                {
                    return(string.Empty);
                }
            }

            var builder = new StringBuilder(content.Length);

            content = Comments
        ? MinifyComments(content, builder, state)
        : MinifyHtml(content, builder, state);

            return(content);
        }
    public MelezeWebMinifierState AnalyseContent(string content, MelezeWebMinifierState state)
    {
      if (string.IsNullOrWhiteSpace(content))
        return state;
      
      var previousIsWhiteSpace = char.IsWhiteSpace(content[content.Length - 1]);
      var previousTokenEndsWithBlockElement = EndsWithBlockElement(content);

      var insideScript = IsInsideTag("<script", "</script>", state.InsideScript, content);
      var insidePre = IsInsideTag("<pre", "</pre>", state.InsidePre, content);
      var insideComment = IsInsideTag("<!--", "-->", state.InsideComment, content);
      var preserveComment = insideComment && state.PreserveComment;

      return new MelezeWebMinifierState(previousIsWhiteSpace, previousTokenEndsWithBlockElement,
        insidePre, insideScript, insideComment, preserveComment);
    }
        public MelezeWebMinifierState AnalyseContent(string content, MelezeWebMinifierState state)
        {
            if (string.IsNullOrWhiteSpace(content))
            {
                return(state);
            }

            var previousIsWhiteSpace = char.IsWhiteSpace(content[content.Length - 1]);
            var previousTokenEndsWithBlockElement = EndsWithBlockElement(content);

            var insideScript    = IsInsideTag("<script", "</script>", state.InsideScript, content);
            var insidePre       = IsInsideTag("<pre", "</pre>", state.InsidePre, content);
            var insideComment   = IsInsideTag("<!--", "-->", state.InsideComment, content);
            var preserveComment = insideComment && state.PreserveComment;

            return(new MelezeWebMinifierState(previousIsWhiteSpace, previousTokenEndsWithBlockElement,
                                              insidePre, insideScript, insideComment, preserveComment));
        }
        /// <summary>
        /// Removes all the comments that are not Javascript or IE conditional comments.
        /// </summary>
        /// <param name="content"></param>
        /// <param name="builder"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        private string MinifyComments(string content, StringBuilder builder, MelezeWebMinifierState state)
        {
            ProcessItem("<!--", "-->", content, state.InsideComment,
                        insideComment =>
            {
                // There is a comment but it contains javascript or IE conditionals => we keep it
                if (CommentsMarkers.Any(insideComment.Contains))
                {
                    state.PreserveComment = true;
                }

                if (state.PreserveComment)
                {
                    builder.Append(insideComment);
                }
            },
                        outsideComment => MinifyHtml(outsideComment, builder, state));

            return(builder.ToString());
        }
    public string Minify(string content, MelezeWebMinifierState state)
    {
      if (state.InsidePre)
      {
        if (string.IsNullOrEmpty(content))
          return string.Empty;
      }
      else
      {
        if (string.IsNullOrWhiteSpace(content))
          return string.Empty;
      }
      
      var builder = new StringBuilder(content.Length);

      content = Comments 
        ? MinifyComments(content, builder, state) 
        : MinifyHtml(content, builder, state);

      return content;
    }
        /// <summary>
        /// Minify all the white space. Only one space is kept between attributes and words.
        /// Whitespace is completly remove arround HTML block elements while only a single
        /// one is kept arround inline elements.
        /// </summary>
        /// <param name="content"></param>
        /// <param name="builder"></param>
        /// <returns></returns>
        private static string MinifyAggressivelyHtml(string content, StringBuilder builder, MelezeWebMinifierState state)
        {
            bool previousTokenEndsWithBlockElement = state.PreviousTokenEndsWithBlockElement;
            bool previousIsWhiteSpace = state.PreviousIsWhiteSpace;

            ProcessTag("script", content, state.InsideScript,
                       jsContent =>
            {
                previousIsWhiteSpace = MinifySafelyHtmlBlock(jsContent, builder, previousIsWhiteSpace, state.InsidePre);
            },
                       htmlContent => ProcessTag("pre", htmlContent, state.InsidePre,
                                                 preContent =>
            {
                builder.Append(preContent);
            },
                                                 htmlPreContent =>
            {
                var tokens = htmlPreContent.Split(WhiteSpaceSeparators, StringSplitOptions.RemoveEmptyEntries);
                previousTokenEndsWithBlockElement |= (htmlPreContent.Length > 0) && !char.IsWhiteSpace(htmlPreContent[0]);
                previousIsWhiteSpace = false;
                for (int i = 0; i < tokens.Length; i++)
                {
                    var token = tokens[i].Trim();
                    if (string.IsNullOrEmpty(token))
                    {
                        continue;
                    }
                    if (!previousTokenEndsWithBlockElement && !StartsWithBlockElement(token))
                    {
                        // We have to keep a white space between 2 texts or an inline element and a text or between 2 inline elements
                        builder.Append(' ');
                    }

                    builder.Append(token);

                    previousTokenEndsWithBlockElement = EndsWithBlockElement(tokens, i);
                }
                if (!previousTokenEndsWithBlockElement && char.IsWhiteSpace(htmlPreContent[htmlPreContent.Length - 1]))
                {
                    builder.Append(' ');
                    previousIsWhiteSpace = true;
                }
            }));

            content = builder.ToString();
            return(content);
        }
        /// <summary>
        /// Minify white space while keeping the HTML compatible with the given one.
        /// Blanks between tags on the same line are not minified.
        /// Just the line start/end are trimmed (the indentation).
        /// </summary>
        private static string MinifySafelyHtml(string content, StringBuilder builder, MelezeWebMinifierState state)
        {
            MinifySafelyHtmlBlock(content, builder, state.PreviousIsWhiteSpace, state.InsidePre);

            content = builder.ToString();
            return(content);
        }
 private string MinifyHtml(string content, StringBuilder builder, MelezeWebMinifierState state)
 {
     return(Aggressive
 ? MinifyAggressivelyHtml(content, builder, state)
 : MinifySafelyHtml(content, builder, state));
 }
    /// <summary>
    /// Minify all the white space. Only one space is kept between attributes and words.
    /// Whitespace is completly remove arround HTML block elements while only a single
    /// one is kept arround inline elements.
    /// </summary>
    /// <param name="content"></param>
    /// <param name="builder"></param>
    /// <returns></returns>
    private static string MinifyAggressivelyHtml(string content, StringBuilder builder, MelezeWebMinifierState state)
    {
      bool previousTokenEndsWithBlockElement = state.PreviousTokenEndsWithBlockElement;
      bool previousIsWhiteSpace = state.PreviousIsWhiteSpace;

      ProcessTag("script", content, state.InsideScript,
        jsContent =>
        {
          previousIsWhiteSpace = MinifySafelyHtmlBlock(jsContent, builder, previousIsWhiteSpace, state.InsidePre);
        },
        htmlContent => ProcessTag("pre", htmlContent, state.InsidePre,
          preContent =>
          {
            builder.Append(preContent);
          },
          htmlPreContent =>
          {
            var tokens = htmlPreContent.Split(WhiteSpaceSeparators, StringSplitOptions.RemoveEmptyEntries);
            previousTokenEndsWithBlockElement |= (htmlPreContent.Length > 0) && !char.IsWhiteSpace(htmlPreContent[0]);
            previousIsWhiteSpace = false;
            for (int i = 0; i < tokens.Length; i++)
            {
              var token = tokens[i].Trim();
              if (string.IsNullOrEmpty(token))
              {
                continue;
              }
              if (!previousTokenEndsWithBlockElement && !StartsWithBlockElement(token))
              {
                // We have to keep a white space between 2 texts or an inline element and a text or between 2 inline elements
                builder.Append(' ');
              }

              builder.Append(token);

              previousTokenEndsWithBlockElement = EndsWithBlockElement(tokens, i);
            }
            if (!previousTokenEndsWithBlockElement && char.IsWhiteSpace(htmlPreContent[htmlPreContent.Length - 1]))
            {
              builder.Append(' ');
              previousIsWhiteSpace = true;
            }
          }));

      content = builder.ToString();
      return content;
    }
    /// <summary>
    /// Minify white space while keeping the HTML compatible with the given one.
    /// Blanks between tags on the same line are not minified.
    /// Just the line start/end are trimmed (the indentation).
    /// </summary>
    private static string MinifySafelyHtml(string content, StringBuilder builder, MelezeWebMinifierState state)
    {
      MinifySafelyHtmlBlock(content, builder, state.PreviousIsWhiteSpace, state.InsidePre);

      content = builder.ToString();
      return content;
    }
 private string MinifyHtml(string content, StringBuilder builder, MelezeWebMinifierState state)
 {
   return Aggressive
     ? MinifyAggressivelyHtml(content, builder, state)
     : MinifySafelyHtml(content, builder, state);
 }
    /// <summary>
    /// Removes all the comments that are not Javascript or IE conditional comments.
    /// </summary>
    /// <param name="content"></param>
    /// <param name="builder"></param>
    /// <param name="state"></param>
    /// <returns></returns>
    private string MinifyComments(string content, StringBuilder builder, MelezeWebMinifierState state)
    {
      ProcessItem("<!--", "-->", content, state.InsideComment,
        insideComment =>
        {
          // There is a comment but it contains javascript or IE conditionals => we keep it
          if (CommentsMarkers.Any(insideComment.Contains))
            state.PreserveComment = true;

          if (state.PreserveComment)
            builder.Append(insideComment);
        },
        outsideComment => MinifyHtml(outsideComment, builder, state));

      return builder.ToString();
    }