// instantCSSEditor
// author: motormean<motormean@s58.xrea.com>
// ver 0.2
var cssPreview = 
{
	defaultNode: 'DIV',

	updateCSS:
	function (source, target)
	{
		// IE
		if (target.styleSheets && target.styleSheets[0].rules)
		{
			for (var i = 0; i < target.styleSheets.length; i++)
				var ss = target.styleSheets[i];
				while (ss.rules && ss.rules.length)
					ss.removeRule(0);
			var styleSheet = target.styleSheets[0];

			source = source.replace(/[\r\n]/g, '').replace(/\/\*.*?\*\//g, '');
			var rules = source.match(/@.*?;|.*?\{.*?\}/g);
			if (rules)
			{
				for (var i = 0; i < rules.length; i++)
				{
					if (!rules[i].match(/(.+?)\{(.+?)\}/))
						continue;
					var selectors 	 = RegExp.$1.split(',');
					var declarations = RegExp.$2;
					for (var j = 0; j < selectors.length; j++)
					{
						if (!selectors[j])
							continue;
						// IE6で属性セレクタ([xx=..])を追加すると問答無用で落ちるので
						styleSheet.addRule(selectors[j].match(/[a-zA-Z1-6\.# >\r\n\*]+/)[0], declarations);
					}
				}
			}
		}
		else
		{
			// Firefox, Opera
			// data:スキームを使ってスタイルシート適用
			var links = document.getElementsByTagName('LINK');
			for (var i = 0, link; link = links[i]; i++)
				if (link.rel.match(/stylesheet/i))
					link.parentNode.removeChild(link);
			var linkStyleSheet = target.createElement('LINK');
			with (linkStyleSheet)
			{
				setAttribute('rel', 'stylesheet');
				setAttribute('type', 'text/css');
				setAttribute('href', 'data:text/css,' + encodeURIComponent(source));
			}
			target.getElementsByTagName('HEAD')[0].appendChild(linkStyleSheet);
		}

		// 解析
		source = source.replace(/[\r\n]/g, '').replace(/\/\*.*?\*\//g, '');
		var rules = source.match(/@.*?;|.*?\{.*?\}/g);
		if (!rules)
			return;

		for (var i = 0, rule; rule = rules[i]; i++)
		{
			if (!rule.match(/(.+?)\{(.*?)\}/))
				continue;
			var selectors = RegExp.$1.split(',');
			for (var j = 0; j < selectors.length; j++)
				this._appendSelectorElements(target, selectors[j]);
		}
	},

	/*
	 * target	: ドキュメント
	 * selectors	: CSSセレクタ
	 * 与えられたCSSセレクタからHTML要素を生成して
	 * ドキュメントに追加していく
	 * その際、セレクタも考慮する
	 */
	_appendSelectorElements:
	function (target, selectors)
	{
		var selectors = this._splitSelectors(selectors);
		var lastElem = target.body;
		var context = '';			// 複数のセレクタの連結による文脈 (ex. "body div#main ins > ins")
		var adjSibling = false;		// 隣接セレクタか？

		for (var i = 0; i < selectors.length; i++)
		{
			/* セレクタの連結子を確認 *
			 *	 ` ' : 子孫(Descendant)セレクタ
			 *	 `>' : 子供(Child)セレクタ
			 *	 `+' : 隣接(Adjacent Sibling)セレクタ
			 *	子孫セレクタ、子供セレクタについてはどちらも親要素の子供を作る
			 *	隣接の場合だけ別
			 */
			if (selectors[i] == '+')
			{
				// 隣接セレクタ
				context += ' + ';
				adjSibling = true;
				continue;
			}
			else if (selectors[i] == '>')
			{
				// 子供セレクタ
				context += ' > ';
				continue;
			}
			context += selectors[i] + ' ';

			// 要素名を取得
			var tagName = selectors[i].replace(/\[.*\]/,'').split('.')[0].split('#')[0].split(':')[0] || this.defaultNode;
			if (tagName == '*')
				continue;

			/* コンテキストが同じものを探す *
			 *	ex.
			 *	> div#one div#two { .. }
			 *	に
			 *	> div#one div#two p { .. }
			 *	が追加された場合、新たにdiv#one,div#twoを作るのではなく
			 *	div#oneの子孫のdiv#twoを探し出してそこに追加
			 */
			var contextElem = null;
			var elms = target.getElementsByTagName(tagName);
			for (var j = 0; j < elms.length; j++)
			{
				if (elms[j]._context && elms[j]._context.indexOf(context) != -1)
				{
					contextElem = elms[j];
					break;
				}
			}

			if (!contextElem)
			{
				// 指定されたコンテキストのエレメントが存在しない場合は作成
				contextElem = target.createElement(tagName);
				if (selectors[i].match(/\.(\w+)/))
					contextElem.className = RegExp.$1;
				else if (selectors[i].match(/#(\w+)/))
					contextElem.id = RegExp.$1;

				// <img>, <script>の特殊処理
				if (contextElem.tagName.toUpperCase() == 'IMG')
					contextElem.setAttribute('alt', context);
				else if(contextElem.tagName.toUpperCase() == 'SCRIPT')
					continue;

				// 属性セレクタの処理
				var attrSelectors = selectors[i].match(/\[.*?\]/g);
				if (attrSelectors)
					for(var j = 0; j < attrSelectors.length; j++)
						if (attrSelectors[j].match(/\[(\w*)([~\|]?="?(.*?)"?)?\]/))
							contextElem.setAttribute(RegExp.$1, RegExp.$3);

				// title属性を付加
				contextElem.setAttribute('title', context);

				if (!tagName.match(/^(hr|img|html|table|iframe|object|br|input)$/i))
					contextElem.innerHTML = context;
				contextElem._context 	= context;

				if (!adjSibling)
				{
					// 子孫/子供セレクタ
					lastElem.appendChild(contextElem);
				}
				else
				{
					// 隣接セレクタ
					var n = lastElem.nextSibling;
					if (n)
						lastElem.parentNode.insertBefore(contextElem, n);
					else
						lastElem.parentNode.appendChild(contextElem);
				}

				// 見やすいように
				lastElem.appendChild(target.createElement('BR'));
				lastElem.appendChild(target.createTextNode("\n"));
			} // if (!contextElem)

			lastElem = contextElem;
			adjSibling = false;
		}
	},

	/*
	 * selectors: ひとつながりのセレクタ
	 * セレクタが記述されている文字列を配列に分割して返す
	 * 例:
	 *    body h2 + p		=> ['body', 'h2', '+', 'p']
	 *    div#main a[href]	=> ['div#main', 'a[href]']
	 */
	_splitSelectors:
	function (selectors)
	{
		var arrSelectors = new Array();
		var selector = '';
		var inAttr = false;
		for (var i = 0; i < selectors.length; i++)
		{
			var ch = selectors.charAt(i);
			if (!inAttr)
			{
				if (ch.match(/\s/))
				{
					//セレクタの区切り
					if (selector != '')
					{
						arrSelectors.push(selector);
						selector = '';
					}
					continue;
				}
				else if(ch == '+' || ch == '>')
				{
					if (selector != '')
					{
						arrSelectors.push(selector);
						selector = '';
					}
					arrSelectors.push(ch);
					continue;
				}
				else if(ch == '[')
				{
					inAttr = true;
				}
			}
			else
			{
				// [attr="..."]部分
				if (ch == ']')
					inAttr = false;
			}
			selector += ch;
		}
		if (selector != '')
			arrSelectors.push(selector);

		return arrSelectors;
	}
};
