/* JavaScript Library: WikiRender
 * Author: cu39 <cu39@myad.jp>
 * Version: 0.0.2 (2006-05-06)
 * License: MIT License (X11 License)
/* ---------------------------------- */

var wikiRender = {
	version: '0.0.2',

	interWikiName : {
		// InterWikiName : [
		//	 "URI prefix", "URI suffix", UTF8_encode_flag (boolean), encodeURIComponent_flag (boolean)
		// ],
		GoogleSrch : [
			'http://google.com/search?q=', '&amp;ie=UTF-8&amp;oe=UTF-8', true
		],
		WikipediaJP : [
			'http://ja.wikipedia.org/wiki/', '', true
		],
		A9 : ['http://a9.com/', '', false]
	},

	headingBase : 1,

	closeTags : function(tagName, opened, result) {
		while( 0 < opened.length ) {
			if ( opened[0].match(tagName) ) return;
			result.push('</' + opened.shift() + '>');
			//var close = opened.shift();
			//result.push('</' + close + '>');
		}
	},
	////////// wikirender
	render : function(string) {
		var lines = string.split("\n");
		var opened = [];
		var result = [];

		// variables for list markup
		var maxLv = 5;
		var listPattern = '^(([\-\+]){1,' + maxLv + '})';

		while( 0 < lines.length ) {
			var a_line = lines.shift();
			a_line = a_line.replace(/[\r\n]+$/, "");

			// sanitize
			a_line = a_line.replace(/&/g, "&amp;");
			a_line = a_line.replace(/"/g, "&quot;");
			// enable HTML entities
			a_line = a_line.replace(/&amp;([A-Za-z]+?);/g, "&$1;");
			// character entity references
			a_line = a_line.replace(/&amp;(#[0-9]+?);/g, "&$1;");
			// lt and gt
			a_line = a_line.replace(/</g, "&lt;");
			a_line = a_line.replace(/>/g, "&gt;");

			// ホワイトリストでエンティティ化解除
			if( 1 > opened.length || opened[0] && !opened[0].match(/spre/) )
				a_line = a_line.replace(/&lt;(\/)?(p)&gt;/g, "<$1$2>");

			// blank line
			if( a_line.match(/^\s*$/) ) {
				if ( 1 > opened.length || !opened[0].match(/pre|spre|blockquote/) )
				wikiRender.closeTags('all', opened, result);
			}

			///// 普通の文章
			if( a_line.match(/^[^,:\*\-\+\|]/) &&
			   !a_line.match(/^&gt\;(?:&gt;|\|)/) ) {
				wikiRender.closeTags('^(?:p|pre|spre|blockquote)$', opened, result);
				if( 1 > opened.length ) {
					opened.unshift('p');
					result.push('<p>');
				}
			}

			///// BLOCKQUOTE or SPRE or PRE or Normal Line
			if( a_line.match(/^&gt;/) ) {

				// SPRE: open
				if( a_line.match(/^&gt\;\|\|/ ) ) {
					wikiRender.closeTags('^(?:pre|spre|blockquote)$', opened, result);

					if ( 0 < opened.length &&
						 opened[0].match(/^(?:pre|spre|blockquote)$/) ) {
						result.push(a_line); continue;
					}
					opened.unshift('spre');
					a_line = a_line.replace(/^&gt\;\|\|/, "<pre>");
				}
				// PRE: open
				else if( a_line.match(/^(?:&gt;\|)/ ) ) {
					wikiRender.closeTags('^(?:pre|spre|blockquote)$', opened, result);
					if( 0 < opened.length &&
						opened[0].match(/^(?:pre|spre|blockquote)$/) ) {
						result.push(a_line); continue;
					}
					opened.unshift('pre');
					a_line = a_line.replace(/^&gt;\|/, "<pre>");
				}
				// BLOCKQUOTE: open
				else if( a_line.match(/^&gt\;&gt\;/) ) {
					wikiRender.closeTags('^(?:pre|spre|blockquote)$', opened, result);
					// ignore when PRE or SPRE is opened.
					if( 0 < opened.length && opened[0].match(/^(?:pre|spre)$/) ) {
						result.push(a_line); continue;
					}
					opened.unshift('blockquote');
					a_line = a_line.replace(/^(?:&gt;&gt;)/, "<blockquote>");
				}
				// normal line
				else {
					wikiRender.closeTags('^(?:p|pre|spre|blockquote)$', opened, result);
					if( 1 > opened.length ) {
						opened.unshift('p');
						result.push('<p>');
					}
				}
			}
			else if( a_line.match(/^\|/) ) {
				// SPRE: close
				if( a_line.match(/^\|\|&lt\;/) &&
					opened.length && opened[0].match(/^spre$/) ) {
					opened.shift();
					a_line = a_line.replace(/^\|\|&lt\;/, "</pre>");
				}
				// PRE: close
				else if( a_line.match(/^\|&lt;/) &&
					opened.length && opened[0].match(/pre/) ) {
					opened.shift();
					a_line = a_line.replace(/^\|&lt;/, "</pre>");
				}
				// TABLE
				else if( a_line.match(/^\|(.*)\|$/) ) {
					var temp = RegExp.$1;
					wikiRender.closeTags('table', opened, result);

					var cols = [];
					// Perl版の名残
					// 末尾の区切り文字より後に文字がないと
					// split が配列要素にしてくれないので、
					// 一時的にスペースをかませて調整
					/*
					if( temp.match(/\|$/) ) {
						temp = temp.replace(/\|$/, "| ");
						cols = temp.split('|');
						cols[cols.length-1]
						= cols[cols.length-1].replace(/^ $/, "");  // 配列末尾がスペースだけだったら排除
					}
					else {
					*/
						cols = temp.split('|');
					//}

					a_line = cols.join ('</td><td>');
					a_line = '<tr><td>' + a_line + '</td></tr>';

					if ( 1 > opened.length ) {
						opened.unshift( 'table' );
						result.push( '<table>' );
					}
					// コラム数が変わった
					//else if (cols.length != col_num) {
					//	result.push( '</table><table>');
					//}

					//col_num = cols.length;
				}
				// normal line
				else {
					wikiRender.closeTags('^(?:p|pre|spre|blockquote)$', opened, result);
					if( 1 > opened.length ) {
						opened.unshift('p');
						result.push('<p>');
					}
				}
			}
			else if( a_line.match(/^&lt\;/) ) {
				// BLOCKQUOTE: close
				if( a_line.match(/^&lt\;&lt\;/) &&
					0 < opened.length && opened[0].match(/^blockquote$/) ) {
					opened.shift();
					a_line = a_line.replace(/^&lt\;&lt\;/, "</blockquote>");
				}
				// normal line
				else {
					wikiRender.closeTags('^(?:p|pre|spre|blockquote)$', opened, result);
					if( 1 > opened.length ) {
						opened.unshift('p');
						result.push('<p>');
					}
				}
			}

			///// CSV TABLE
			else if( a_line.match(/^,(.*)/) && 0 < RegExp.$1.length ) {
				var temp = RegExp.$1.split(',');
				wikiRender.closeTags('^(?:table|pre|spre|blockquote)$', opened, result);
				if( 1 > opened.length || !opened[0].match(/^(?:pre|spre|blockquote)$/) )
					a_line = '<tr><td>' + temp.join('</td><td>') + '</td></tr>';
				if( 1 > opened.length ) {
					opened.unshift( 'table' );
					result.push( '<table>' );
				}
			}
			///// end of CSV TABLE

			///// LIst
			else if( a_line.match(listPattern) ) {
				var listLength = RegExp.$1.length;
				var listChar   = RegExp.$2;

				// pre spre blockquote が開かれていたら無視
				if( 0 < opened.length &&
					opened[0].match(/^(?:pre|spre|blockquote)$/) ) {
					result.push(a_line); continue;
				}

				// 行頭の + - を除去
				a_line = a_line.replace(/^[\-\+]+/, "");

				var tag = (listChar == '-') ? 'ul' : 'ol';

				wikiRender.closeTags('^li$', opened, result);

				while( 0 < opened.length && opened[0] == 'li' && opened[1] != tag ) {
					result.push( '</' + opened.shift() + '>' );
					result.push( '</' + opened.shift() + '>' );
				}
				// 現在と同じレベルだったら
				if( listLength == (opened.length/2) ) {
					result.push( '</li><li>' );
				}
				// 現在の ul+1 以上の繰り返し＝ネストが深くなっていたら
				while ( listLength > (opened.length/2) ) {
					opened.unshift( 'li', tag );
					result.push( '<' + tag + '><li>' );
				}
				// 現在の ul-1 以下の繰り返し＝ネストが浅くなっていたら
				while ( listLength < opened.length/2 ) {
					if (2 <= (opened.length)/2 - listLength) {
						result.push( '</li></' + tag + '>' );
						opened.shift(); // li を除去
						opened.shift(); // ul or ol を除去
					} else {
						result.push( '</li></' + tag +'></li><li>' );
						opened.shift(); // li を除去
						opened.shift(); // ul or ol を除去
						//opened.shift(); // li を除去
						//opened.unshift('li');
					}
				}
			}
			///// end of LIst

			///// Definition List
			if( a_line.match(/^:(?:[^:]+?):(?:.+)/) ) {
				wikiRender.closeTags('^dl$', opened, result);
				if ( 1 > opened.length ) {
					opened.unshift( 'dl' );
					result.push( '<dl>');
				}
				a_line = a_line.replace(/^:/, "<dt>");
				a_line = a_line.replace(/^([^:]+?):/, "$1</dt><dd>");
				a_line += '</dd>';
			}
			///// end of Definition List

			///// INLINE
			if( 1 > opened.length || opened[0] != 'spre' ) {
				///// emphasis
				a_line = a_line.replace(/'''''(.+?)'''''/g, "<strong><em>$1</em></strong>");
				a_line = a_line.replace(/'''(.+?)'''/g, "<strong>$1</strong>");
				a_line = a_line.replace(/''(.+?)''/g, "<em>$1</em>");
				a_line = a_line.replace(/'/g, "&#39;"); // 残った ' を文字参照に

				///// Hn
				a_line = a_line.replace(/^\*\*\s*(.*)$/g, 
					'<h' + (wikiRender.headingBase + 1) + 
					">$1</h" + (wikiRender.headingBase + 1) + '>');
				a_line = a_line.replace(/^\*\s*([^\*]*?)$/g, 
					'<h' + wikiRender.headingBase + 
					">$1</h" + wikiRender.headingBase + '>');

				var tmpline = a_line;
				///// Anchor link or InterWikiName or InterWikiFunc
				while( tmpline.match( /(\[\[(.+?):(.+?)\]\])/ ) ) {
					var ptn   = RegExp.$1;
					var name  = RegExp.$2;
					var value = RegExp.$3;

					///// InterWikiName
					if( wikiRender.interWikiName[name] ) {
						var encvalue = wikiRender.interWikiName[name][2] ?
							wikiRender.interWikiName[name][3] ?
								encodeURIComponent(value) : encodeURI(value)
								: value;
						encvalue = encvalue.replace(/%20/g, '+');
						a_line = a_line.replace(ptn, 
							'<a href="' 
							+ wikiRender.interWikiName[name][0]
							+ encvalue 
							+ wikiRender.interWikiName[name][1] 
							+ '">' + name + ':' + value + '</a>');
					}

					///// Anchor
					else if( value.match( /^(?:http|ftp):\/\/[^ ]+/ ) ) {
						a_line = a_line.replace(
							ptn, "<a href='" + value + "'>" + name + "</a>"
						);
					}

					tmpline = tmpline.replace(ptn, "");
				}
				///// end of Anchor link or InterWikiName or InterWikiFunc
			}
			///// end of INLINE

			result.push(a_line);
		}
		if( 0 < opened.length ) wikiRender.closeTags('all', opened, result);

		return result.join("\n");
	}
}

/*
History

2006-05-06 : 0.0.2
- 1行に2つ以上のInterWikiNameがある場合、リンクが作成されないバグを修正。
- 1行に2つ以上のAnchor linkがある場合、2つめ以降のリンクが1つめのリンクで置き換えられてしまうバグを修正。
- & を " よりも前（最初）にエンティティ化するようにした。
- 強調に使われなかった ' を文字参照（&#39;）に置換するようにした。

2006-05-05 : 0.0.1
最初のリリース。

*/

/*

(c) 2006 cu39

Permission is hereby granted, free of charge, to any person obtaining a copy of 
this software and associated documentation files (the "Software"), to deal in 
the Software without restriction, including without limitation the rights to use, 
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 
Software, and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all 
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
