/*
  Gemaakt door                            : Wim te Groen  - tegroen[at]home.nl  - http://xlerator.beefhole.nl
  Mede mogelijk gemaakt door de hulp van  : crisp         - freesoftware[at]xs4all.nl
*/
var props = new Array("width", "height", "valign", "align", "bgcolor", "border", "face", "color", "background", "bgproperties", "topmargin", "leftmargin", "marginheight", "marginwidth", "fontsize");
var styleprops = new Array("width", "height", "vertical-align", "text-align", "background-color", "border-width", "font-family", "color", "background-color", "background-attachment", "padding-top", "padding-left", "padding-bottom", "padding-right", "font-size");
var inlineTags = new Array("a", "title", "b", "i", "u", "em", "iframe");
var indentLevel = 0;
// Om te checken of de tag die in een inline Tag staat (in een a ofzo)
var prevTag = "";

function indexOfArray(arr, value, exact)
{
  var res = 0;
  for (var x = 0; x <= arr.length - 1; x++)
  {
    if ((arr[x] == value && exact) || (value.indexOf(arr[x]) >= 0 && !exact)) return x;
  }
  return -1;
}

function toStyle(alles, property, content)
{
  property = property.toLowerCase();
  content = content.toLowerCase();
  if (indexOfArray(props, property, true) >= 0)
    return "style=\"" + styleprops[indexOfArray(props, property, false)] + ":" + content + ";\" ";
  else return alles;
}
function fixQuotes(alles, property, content)
{
  // Spaties achteraan weghalen.
  var x = 0;
  while (content.charAt(content.length - 1 - x) == " ") x++;
  content = content.substr(0, content.length - x);
  // Einde spaties weghalen.
  if (property.indexOf("style") < 0) { return property + "=\"" + content + "\" "; } else return alles;
}

function toSpaces(x)
{
  var res = "";
  for (var fX = 1; fX <= x; fX++) res += " ";
  return res;
}

function setPx(alles, property, value, tag)
{
  if (property == "size")
  {
    if (tag.match(/<input/i)) return alles;
    else if (tag.match(/<span/i)) 
    { // FONT tag
      switch (value) 
      {
        case '0': { value = '0px'; break; }
        case '1': { value = 'xx-small'; break; }
        case '2': { value = 'x-small'; break; }
        case '3': { value = 'small'; break; }
        case '4': { value = 'medium'; break; }
        case '5': { value = 'large'; break; }
        case '6': { value = 'x-large'; break; }
        case '7': { value = 'xx-large'; break; }
        default:  { value = 'xx-large'; break; }
      }
      return "fontsize=\"" + value + "\"";
    }
  }
  return property + "=\"" + value + "px\"";
}

