/// <summary>
        /// �������������� ������.
        /// <b>������������������!</b>
        /// </summary>
        /// <param name="txt">�������� �����.</param>
        /// <param name="smile">������� ��������� ���������.</param>
        /// <param name="doNotReplaceTags">�� �������� ��������� ������� HTML.</param>
        /// <param name="doNotFormatImplicitLinks">�� ������������� ���� �� ��������� ������.</param>
        /// <returns>���������������� �����.</returns>
        public virtual string Format(
            string txt,
            bool smile,
            bool doNotReplaceTags,
            bool doNotFormatImplicitLinks)
        {
            var sb = new StringBuilder(txt);

            sb.Trim(TrimArray);

            if (sb.IsEmpty())
                return "";

            // ��������! ������� �������������� �����.
            //

            // ������  ������������ ��������
            if (!doNotReplaceTags)
                sb = sb.ReplaceTagsWQ();

            // ���������� ���� ����� ������ ����� � \n
            //
            sb = _rxNewLineUnifier.Replace(sb, "\n");

            // ��������� �������� ����� � �����,
            // ������� �� ����� ���� ������ ����������.
            //

            // temporary remove [code...] tags
            const string codeExpression = "$$code{0}$$";
            var codeMatcher = new Matcher(codeExpression);
            sb = _rxCodeFormatting.Replace(sb, codeMatcher.Match);

            // temporary remove [img] tags
            const string imgExpression = "$$img{0}$$";
            var imgMatcher = new Matcher(imgExpression);
            sb = _imgTagRegex.Replace(sb, imgMatcher.Match);

            // temporary remove [url] & [purl] tags
            const string urlExpression = "$$url{0}$$";
            var urlMatcher = new Matcher(urlExpression);
            sb = _urlTagRegex.Replace(sb, urlMatcher.Match);

            // temporary remove implicit links
            const string implicitUrlExpression = "$$iurl{0}$$";
            var implicitUrlMatcher = new Matcher(implicitUrlExpression);
            if (!doNotFormatImplicitLinks)
                sb = _urlRegex.Replace(sb, implicitUrlMatcher.Match);

            // temporary remove [q] tags
            const string quoteExpression = "$$quote{0}$$";
            var quoteMatcher = new Matcher(quoteExpression);
            sb = _rxPrep12.Replace(sb, quoteMatcher.Match);

            // temporary remove [cut] tags
            const string cutExpression = "$$cut{0}$$";
            Matcher cutMatcher;
            do
            {
                cutMatcher = new Matcher(cutExpression);
                sb = _rxPrep13.Replace(sb, cutMatcher.Match);

                // �����������.
                sb = _rxTextUrl09.Replace(sb, "<span class='lineQuote'>$&</span>");

                // restore & transform [cut] tags
                for (var i = 0; i < cutMatcher.Count; i++)
                {
                    var m = cutMatcher[i];
                    var capt = String.IsNullOrEmpty(m.Groups[3].Value) ? "������� �����" : m.Groups[3].Value;
                    sb = sb.Replace(String.Format(cutExpression, i),
                        _hiddenTextSnippet.Replace("%CAPT%", capt).Replace("%TEXT%", m.Groups[4].Value).
                        Replace("%URL%", GetImagePrefix()));
                }
            } while (cutMatcher.Count > 0);

            // restore & transform [q] tags
            // ����������� [q].
            // http://www.rsdn.ru/forum/?mid=111506
            for (var i = 0; i < quoteMatcher.Count; i++)
                sb =
                    sb.Replace(
                        string.Format(quoteExpression, i),
                        string.Format(
                            "<blockquote class='q'><p>{0}</p></blockquote>",
                            quoteMatcher[i].Groups[1]));

            // ��������� ��������� � ������ ������ � http://www.rsdn.ru/forum/?mid=184751
            if (smile)
            {
                var prefix = GetImagePrefix();
                sb = _smileReplacers.Aggregate(
                    sb,
                    (current, replacer) => replacer.Replace(current, prefix));
            }

            // ISBN
            sb =
                _isbnDetector.Replace(
                    sb,
                    match =>
                    {
                        var isbn = new StringBuilder(match.Length);
                        foreach (Capture capture in match.Groups["isbn"].Captures)
                        {
                            isbn.Append(capture.Value).Append('-');
                        }
                        if (isbn.Length > 0)
                            isbn.Length--;
                        return ProcessISBN(match, isbn.ToString());
                    });

            // restore & transform [url] and [purl] tags
            for (var i = 0; i < urlMatcher.Count; i++)
            {
                var url = urlMatcher[i].Groups["url"].Value;
                var tag = urlMatcher[i].Groups["tag"].Value;

                // ���� url � tag ����������:
                //
                if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
                    // ���� tag �� ������
                    //
                    if (!String.IsNullOrEmpty(tag))
                        // ���� tag ���������� Uri
                        //
                        if (Uri.IsWellFormedUriString(tag, UriKind.RelativeOrAbsolute))
                        {
                            //
                            //
                            var temp = tag;
                            tag = url;
                            url = temp;
                        }

                sb = sb.Replace(
                    string.Format(urlExpression, i),
                    ProcessURLs(url, tag));
            }

            // restore & transform implicit links
            for (var i = 0; i < implicitUrlMatcher.Count; i++)
                sb = sb.Replace(
                    string.Format(implicitUrlExpression, i),
                    ProcessImplicitURLs(implicitUrlMatcher[i]));

            // restore & transform [img] tags
            for (var i = 0; i < imgMatcher.Count; i++)
                sb = sb.Replace(
                    string.Format(imgExpression, i),
                    ImagesDelegate(this, imgMatcher[i]));

            // RSDN links
            sb = _rsdnLinkDetector.Replace(sb, ProcessRsdnLink);

            // [email]
            sb = _emailTagRegex.Replace(sb, ProcessEmailLink);

            // Replace hyphen to dash
            sb = _dashDetector.Replace(sb, "&mdash;");

            // [tagline]
            sb = _taglineDetector.Replace(sb, "<div class='tagline'>$1</div>");

            // [list]
            sb = _rxPrep06.Replace(sb, @"<ul style='margin-top:0; margin-bottom:0;'>$1</ul>");

            // [list=..]
            sb = _rxPrep07.Replace(sb, ListEvaluator);

            // [*]
            sb = _rxPrep08.Replace(sb, "<li />");

            // [hr]
            sb = _rxPrep09.Replace(sb, "<hr />");

            // Q12345(6)
            sb = _rxPrep10.Replace(
                sb,
                @"<a target='_blank' class='m' href='http://support.microsoft.com/default.aspx?scid=kb;EN-US;$1'>$1</a>");

            // ��������� ����������.
            sb = _moderatorDetector.Replace(sb, "<div class='mod'>$1</div>");

            // Table
            sb = _rxTable.Replace(
                sb,
                "<table class='formatter' border='0' cellspacing='2' cellpadding='5'>$1</table>");
            sb = _rxTableRow.Replace(sb, "<tr class='formatter'>$1</tr>");
            sb = _rxTableHeader.Replace(sb, "<th class='formatter'>$1</th>");
            sb = _rxTableColumn.Replace(sb, "<td class='formatter'>$1</td>");

            // Headers
            sb = _rxHeaders.Replace(sb, "<h$2 class='formatter'>$1</h$2>");

            // ��������� � ����� ������ ������ <br />,
            // �� �� ����� </table>, </div>, </ol>, </ul>, <blockquote> (�������� ������ <span>)
            // � �� � ����� ����� ������
            sb = _rxNewLines.Replace(sb, "<br />$0");

            sb = _inlineTagReplacers.Aggregate(sb, (cur, replacer) => replacer(cur));

            // ������ �� MSDN.
            sb = _rxMsdn.Replace(sb, DoMSDNRef);

            // ����� ��� ������ ����� � ������ ���������.
            sb = _rxPrep01.Replace(sb, "");
            sb = _rxPrep02.Replace(sb, "");
            sb = _rxPrep03.Replace(sb, "");

            // restore & transform [code] tags
            for (var i = 0; i < codeMatcher.Count; i++)
            {
                // code coloring
                var code = PaintCode(codeMatcher[i]);

                // bold & italic formatting inside code
                // without checking canceling tag syntax
                code = _inlineTagReplacersNoChecks.Aggregate(code, (cur, replacer) => replacer(cur));

                sb = sb.Replace(string.Format(codeExpression, i), code.ToString());
            }

            return sb.ToString();
        }
