All pastes #252277 Raw Edit

Kiryn

public text v1 · immutable
#252277 ·published 2006-11-19 08:33 UTC
rendered paste body
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace Frost.Controls
{
	/// <summary>
	/// Summary description for XHTMLPage.
	/// </summary>
	public class XHTMLPage : Page
	{
		#region Private Variables
		/// <summary>
		/// XHTML Code.
		/// </summary>
		private string _xhtml;
		/// <summary>
		/// XHTML Format To Use.
		/// </summary>
		private XHtmlFormat _xhtmlFormat;
		/// <summary>
		/// XHTML Encoding.
		/// </summary>
		private Encoding _encoding;
		/// <summary>
		/// Encoding Language
		/// </summary>
		private string _language;
		/// <summary>
		/// XML CDATA For Use By Bool.
		/// </summary>
		private bool _xmlCDATA;
		#endregion
		#region Constructor
		/// <summary>
		/// Default Constructor.
		/// </summary>
		public XHTMLPage( )
		{
			_xhtml = "";
			_xhtmlFormat = XHtmlFormat.XHtml10_Strict;
			_encoding = Encoding.UTF8;
			_language = "en";
			_xmlCDATA = false;
		}
		#endregion
		#region Properties
		/// <summary>
		/// Input of which XHTMLFormat to use out of Strict, Transitional, Or Frameset.
		/// </summary>
		public XHtmlFormat XHTMLFormat
		{
			get { return _xhtmlFormat; }
			set { _xhtmlFormat = value; }
		}
		/// <summary>
		/// Input of the page encoding type.
		/// </summary>
		public Encoding Encoding
		{
			get { return _encoding; }
			set { _encoding = value; }
		}
		/// <summary>
		/// Input of the user language.
		/// </summary>
		public string Language
		{
			get { return _language; }
			set { _language = value; }
		}
		/// <summary>
		/// Should we use CDATA of XML?
		/// </summary>
		public bool XmlCDATA
		{
			get { return _xmlCDATA; }
			set { _xmlCDATA = value; }
		}
		#endregion
		#region Overriden Methods
		/// <summary>
		/// Render XHTML to the page.
		/// </summary>
		/// <param name="output"></param>
		protected override void Render( HtmlTextWriter output )
		{
			StringWriter w;
			w = new StringWriter( );

			HtmlTextWriter myoutput = new HtmlTextWriter( w );
			base.Render( myoutput );

			myoutput.Close( );

			_xhtml = w.GetStringBuilder( ).ToString( );
			ReplaceDocType( );
			

			switch ( _xhtmlFormat )
			{
				case XHtmlFormat.XHtml10_Strict:
					ConvertToXHTMLStrict( );
					break;

				case XHtmlFormat.XHtml10_Transitional:
					ConvertToXHTMLTransitional( );
					break;

				case XHtmlFormat.XHtml10_Frameset:
					ConvertToXHTMLFrameset( );
					break;
			}
			output.Write( _xhtml );
		}
		#endregion
		#region Private Methods
		/// <summary>
		/// Self close the XHTML tag.
		/// </summary>
		/// <example>
		/// <hr> => <hr />
		/// </example>
		/// <param name="sTagName">XHTML Tag</param>
		private void AddSelfClose( string sTagName )
		{
			int nStart = 0;
			int nPos = 0;

			while ( nPos >= 0 )
			{
				string sSearch = "<" + sTagName;
				nPos = _xhtml.IndexOf( sSearch, nStart );
				if ( nPos > 0 )
				{
					nStart = nPos + 1;
					int nEnd = _xhtml.IndexOf( ">", nStart );
					if ( nEnd > 0 )
					{
						if ( _xhtml[ nEnd - 1 ] != '/' )
						{
							_xhtml = _xhtml.Insert( nEnd, " /" );
						}
					}
				}
			}
		}
		/// <summary>
		/// Convert text to lowercase
		/// </summary>
		/// <example>
		/// XHTML =&gt; xhtml
		/// </example>
		private void ConvertToLowerCase( )
		{
			_xhtml = Regex.Replace( _xhtml, "<(/?)([a-zA-Z0-9]+)[ ]*(.*?)>", new MatchEvaluator( SingleTagToLowerCase ), RegexOptions.IgnoreCase );
			_xhtml = Regex.Replace( _xhtml, "<([a-zA-Z0-9]+)(\\s+[a-zA-Z]+)(=\".+?>)", new MatchEvaluator( PropertiesToLowerCase ), RegexOptions.IgnoreCase );
		}
		/// <summary>
		/// Convert the page to XHTML 1.0 Frameset Compliance.
		/// </summary>
		private void ConvertToXHTMLFrameset( )
		{
			ConvertToLowerCase( );
			AddSelfClose( "meta" );
			AddSelfClose( "link" );
			AddSelfClose( "img" );
			AddSelfClose( "hr" );

			//FixScript( );
			FixBr( );
			//FixStyle( );
			FixHtml( );
			maskScript( );
		}
		/// <summary>
		/// Convert the page to XHTML 1.0 Strict Compliance.
		/// </summary>
		private void ConvertToXHTMLStrict( )
		{
			ConvertToLowerCase( );
			AddSelfClose( "meta" );
			AddSelfClose( "link" );
			AddSelfClose( "img" );
			AddSelfClose( "hr" );

			//FixScript( );
			RemoveAttribute( "form", "name" );
			FixInput( );
			FixBr( );
			//FixStyle( );
			FixHtml( );
			maskScript( );
		}
		/// <summary>
		/// Convert the page to XHTML 1.0 Transitional Compliance.
		/// </summary>
		private void ConvertToXHTMLTransitional( )
		{
			ConvertToLowerCase( );
			AddSelfClose( "meta" );
			AddSelfClose( "link" );
			AddSelfClose( "img" );
			AddSelfClose( "hr" );

			//FixScript( );
			FixBr( );
			//FixStyle( );
			FixHtml( );
		}
		/// <summary>
		/// Turns the HTML 4.01 <br>.
		/// </summary>
		/// <example>
		/// <br> => <br />
		/// </example>
		private void FixBr( )
		{
			_xhtml = _xhtml.Replace( "<br>", "<br />" );
		}
		/// <summary>
		/// Fix the HTML tag.
		/// </summary>
		private void FixHtml( )
		{
			_xhtml = Regex.Replace( _xhtml, "<html>", new MatchEvaluator( HTMLTag ), RegexOptions.IgnoreCase );
		}
		/// <summary>
		/// Fixes the Input
		/// </summary>
		private void FixInput( )
		{
			int nStart = 0;
			int nPos = 0;

			while ( nPos >= 0 )
			{
				string sSearch = "<input type=\"hidden\"";
				nPos = _xhtml.IndexOf( sSearch, nStart );
				if ( nPos > 0 )
				{
					nStart = nPos + sSearch.Length;
					_xhtml = _xhtml.Insert( nPos, "<pre>" );

					int nEnd = _xhtml.IndexOf( ">", nStart );
					if ( nEnd > 0 )
					{
						_xhtml = _xhtml.Insert( nEnd + 1, "</pre>" );
					}
				}
			}
		}
		/// <summary>
		/// 
		/// </summary>
		private void FixScript( )
		{
			_xhtml = _xhtml.Replace( "<script language=\"javascript\">", "<script type=\"text/javascript\">" );
		}
		/// <summary>
		/// Fixes the Style Tag.
		/// </summary>
		/// <example>
		/// <!--
		/// body, html
		/// {
		///		color: white;
		/// }
		/// -->
		/// </example>
		private void FixStyle( )
		{
			_xhtml = Regex.Replace( _xhtml, "style=\".+\"", new MatchEvaluator( ToLowerCase ), RegexOptions.IgnoreCase );
			_xhtml = Regex.Replace( _xhtml, @"(<style[^<]*>){1}(?(?=\s*<!--)(\s*<!--)(\s*.*?)(//\s*-->)|(\s*.*?))\s*(</style>){1}", new MatchEvaluator( FixStyleAndScript ), RegexOptions.IgnoreCase | RegexOptions.Singleline );
		}

		/// <summary>
		/// Fixes the style and script based on match.
		/// </summary>
		/// <param name="m">Expression to match.</param>
		/// <returns></returns>
		private string FixStyleAndScript( Match m )
		{
			string ret = "";
			string st, ed;

			if ( _xmlCDATA )
			{
				st = "\r\n<![CDATA[\r\n";
				ed = "\r\n]]>\r\n";
			}
			else
			{
				st = "\r\n<!--\r\n";
				ed = "\r\n//-->\r\n";
			}

			if ( m.Groups[ 2 ].ToString( ).Trim( ) == "" && m.Groups[ 4 ].ToString( ).Trim( ) == "" )
				st = ed = "";

			ret = m.Groups[ 1 ].ToString( ) + st;
			ret += m.Groups[ 2 ].ToString( ) + m.Groups[ 4 ].ToString( ) + ed + m.Groups[ 5 ].ToString( );
			return ret;
		}
		/// <summary>
		/// Fixes the HTML tag to include the XMLNS and the XML:LANG
		/// </summary>
		/// <param name="m">Expression To Match.</param>
		/// <returns>
		/// Returns the correct XHTML Tag.
		/// </returns>
		private string HTMLTag( Match m )
		{
			return "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"" + _language + "\">";
		}

		/// <summary>
		/// Fixes the script tag to hide from older browsers.
		/// </summary>
		/// <example>
		/// <script type="text/javascript">
		/// <!--
		/// document.write("Example");
		/// -->
		/// </script>
		/// </example>
		private void maskScript( )
		{
			_xhtml = Regex.Replace( _xhtml, @"(<script[^<]*>){1}(?(?=\s*<!--)(\s*<!--)(\s*.*?)(//\s*-->)|(\s*.*?))\s*(</script>){1}", new MatchEvaluator( FixStyleAndScript ), RegexOptions.IgnoreCase | RegexOptions.Singleline );
		}
		/// <summary>
		/// Recursively search for property in tag
		/// </summary>
		/// <param name="m">Match of the regular expression</param>
		/// <returns>tag with lower case properties</returns>
		private string nextProperty( Match m )
		{
			string szReplace = "";
			szReplace = m.Groups[ 1 ].ToString( ) + m.Groups[ 2 ].ToString( ).ToLower( );

			// Search another property in tag
			// Ignore if tag contains __VIEWSTATE... prevent long time calculation.
			if ( Regex.Match( m.Groups[ 3 ].ToString( ), "(.*?\")(\\s+\\w+)(=\".+>)",
				RegexOptions.IgnoreCase ).Success && m.Groups[ 3 ].ToString( ).IndexOf( "__VIEWSTATE" ) == -1 )
			{
				System.Diagnostics.Debug.WriteLine( "Match OK", "nextProperty" );
				szReplace += Regex.Replace( m.Groups[ 3 ].ToString( ),
					"(.*?\")(\\s+\\w+)(=\".+>)", new MatchEvaluator( nextProperty ),
					RegexOptions.IgnoreCase );
			}
			else
			{
				System.Diagnostics.Debug.WriteLine( "Match NOK", "nextProperty" );
				szReplace += m.Groups[ 3 ].ToString( );
			}
			return szReplace;
		}
		/// <summary>
		/// Make the properties of the tag.
		/// </summary>
		/// <param name="m">Expression to match.</param>
		/// <returns>
		/// Returns the properties to lowercase.
		/// </returns>
		private string PropertiesToLowerCase( Match m )
		{
			string szReplace = "";
			szReplace = "<" + m.Groups[ 1 ].ToString( ) + m.Groups[ 2 ].ToString( ).ToLower( );

			if ( Regex.Match( m.Groups[ 3 ].ToString( ), "(.*?\")(\\s+\\w+)(=\".+>)", RegexOptions.IgnoreCase ).Success )
			{
				szReplace += Regex.Replace( m.Groups[ 3 ].ToString( ),
					"(.*?\")(\\s+\\w+)(=\".+>)", new MatchEvaluator( nextProperty ), RegexOptions.IgnoreCase );
			}
			else
			{
				szReplace += m.Groups[ 3 ].ToString( );
			}

			return szReplace;
		}
		/// <summary>
		/// Removes the specific tag.
		/// </summary>
		/// <param name="sTagName">Tag name for attribute to be removed from.</param>
		/// <param name="sAttrName">Attribute to remove.</param>
		private void RemoveAttribute( string sTagName, string sAttrName )
		{
			int nStart = 0;
			int nPos = 0;

			while ( nPos >= 0 )
			{
				string sSearch = "<" + sTagName + " " + sAttrName + "=\"";
				nPos = _xhtml.IndexOf( sSearch, nStart );
				if ( nPos > 0 )
				{
					nStart = nPos + sTagName.Length + 1;

					int nEnd = _xhtml.IndexOf( "\"", nPos + sSearch.Length + 1 );
					if ( nEnd > 0 )
					{
						_xhtml = _xhtml.Remove( nStart, nEnd - nStart + 1 );
					}
				}
			}
		}

		private void ReplaceTitle()
		{
			int nStart = _xhtml.IndexOf( "<title", 0 );
			if(nStart > 0)
			{
				int nEnd = _xhtml.IndexOf( ">", nStart + 1 );
				if(nEnd > 0)
				{
					_xhtml = _xhtml.Remove(nStart, nEnd - nStart + 1);
					_xhtml = "\n\t<title> Frost Designz </title>";
				}
			}
		}
		/// <summary>
		/// Removes the default DOCTYPE and replacee. And uses the correct doctype.
		/// </summary>
		private void ReplaceDocType( )
		{
			int nStart = _xhtml.IndexOf( "<!DOCTYPE", 0 );
			if ( nStart > 0 )
			{
				int nEnd = _xhtml.IndexOf( ">", nStart + 1 );
				if ( nEnd > 0 )
				{
					_xhtml = _xhtml.Remove( nStart, nEnd - nStart + 1 );

					switch ( _xhtmlFormat )
					{
						case XHtmlFormat.XHtml10_Strict:
							_xhtml = _xhtml.Insert( 0, "<?xml encoding=\"utf-8\" version=\"1.0\" ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" );
							break;

						case XHtmlFormat.XHtml10_Transitional:
							_xhtml = _xhtml.Insert( 0, "<?xml encoding=\"utf-8\" version=\"1.0\" ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" );
							break;

						case XHtmlFormat.XHtml10_Frameset:
							_xhtml = _xhtml.Insert( 0, "<?xml encoding=\"utf-8\" version=\"1.0\" ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">" );
							break;
					}
				}
			}
		}
		/// <summary>
		/// Single tag to be lowercase.
		/// </summary>
		/// <param name="m">Expression to be match.</param>
		/// <returns></returns>
		private string SingleTagToLowerCase( Match m )
		{
			if ( m.Groups[ 3 ].ToString( ).Trim( ) == String.Empty )
				return "<" + m.Groups[ 1 ].ToString( ).ToLower( ) + m.Groups[ 2 ].ToString( ).ToLower( ) + ">";
			else
				return "<" + m.Groups[ 1 ].ToString( ).ToLower( ) + m.Groups[ 2 ].ToString( ).ToLower( ) + " " + m.Groups[ 3 ].ToString( ) + ">";
		}
		/// <summary>
		/// Lowercase the match.
		/// </summary>
		/// <param name="m">Expression to be match.</param>
		/// <returns>
		/// Returns the lowercase match.
		/// </returns>
		private string ToLowerCase( Match m )
		{
			return m.ToString( ).ToLower( );
		}
		#endregion
	}
}