﻿/*
 named capture support for JavaScript
*/
RegExp.enable_named_capture = function(){
	var native_match   = String.prototype.match;
	var native_replace = String.prototype.replace;
	var native_test = RegExp.prototype.test;
	RegExp.last_match = {};
	var last_match = RegExp.last_match;
	RegExp.prototype.support_capture = function(){
		if(this.useCapture) return this;
		var re = this.toString();
		re = re.slice(0, re.lastIndexOf("/"));
		var flag = [
			this.ignoreCase ? "i":"",
			this.global     ? "g":"",
			this.multiline  ? "m":""
		];
		var captureNames = [];
		var capturePattern = /(?:[^\\])\(.*?(?:[^\\])\)/g;
		var namePattern = /<[$\w]+?>/;
		re = native_replace.call(
			re, capturePattern,
			function($0){
				// contain capture name
				if(native_test.call(namePattern,$0)){
					var name = RegExp.lastMatch;
					captureNames.push(name.slice(1,-1));
					return $0.replace(name,"");
				} else {
					captureNames.push(null);
					return $0;
				}
			}
		).slice(1);
		var reg = new RegExp(re,flag.join(""));
		reg.useCapture = true;
		reg.captureNames = captureNames;
		return reg;
	}
	String.prototype.replace = function(before,after){
		if(!RegExp.UseNamedCapture) return native_replace.apply(this,arguments);
		if(before instanceof RegExp && after instanceof Function){
			before = before.support_capture();
		} else {
			return native_replace.apply(this,arguments)
		}
		var hacked = function(){
			var args = arguments;
			args[0] = new String(args[0]);
			for(var i=1;i<args.length-2;i++){
				var cname = before.captureNames[i-1];
				if(cname){
					last_match[cname] = args[0][cname] = args[i];
				}
			}
			return after.apply(null,args);
		}
		return native_replace.call(this,before,hacked);
	};
	String.prototype.match = function(regexp){
		if(!RegExp.UseNamedCapture) return native_match.apply(this,arguments);
		regexp = regexp.support_capture();
		var res = native_match.call(this,regexp);
		if(res == null) return res;
		var global_flag = regexp.global;
		// globalの場合通常通りの結果を返す。
		if(global_flag) return res;
		// res[0]はマッチ部分全体なのでスキップ
		for(var i=1;i<res.length;i++){
			var cname = regexp.captureNames[i-1];
			if(cname) last_match[cname] = res[cname] = res[i];
		}
		return res;
	};
	RegExp.prototype.test = function(str){
		if(!RegExp.UseNamedCapture) return native_test.apply(this,arguments);
		var reg = this.support_capture();
		return String.prototype.match.call(str, reg) ? true : false;
	}
	RegExp.UseNamedCapture = true;
}
RegExp.enable_named_capture();


