2011年 09月 08日

ACE のシンタックスハイライトルールを独立で使う

function dependon (check, src) {
	return function () {
		var ret = Deferred();
		check = new Function('try { return !!(' + check + ') } catch (e) { return false }');
		if (check()) {
			Deferred.next(function () { ret.call() });
		} else {
			var script = document.createElement('script');
			script.charset = 'utf-8';
			script.src = src;
			document.body.appendChild(script);
			setTimeout(function () {
				if (!check()) {
					setTimeout(arguments.callee, 100);
				} else {
					ret.call();
				}
			});
		}
		return ret;
	};
}

var Supported = {
	'perl' : 'Perl',
	'javascript' : 'JavaScript',
	'css' : 'Css'
};

function highlight (container) {
	container.find('pre.code').each(function () {
		if (/lang-(\S+)/.test(this.className)) {
			var lang = Supported[RegExp.$1.toLowerCase()];
			if (!lang) return;
			var pre  = $(this);
			var code = pre.text();

			Deferred.chain(
				dependon('ace', '/js/editor/ace/ace-uncompressed.js'),
				dependon('require("ace/mode/' + lang.toLowerCase() + '_highlight_rules")', '/js/editor/ace/mode-' + lang.toLowerCase() + '.js')
			).
			next(function () {
				var Tokenizer = require("ace/tokenizer").Tokenizer;

				var rules = require("ace/mode/" + lang.toLowerCase() + "_highlight_rules")[lang + 'HighlightRules'];
				var tokenizer = new Tokenizer(new rules().getRules());

				var parent = document.createDocumentFragment();

				var state = 'start';
				var lines = code.split(/\n/);

				return Deferred.repeat(lines.length, function (i) {
					var line = document.createElement('span');
					line.className = 'line';

					var tokens = tokenizer.getLineTokens(lines[i], state);
					for (var j = 0, it; (it = tokens.tokens[j]); j++) {
						if (it.type == 'text') {
							line.appendChild(document.createTextNode(it.value));
						} else {
							var span = document.createElement('span');
							span.className = it.type;
							span.appendChild(document.createTextNode(it.value));
							line.appendChild(span);
						}
					}

					line.appendChild(document.createElement('br'));
					parent.appendChild(line);

					state = tokens.state;
				}).
				next(function () {
					pre.empty().append(parent);
				});
			}).
			error(function (e) {
				alert(e);
			});

		}
	});
}