Esempio n. 2
0
		/// <summary>
		/// Форматирование текста.
		/// <b>НЕПОТОКОБЕЗОПАСНЫЙ!</b>
		/// </summary>
		/// <param name="txt">Исходный текст.</param>
		/// <param name="smile">Признак обработки смайликов.</param>
		/// <param name="doNotReplaceTags">Не заменять служебные символы HTML.</param>
		/// <param name="doNotFormatImplicitLinks">Не форматировать явно не указанные ссылки.</param>
		/// <returns>Сформатированный текст.</returns>
		public virtual string Format(
			string txt,
			bool smile,
			bool doNotReplaceTags,
			bool doNotFormatImplicitLinks)
		{
			var sb = new StringBuilder(txt);

			sb.Trim(TrimArray);

			if (sb.IsEmpty())
				return "";

			// Внимание! Порядок преобразования ВАЖЕН.
			//

			// Замена  небезопасных символов
			if (!doNotReplaceTags)
				sb = sb.ReplaceTagsWQ();

			// Приведение всех типов концов строк к \n
			//
			sb = _rxNewLineUnifier.Replace(sb, "\n");

			// Обработка исходных кодов и тегов, 
			// которые не могут быть внутри исходников.
			//

			// temporary remove [code...] tags
			const string codeExpression = "$$code{0}$$";
			var codeMatcher = new Matcher(codeExpression);
			sb = _rxCodeFormatting.Replace(sb, codeMatcher.Match);

			// temporary remove [img] tags
			const string imgExpression = "$$img{0}$$";
			var imgMatcher = new Matcher(imgExpression);
			sb = _imgTagRegex.Replace(sb, imgMatcher.Match);

			// temporary remove [url] & [purl] tags
			const string urlExpression = "$$url{0}$$";
			var urlMatcher = new Matcher(urlExpression);
			sb = _urlTagRegex.Replace(sb, urlMatcher.Match);

			// temporary remove implicit links
			const string implicitUrlExpression = "$$iurl{0}$$";
			var implicitUrlMatcher = new Matcher(implicitUrlExpression);
			if (!doNotFormatImplicitLinks)
				sb = _urlRegex.Replace(sb, implicitUrlMatcher.Match);

			// temporary remove [q] tags
			const string quoteExpression = "$$quote{0}$$";
			var quoteMatcher = new Matcher(quoteExpression);
			sb = _rxPrep12.Replace(sb, quoteMatcher.Match);

			// temporary remove [cut] tags
			const string cutExpression = "$$cut{0}$$";
			Matcher cutMatcher;
			do
			{
				cutMatcher = new Matcher(cutExpression);
				sb = _rxPrep13.Replace(sb, cutMatcher.Match);

				// Цитирование.
				sb = _rxTextUrl09.Replace(sb,
					m => $"<span class='lineQuote level{WebUtility.HtmlDecode(m.Groups["lev"].Value).Length}'>{m.Groups[0].Value}</span>");

				// restore & transform [cut] tags
				for (var i = 0; i < cutMatcher.Count; i++)
				{
					var m = cutMatcher[i];
					var capt = String.IsNullOrEmpty(m.Groups[3].Value) ? "Скрытый текст" : m.Groups[3].Value;
					sb = sb.Replace(String.Format(cutExpression, i),
						_hiddenTextSnippet.Replace("%CAPT%", capt).Replace("%TEXT%", m.Groups[4].Value).
						Replace("%URL%", GetImagePrefix()));
				}
			} while (cutMatcher.Count > 0);

			// restore & transform [q] tags
			// Цитирование [q].
			// http://www.rsdn.ru/forum/?mid=111506
			for (var i = 0; i < quoteMatcher.Count; i++)
				sb =
					sb.Replace(
						string.Format(quoteExpression, i),
						$"<blockquote class='q'><p>{quoteMatcher[i].Groups[1]}</p></blockquote>");

			// Обработка смайликов с учетом отмены и http://www.rsdn.ru/forum/?mid=184751
			if (smile)
			{
				var prefix = GetImagePrefix();
				sb = _smileReplacers.Aggregate(
					sb,
					(current, replacer) => replacer.Replace(current, prefix));
			}

			// ISBN
			sb = 
				_isbnDetector.Replace(
					sb,
					match =>
					{
						var isbn = new StringBuilder(match.Length);
						foreach (Capture capture in match.Groups["isbn"].Captures)
						{
							isbn.Append(capture.Value).Append('-');
						}
						if (isbn.Length > 0)
							isbn.Length--;
						return ProcessISBN(match, isbn.ToString());
					});

			// restore & transform [url] and [purl] tags
			for (var i = 0; i < urlMatcher.Count; i++)
			{
				var url =
					urlMatcher[i]
						.Groups["url"]
						.Value;
				var tag = urlMatcher[i].Groups["tag"].Value;

				// если url и tag перепутаны:
				//
				if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
					// если tag не пустой
					//
					if (!String.IsNullOrEmpty(tag))
						// если tag правильный Uri
						//
						if (Uri.IsWellFormedUriString(tag, UriKind.RelativeOrAbsolute))
						{
							// 
							//
							var temp = tag;
							tag = url;
							url = temp;
						}

				url = url.Replace("&amp;", "&"); // Returns escaped ampersands

				sb = sb.Replace(
					string.Format(urlExpression, i),
					ProcessURLs(url, tag));
			}

			// restore & transform implicit links
			for (var i = 0; i < implicitUrlMatcher.Count; i++)
				sb = sb.Replace(
					string.Format(implicitUrlExpression, i),
					ProcessImplicitURLs(implicitUrlMatcher[i]));

			// restore & transform [img] tags
			for (var i = 0; i < imgMatcher.Count; i++)
				sb = sb.Replace(
					string.Format(imgExpression, i),
					ImagesDelegate(this, imgMatcher[i]));

			// RSDN links
			sb = _rsdnLinkDetector.Replace(sb, ProcessRsdnLink);

			// [email]
			sb = _emailTagRegex.Replace(sb, ProcessEmailLink);

			// Replace hyphen to dash
			sb = _dashDetector.Replace(sb, "&mdash;");

			// [tagline]
			sb = _taglineDetector.Replace(sb, "<div class='tagline'>$1</div>");

			// [list]
			sb = _rxPrep06.Replace(sb, @"<ul style='margin-top:0; margin-bottom:0;'>$1</ul>");

			// [list=..]
			sb = _rxPrep07.Replace(sb, ListEvaluator);

			// [*]
			sb = _rxPrep08.Replace(sb, "<li />");

			// [hr]
			sb = _rxPrep09.Replace(sb, "<hr />");

			// Q12345(6)
			sb = _rxPrep10.Replace(
				sb,
				@"<a target='_blank' class='m' href='http://support.microsoft.com/default.aspx?scid=kb;EN-US;$1'>$1</a>");

			// Сообщение модератора.
			sb = _moderatorDetector.Replace(sb, "<div class='mod'>$1</div>");

			// Table
			sb = _rxTable.Replace(
				sb,
				"<table class='formatter' border='0' cellspacing='2' cellpadding='5'>$1</table>");
			sb = _rxTableRow.Replace(sb, "<tr class='formatter'>$1</tr>");
			sb = _rxTableHeader.Replace(sb, "<th class='formatter'>$1</th>");
			sb = _rxTableColumn.Replace(sb, "<td class='formatter'>$1</td>");

			// Headers
			sb = HeadersRegex.Replace(sb, "<h$2 class='formatter'>$1</h$2>");

			// Добавляем в конец каждой строки <br />,
			// но не после </table>, </div>, </ol>, </ul>, <blockquote> (возможно внутри <span>)
			// и не в самом конце текста
			sb = _rxNewLines.Replace(sb, "<br />$0");

			sb = _inlineTagReplacers.Aggregate(sb, (cur, replacer) => replacer(cur));

			// Ссылки на MSDN.
			sb = _rxMsdn.Replace(sb, DoMSDNRef);

			// Нужно для отмены тэгов и отмены смайликов.
			sb = _rxPrep01.Replace(sb, "");
			sb = _rxPrep02.Replace(sb, "");
			sb = _rxPrep03.Replace(sb, "");

			// restore & transform [code] tags
			for (var i = 0; i < codeMatcher.Count; i++)
			{
				// code coloring
				var code = PaintCode(codeMatcher[i]);

				// bold & italic formatting inside code
				// without checking canceling tag syntax
				code = _inlineTagReplacersNoChecks.Aggregate(code, (cur, replacer) => replacer(cur));

				sb = sb.Replace(string.Format(codeExpression, i), code.ToString());
			}

			return sb.ToString();
		}