function restyle(str) // str = hele tag.
{
  if (str.toLowerCase().indexOf("!doctype") >= 0) return str;
  var str = str.replace(/__enter__/gi, "");
  // Begin tag eraf halen.
  var beginTag = str.match(/(\s*<[a-z|0-9]+)/gi)[0].toLowerCase();
  if (beginTag == "<font") beginTag = "<span";
  str = str.replace(/(<\w*)\s/i, "");
  // En het einde ook.
  var endTag = str.match(/(\/?>)/gi)[0].toLowerCase();
  str = str.replace(/(\/?>)/i, "");
  // Quootjes naar " omzetten.
  str = str.replace(/(\w+=)\'([^\']+)\'/gi, function($1, $2, $3) { return $2 + "\"" + $3.replace(/\"/gi, "'")  + "\""; } );
  // Quootjes toevoegen, behalve bij styles.
  str = str.replace(/(\w+)=([^\'\"][#\w%]*[^\'\"])/gi, function($1, $2, $3) { return fixQuotes($1, $2, $3); });
  // PX erachter als er nix staat behalve bij rowspan/colspan.
  str = str.replace(/(\w+[^colspan|rowspan])=[\'\"](\d+)[\'\"]/gi, function($1, $2, $3) { return setPx($1, $2, $3, beginTag); });
  // Quootjes uit de content halen.
  str = str.replace(/style=\"(.*?)\"/gi, function($1, $2) { return "style=\"" + $2.replace(/[\'\"]/gi, "") + "\""; });
  // Omzetten naar losse style="" stukjes.
  str = str.replace(/(\w+)=[\'\"]([#\d\w\s-]+[%]?)[\'\"]/gi, function($1, $2, $3) { return toStyle($1, $2, $3); });
  var tmpStyle = "";
  // Zoeken naar style=""
  tmpStyle += str.match(/style=([\"\'][^\'\"]*[\"\'])/gi);
  // style="bla:boe" omzetten naar "bla:boe" En als er een ; mist, die er effe bij zetten.
  tmpStyle = tmpStyle.replace(/style=[\"\']([^\'\"]*)[\"\']/gi, function($1, $2) { if ($2.charAt($2.length - 1) != ";") $2 += ";"; return $2; });
  // PX erachter als er nix staat.
  tmpStyle = tmpStyle.replace(/(\w+):(\d+);/gi, function($1, $2, $3) { return setPx($1, $2, $3, beginTag) + ";"; }); 
  // Quootjes weghalen in de style=""
  tmpStyle = tmpStyle.replace(/\"|,/gi, "");
  // Eventuele = omzetten naar :
  tmpStyle = tmpStyle.replace(/=/gi, ":");
  // De style regel inelkaar plakken als er wel iets gevonden is.
  if (tmpStyle != "null") tmpStyle = " style=\"" + tmpStyle + "\""; else tmpStyle = " ";
  // Spaties achter quootjes verwijderen.
  tmpStyle = tmpStyle.replace(/([\"|\'])\s/gi, "$1");
  // Alle style="" stukjes verwijderen en ...
  str = str.replace(/style=[\"\'][^\'\"]*[\"\'][\s+\n]?/gi, "");
  // ... vervangen door de net gemaakte Style regel (tmpStyle).
  str = tmpStyle + str;
  // Dubbele spaties eruit filteren.
  str = str.replace(/ +/gi, " ");
  // Enters terug zetten.
  str = str.replace(/\x0A|\x0D/gi, "__enter__");
  // Tag naam er weer voor zetten.
  str = beginTag + str + endTag;
  return str;
}

function xhtmlTagClose(tag)
{
  tag = tag.replace(/(\s*[^\/])>$/, "$1 />");
  return tag;
}

function clean(tag)
{
  // ; ; eruit vissen.
  tag = tag.replace(/;\s+;/gi, ";");
  // value";prop opschonen tot value"; property.
  tag = tag.replace(/;([\"\'])([^\s])/gi, ";$1 $2");
  // value" >  omzetten naar value">
  tag = tag.replace(/\s+>$/gi, ">");
  // Dubbele spaties eruit.
  tag = tag.replace(/\s+/gi, " ");
  // Spaties voor de />
  tag = tag.replace(/([^\s])\/>/gi, "$1 />");
  return tag;
}

function toXHTML(tag)
{
	// Als er niet een tag wordt gevonden waarin alleen letters en spaties in voor komen dan...
	if (tag.match(/<[\/]?[a-z\/ ]+>/i) == null) tag = restyle(tag); else tag = tag.toLowerCase();
	// Dubbele enters eruit filteren.
	tag = tag.replace(/__enter__\s*__enter__/gi, "__enter__");
	// Hier de tags op de goeie manier sluiten als dat nog niet gedaan is.
	tag = tag.replace(/<(br|base|img|area|input|link|frame|meta|hr)[^>]*?>/gi, function($1, $2) { return xhtmlTagClose($1); });
	// Tags opschonen.
	tag = clean(tag);
//        openTags.push(tag.replace(/<([a-z]+)/, "$1"));
	return tag;
}

function saveNoParse(arr, str, ID)
{
  arr.push(str);
  return "]" + ID + (arr.length - 1)+ "[";
}

function restoreNoParse(arr, number)
{
  var fArr = window[arr];
  var arrIndent = window[arr + "Indent"];
  return fArr[number].replace(/__enter__/gi, "\n" + toSpaces(arrIndent[number] * 2));
}

function indent(tag)
{
	// Als het een van de NoParse tags is dan gaat ie hier de indentLevel van deze plek opslaan in het bijbehoorende Array.
	if (tag.match(/\]MASCRIPTS\d+\[|\]MATEXTAREAS+\d\[|\]MAPRES+\d\[|\]MASTYLES+\d\[|\]MAPHPPIECES+\d\[|\]MAASPPIECES+\d\[/gi))
	{
		var idx = tag.match(/\]([A-Z]+)(\d+)\[/);
		switch (idx[1])
		{
			case "MASCRIPTS"    : { var arr = scriptsIndent; break; }
			case "MATEXTAREAS"  : { var arr = textareasIndent; break; }
			case "MAPRES"       : { var arr = presIndent; break; }
			case "MASTYLES"     : { var arr = stylesIndent; break; }
			case "MAPHPPIECES"  : { var arr = phpIndent; break; }
			case "MAASPPIECES"  : { var arr = aspIndent; break; }
			case "MACOMMENTPIECES"  : { var arr = commentIndent; break; }
		}
		arr[idx[2]] = indentLevel;
	}
	// Als het de !doctype is dan moet ie nix doen.
	if (tag.toLowerCase().match(/!doctype/)) return tag;
	tag = tag.replace(/^\s+/, "");
	var isInline = false;
	for (var x = 0; x <= inlineTags.length - 1; x++)
	{
		// Als het een eindtag is ( </tag> ) en het is een inlineTag, dan..
		if (tag.match(/^<[^\/]\w+>.*?<\/?(\w+)>/) && tag.match(/^<\/?(\w+)>/)[1] == inlineTags[x]) { isInline = true; break; }
	}
	
	if (!isInline)
	{
		if (tag.match(/^<\//)) indentLevel -= 1;
		res = toSpaces(indentLevel * 2) + tag;
		// Als het een opentag is dan (of een tag met 1 letter (<b>) ==> { Als het een inline tag is <b>asd</b> de indentLevel 1 naar beneden doen, omdat ie zometeen omhoog gegooit wordt omdat er ook een opentag inzit (<b>). }
		if (tag.match(/^<[^\/].*[^\/]>\s*?$/) || tag.match(/^<\w>\s*?$/)) { if (!tag.match(/<[^>]*>.*<\//g)) indentLevel += 1; }
		// Als er tekst na een tag staat (<div>Hoi).
		else if (tag.match(/^\s*?<.*?>[^<>\/\s]+/i)) { indentLevel += 1; res = toSpaces((indentLevel - 1) * 2) + tag.match(/<.*?>/) + "\n" + toSpaces(indentLevel * 2) + tag.match(/[^<>\/]+$/); } 
		// Als er text voor een sluittag staat ( Hoi</b> )
		else if (tag.match(/^\s*?[^<>\/]+<\/.*?>/i)) { indentLevel -= 1; res = toSpaces((indentLevel + 1) * 2) + tag.match(/[^<>\/]+/) + "\n" + toSpaces(indentLevel * 2) + tag.match(/<.*?>/); }
		// Als er meerdere sluittags achterelkaar staan dan...
		else if (tag.match(/<\/.*?>.*?<\/.*?>/i)) { indentLevel -= tag.match(/<\/\w+>/g).length - 1; }
		return res;
	} else 
	{
		return toSpaces(indentLevel * 2) + tag;
	}
}

function findForgottenCloseTags(str)
{
  var tmpStr = ""; var idx = 0;
  var unClosedTags = new Array();
  var unOpenedTags = new Array();
  str.replace(/<([^\/\s>]+).*?>/g, function($1, $2) { if (!$1.match(/\/>\s*?$/) && !$1.match(/!DOCTYPE/gi)) openTags.push($2); } );
  var x = 0;
  while (x <= openTags.length - 1)
  {
    tmpStr = openTags.shift();
    idx = indexOfArray(closeTags, tmpStr, true);
    if (idx >= 0) closeTags.splice(idx, 1); else unClosedTags.push(tmpStr);
    if (closeTags.length <= 0) { unClosedTags.push(openTags); break; }
  }
  if (closeTags.length > 0) unOpenedTags.push(closeTags);
  var msg = "";
  if (unClosedTags.length > 0 && unClosedTags[0] != "") 
  {
    msg += "The follow tags are not closed:\n";
    for (n in unClosedTags) msg += unClosedTags[n] + "\n";
  }
  if (unOpenedTags.length > 0) 
  {
    msg += "The follow tags are not opened:\n";
    for (n in unOpenedTags) msg += unOpenedTags[n] + "\n";
  }
  if (msg != "") alert(msg);
}

function tagEnter(alles, tussentext, tag)
{
  tag = tag.toLowerCase();
  var isInline = false;
  for (var x = 0; x <= inlineTags.length - 1; x++)
  {
    // Als de tag ( /img ) een inlineTag is of als de vorige er eentje was :)
    if (tag == "/" + inlineTags[x] || indexOfArray(inlineTags, prevTag, true) >= 0) { isInline = true; break; }
  }
  prevTag = tag;
  if (isInline)
    return ">" + tussentext + "<" + tag;
  else
    return ">" + tussentext + "\n<" + tag;
}

function cleanUp(str)
{
  // Alle /td die na een img komen op 1 rij zetten.
  str = str.replace(/(img.*\s\/>)\s*?(<\/td>)/gi, function($1, $2, $3) { return $2 + $3; });

  // Alle <br /> op 1 rij zetten.
  str = str.replace(/(.*)\n\s+<br \/>/gi, "$1<br />");

  return str;
}

function _cleanUp(textAreaToClean, doIndent, doTags)
{
  indentLevel = 0;
  scripts = new Array();
  scriptsIndent = new Array();
  textareas = new Array();
  textareasIndent = new Array();
  pres = new Array();
  presIndent = new Array();
  styles = new Array();
  stylesIndent = new Array();
  php = new Array();
  phpIndent = new Array();
  asp = new Array();
  aspIndent = new Array();
  comment = new Array();
  commentIndent = new Array();

  openTags = new Array();
  closeTags = new Array();
  var str = textAreaToClean.value;
  
  // Enters effe markeren.
  str = str.replace(/\x0D\x0A/gi, "__enter__");
  
  // Scripts etc opslaan. (Die niet geparst moeten worden)
	str = str.replace(/<script.*?<\/script>/gi, function($1) { return saveNoParse(scripts, $1, "MASCRIPTS") } );
	str = str.replace(/<!--.*?-->/gi, function($1) { return saveNoParse(comment, $1, "MACOMMENTPIECES") } );
	str = str.replace(/<textarea.*?<\/textarea>/gi, function($1) { return saveNoParse(textareas, $1, "MATEXTAREAS") } );
	str = str.replace(/<pre.*?<\/pre>/gi, function($1) { return saveNoParse(pres, $1, "MAPRES") } );
	str = str.replace(/<style[^<]*?<\/style>/gi, function($1) { return saveNoParse(styles, $1, "MASTYLES") } );
	str = str.replace(/<\?[^\?>]*?\?>/gi, function($1) { return saveNoParse(php, $1, "MAPHPPIECES") } );
	str = str.replace(/<%[^%>]*?%>/gi, function($1) { return saveNoParse(asp, $1, "MAASPPIECES") } );
  
  if (doTags)
  {
    // Zoeken naar alle tags en omzetten naar XHTML.
    str = str.replace(/<[^\/][^>]*>/gi, function($1) { return toXHTML($1) } );
    // </font> omzetten naar </span>
    str = str.replace(/<\/font>/gi, "</span>");
    // Afsluitende tags effe lowerCasen
    str = str.replace(/<\/(\w+)>/gi, function($1, $2) { closeTags.push($2.toLowerCase()); return $1.toLowerCase(); } );
    // Een spatie achter de ;
    str = str.replace(/;(\w)/gi, "; $1");
  }

  // Enters omzetten naar echte.
  str = str.replace(/__enter__/gi, "\n");
  // Dubbele enters eruit filteren.
  str = str.replace(/\n\s*?\n/gi, "\n");
  if (doIndent)
  {
    // Alle tags op een nieuwe regel plaatsen (Als de > en de < van een andere tag op 1 regel staan).
    str = str.replace(/>([^\n<]*)<([\/]?\w+)/gi, function($1, $2, $3) { return tagEnter($1, $2, $3); } );
    // Indenting.
    str = str.replace(/.+|\]MASCRIPTS\d+\[|\]MATEXTAREAS\d+\[|\]MAPRES\d+\[|\]MASTYLES\d+\[|\]MAPHPPIECES\d+\[|\]MAASPPIECES\d+\[/gi, function($1) { return indent($1); } );
  }

  str = cleanUp(str);

  // Zoeken naar vergeten sluit-tags.
  findForgottenCloseTags(str);

  // Scripts etc terug zetten. (Die niet geparst moeten worden)
  str = str.replace(/\]MAASPPIECES(\d+)\[/g, function($1, $2) { return restoreNoParse("asp", $2) } );
  str = str.replace(/\]MAPHPPIECES(\d+)\[/g, function($1, $2) { return restoreNoParse("php", $2) } );
  str = str.replace(/\]MASTYLES(\d+)\[/g, function($1, $2) { return restoreNoParse("styles", $2) } );
  str = str.replace(/\]MAPRES(\d+)\[/g, function($1, $2) { return restoreNoParse("pres", $2) } );
  str = str.replace(/\]MATEXTAREAS(\d+)\[/g, function($1, $2) { return restoreNoParse("textareas", $2) } );
  str = str.replace(/\]MACOMMENTPIECES(\d+)\[/g, function($1, $2) { return restoreNoParse("comment", $2) } );
  str = str.replace(/\]MASCRIPTS(\d+)\[/g, function($1, $2) { return restoreNoParse("scripts", $2) } );
  textAreaToClean.value = str;
}
