<commit_before>/*global process, require, BuiltinAssets, requirejs, global */
define(["FS","Platform","root"], function (FS,Platform,root) {
	var P=FS.PathUtil;
	var loc=document.location.href;
	const WebSite=root.WebSite||{};
	root.WebSite=WebSite;
	WebSite.runType=WebSite.runType||root.WebSite_runType;
	var prot=location.protocol;
	var k;

	var VER=1667370768012;
	if (!prot.match(/^http/)) prot="https:";
	switch(WebSite.runType) {
	case "IDE":
		Object.assign(WebSite,{
			urlAliases: WebSite.urlAliases||{}, top: ".",
			tablet:Platform.tablet,
			mobile:Platform.mobile
		});
		WebSite.builtinAssetNames={
			"images/Ball.png":{name:"$pat_ball", url: "images/Ball.png"},
			"images/base.png":{name:"$pat_base", url: "images/base.png", pwidth:32, pheight:32},
			"images/Sample.png":{name:"$pat_sample", url: "images/Sample.png"},
			"images/inputPad.png":{name:"$pat_inputPad", url: "images/inputPad.png"},
			"images/neko.png":{name:"$pat_neko", url: "images/neko.png", pwidth:32, pheight:32},
			"images/mapchip.png":{name:"$pat_mapchip", url: "images/mapchip.png", pwidth:32, pheight:32}
		};
		if (!WebSite.pluginTop) {
			WebSite.pluginTop=WebSite.top+"/js/plugins";
		}
		WebSite.sampleImg=WebSite.top+"/images";
		WebSite.isNW=(typeof process=="object" && (process.__node_webkit||process.__nwjs));
		WebSite.mp4Disabled=WebSite.isNW;
		WebSite.tonyuHome="/Tonyu/";
		WebSite.PathSep="/";
		if (WebSite.isNW) {
			WebSite.PathSep=require("path").sep;
			WebSite.cwd=P.directorify(process.cwd().replace(/\\/g,"/"));
			//WebSite.exeDir=WebSite.execDir=P.up(P.fixSep(process.execPath)); not suitable when mac
			if (process.env.TONYU_HOME) {
				WebSite.tonyuHome=P.directorify(process.env.TONYU_HOME);
			} else {
				WebSite.tonyuHome=P.rel(WebSite.cwd,"fs/Tonyu/");
			}
			WebSite.logdir=process.env.TONYU_LOGDIR;//"C:/var/log/Tonyu/";
			WebSite.wwwDir=P.rel(WebSite.cwd,"www/");
			WebSite.platform=process.platform;
			WebSite.ffmpeg=P.rel(WebSite.cwd,(WebSite.platform=="win32"?
					"ffmpeg/bin/ffmpeg.exe":"ffmpeg/bin/ffmpeg"));
			WebSite.pkgInfo=require(P.rel(WebSite.cwd, "package.json"));
			if (process.env.TONYU_PROJECTS) {
				WebSite.projects=process.env.TONYU_PROJECTS.replace(/\\/g,"/").split(require('path').delimiter);
			} else if ( WebSite.pkgInfo && WebSite.pkgInfo.config && WebSite.pkgInfo.config.prjDirs ){
				WebSite.projects=WebSite.pkgInfo.config.prjDirs.map(function (d) {
					d=P.directorify(d);
					if (P.isAbsolute(d)) return d;
					return P.rel(WebSite.cwd,d);
				});
			} else {
				WebSite.projects=[P.rel(WebSite.cwd,"Projects/"),
					P.rel(WebSite.tonyuHome,"Projects/")];
			}
			WebSite.kernelDir=P.rel(WebSite.wwwDir,"Kernel/");
		} else {
			if (loc.match(/edit\.tonyu\.jp\/n\//)) {
				WebSite.wwwDir=prot+"//"+location.host+"/n/";
			} else {
				// Why?
				WebSite.wwwDir=prot+"//"+location.host+"/";
			}
			WebSite.projects=[P.rel(WebSite.tonyuHome,"Projects/")];
		}
		//WebSite.kernelDir=WebSite.top+"/Kernel/";
		// kernelDir must be absolute
		WebSite.kernelDir=P.rel(WebSite.wwwDir,"Kernel/");
		// compiledKernel is URL , not file path.
		// It is correct as long as the html pages in www/ (not html/build|dev )
		WebSite.compiledKernel="Kernel/js/concat.js";   //P.rel(WebSite.kernelDir,"js/concat.js");
		WebSite.compiledTools={
			mapEditor2: "Tools/MapEditor2/js/concat.js",
		};
		setNS2DepSpec();
		if (loc.match(/localhost\/tonyu2/)) {
			WebSite.wwwDir=prot+"//"+location.host+"/tonyu2/";
			WebSite.kernelDir=WebSite.wwwDir+"Kernel/";
			WebSite.compiledKernel=WebSite.kernelDir+"js/concat.js";
			WebSite.uploadTmpUrl=prot+"//localhost/tsite/tonyu/e/cgi-bin/uploadTmp.cgi";
			WebSite.newVersionUrl=prot+"//localhost/tsite/tonyu/project/newVersion.cgi";
			WebSite.quickUploadURL=prot+"//localhost/tsite/tonyu/project/quickDone.cgi";
			WebSite.scriptServer="https://localhost/tonyu2/";
		} else {
			WebSite.uploadTmpUrl=prot+"//edit.tonyu.jp/cgi-bin/uploadTmp.cgi";
			WebSite.newVersionUrl=prot+"//www.tonyu.jp/project/newVersion.cgi";
			WebSite.quickUploadURL=prot+"//www.tonyu.jp/project/quickDone.cgi";
			WebSite.scriptServer="https://edit.tonyu.jp/";
		}
		FS.setEnvProvider(new FS.Env(WebSite));
		WebSite.sysVersion=VER;
		WebSite.version=2;
		WebSite.projectEditorURL="project2.html";
		setDefaultResource(WebSite);
		return WebSite;
	case "singleHTML":
		Object.assign(WebSite,{
			urlAliases: WebSite.urlAliases||{}, top: ".",
			tablet:Platform.tablet,
			mobile:Platform.mobile
		});
		if (typeof BuiltinAssets==="object") {
			for (k in BuiltinAssets) {
				WebSite.urlAliases[k]=BuiltinAssets[k];
			}
		}

		WebSite.tonyuHome="/Tonyu/";
		WebSite.projects=[P.rel(WebSite.tonyuHome,"Projects/")];
		if (loc.match(/localhost\/tonyu2/)) {
			WebSite.scriptServer="http://localhost/tonyu2/";
		} else {
			WebSite.scriptServer="https://edit.tonyu.jp/";
		}
		WebSite.pluginTop=WebSite.scriptServer+"js/plugins";
		WebSite.isNW=(typeof process=="object" && (process.__node_webkit||process.__nwjs));
		WebSite.PathSep="/";
		WebSite.compiledKernel=WebSite.scriptServer+"Kernel/js/concat.js";
		WebSite.compiledTools={
			mapEditor2: `${WebSite.scriptServer}Tools/MapEditor2/js/concat.js`,
		};
		setNS2DepSpec();
		FS.setEnvProvider(new FS.Env(WebSite));
		WebSite.sysVersion=VER;
		setDefaultResource(WebSite);
		return WebSite;
	case "multiHTML":
		Object.assign(WebSite,{
			urlAliases: WebSite.urlAliases||{}, top: ".",
			tablet:Platform.tablet,
			mobile:Platform.mobile
		});
		WebSite.tonyuHome="/Tonyu/";
		WebSite.pluginTop=WebSite.top+"/js/plugins";
		WebSite.isNW=(typeof process=="object" && (process.__node_webkit||process.__nwjs));
		WebSite.PathSep="/";
		WebSite.mp4Disabled=WebSite.isNW;
		if (WebSite.isNW) {
			WebSite.PathSep=getPathSep();
			WebSite.cwd=P.directorify(process.cwd().replace(/\\/g,"/"));
			WebSite.platform=process.platform;
		}
		// this sets at runScript2.js
		//WebSite.compiledKernel=WebSite.top+"/js/kernel.js";
		//-------------
		FS.setEnvProvider(new FS.Env(WebSite));
		WebSite.sysVersion=VER;
		return WebSite;
	case "manual":// for BitArrow
		return WebSite;
	default:
		throw new Error("WebSite.runType is not set");
	}
	function setNS2DepSpec() {
		WebSite.ns2depspec=[
	        {namespace:"kernel", url: WebSite.compiledKernel},
	        {namespace:"mapEditor2", url: WebSite.compiledTools.mapEditor2},
	    ];
	}
	function setDefaultResource(WebSite) {
		WebSite.defaultResource={
	       images:[
	          {name:"$pat_base", url: "images/base.png", pwidth:32, pheight:32},
	          {name:"$pat_sample", url: "images/Sample.png"},
	          {name:"$pat_neko", url: "images/neko.png", pwidth:32, pheight:32},
	          {name:"$pat_mapchip", url: "images/mapchip.png", pwidth:32, pheight:32}
	       ],
	       sounds:[]
	    };
	}
	//WebSite.blobPath=WebSite.serverTop+"/serveBlob";        //TODO: urlchange!
	/*WebSite.url={
		getDirInfo:WebSite.serverTop+"/getDirInfo",
		getFiles:WebSite.serverTop+"/File2LSSync",
		putFiles:WebSite.serverTop+"/LS2FileSync"
	};*/
	function getPathSep() {
		const requireTries=[
			()=>require("path"),
			()=>requirejs.nodeRequire("path"),
			()=>global.require("path")
		];
		for (let fsf of requireTries) {
			try {
				const path=fsf();
				if (typeof path.sep==="string") return path.sep;
			} catch(e){
			}
		}
		return "/";
	}
});
<commit_msg>build<commit_after>/*global process, require, BuiltinAssets, requirejs, global */
define(["FS","Platform","root"], function (FS,Platform,root) {
	var P=FS.PathUtil;
	var loc=document.location.href;
	const WebSite=root.WebSite||{};
	root.WebSite=WebSite;
	WebSite.runType=WebSite.runType||root.WebSite_runType;
	var prot=location.protocol;
	var k;

	var VER=1667373160521;
	if (!prot.match(/^http/)) prot="https:";
	switch(WebSite.runType) {
	case "IDE":
		Object.assign(WebSite,{
			urlAliases: WebSite.urlAliases||{}, top: ".",
			tablet:Platform.tablet,
			mobile:Platform.mobile
		});
		WebSite.builtinAssetNames={
			"images/Ball.png":{name:"$pat_ball", url: "images/Ball.png"},
			"images/base.png":{name:"$pat_base", url: "images/base.png", pwidth:32, pheight:32},
			"images/Sample.png":{name:"$pat_sample", url: "images/Sample.png"},
			"images/inputPad.png":{name:"$pat_inputPad", url: "images/inputPad.png"},
			"images/neko.png":{name:"$pat_neko", url: "images/neko.png", pwidth:32, pheight:32},
			"images/mapchip.png":{name:"$pat_mapchip", url: "images/mapchip.png", pwidth:32, pheight:32}
		};
		if (!WebSite.pluginTop) {
			WebSite.pluginTop=WebSite.top+"/js/plugins";
		}
		WebSite.sampleImg=WebSite.top+"/images";
		WebSite.isNW=(typeof process=="object" && (process.__node_webkit||process.__nwjs));
		WebSite.mp4Disabled=WebSite.isNW;
		WebSite.tonyuHome="/Tonyu/";
		WebSite.PathSep="/";
		if (WebSite.isNW) {
			WebSite.PathSep=require("path").sep;
			WebSite.cwd=P.directorify(process.cwd().replace(/\\/g,"/"));
			//WebSite.exeDir=WebSite.execDir=P.up(P.fixSep(process.execPath)); not suitable when mac
			if (process.env.TONYU_HOME) {
				WebSite.tonyuHome=P.directorify(process.env.TONYU_HOME);
			} else {
				WebSite.tonyuHome=P.rel(WebSite.cwd,"fs/Tonyu/");
			}
			WebSite.logdir=process.env.TONYU_LOGDIR;//"C:/var/log/Tonyu/";
			WebSite.wwwDir=P.rel(WebSite.cwd,"www/");
			WebSite.platform=process.platform;
			WebSite.ffmpeg=P.rel(WebSite.cwd,(WebSite.platform=="win32"?
					"ffmpeg/bin/ffmpeg.exe":"ffmpeg/bin/ffmpeg"));
			WebSite.pkgInfo=require(P.rel(WebSite.cwd, "package.json"));
			if (process.env.TONYU_PROJECTS) {
				WebSite.projects=process.env.TONYU_PROJECTS.replace(/\\/g,"/").split(require('path').delimiter);
			} else if ( WebSite.pkgInfo && WebSite.pkgInfo.config && WebSite.pkgInfo.config.prjDirs ){
				WebSite.projects=WebSite.pkgInfo.config.prjDirs.map(function (d) {
					d=P.directorify(d);
					if (P.isAbsolute(d)) return d;
					return P.rel(WebSite.cwd,d);
				});
			} else {
				WebSite.projects=[P.rel(WebSite.cwd,"Projects/"),
					P.rel(WebSite.tonyuHome,"Projects/")];
			}
			WebSite.kernelDir=P.rel(WebSite.wwwDir,"Kernel/");
		} else {
			if (loc.match(/edit\.tonyu\.jp\/n\//)) {
				WebSite.wwwDir=prot+"//"+location.host+"/n/";
			} else {
				// Why?
				WebSite.wwwDir=prot+"//"+location.host+"/";
			}
			WebSite.projects=[P.rel(WebSite.tonyuHome,"Projects/")];
		}
		//WebSite.kernelDir=WebSite.top+"/Kernel/";
		// kernelDir must be absolute
		WebSite.kernelDir=P.rel(WebSite.wwwDir,"Kernel/");
		// compiledKernel is URL , not file path.
		// It is correct as long as the html pages in www/ (not html/build|dev )
		WebSite.compiledKernel="Kernel/js/concat.js";   //P.rel(WebSite.kernelDir,"js/concat.js");
		WebSite.compiledTools={
			mapEditor2: "Tools/MapEditor2/js/concat.js",
		};
		setNS2DepSpec();
		if (loc.match(/localhost\/tonyu2/)) {
			WebSite.wwwDir=prot+"//"+location.host+"/tonyu2/";
			WebSite.kernelDir=WebSite.wwwDir+"Kernel/";
			WebSite.compiledKernel=WebSite.kernelDir+"js/concat.js";
			WebSite.uploadTmpUrl=prot+"//localhost/tsite/tonyu/e/cgi-bin/uploadTmp.cgi";
			WebSite.newVersionUrl=prot+"//localhost/tsite/tonyu/project/newVersion.cgi";
			WebSite.quickUploadURL=prot+"//localhost/tsite/tonyu/project/quickDone.cgi";
			WebSite.scriptServer="https://localhost/tonyu2/";
		} else {
			WebSite.uploadTmpUrl=prot+"//edit.tonyu.jp/cgi-bin/uploadTmp.cgi";
			WebSite.newVersionUrl=prot+"//www.tonyu.jp/project/newVersion.cgi";
			WebSite.quickUploadURL=prot+"//www.tonyu.jp/project/quickDone.cgi";
			WebSite.scriptServer="https://edit.tonyu.jp/";
		}
		FS.setEnvProvider(new FS.Env(WebSite));
		WebSite.sysVersion=VER;
		WebSite.version=2;
		WebSite.projectEditorURL="project2.html";
		setDefaultResource(WebSite);
		return WebSite;
	case "singleHTML":
		Object.assign(WebSite,{
			urlAliases: WebSite.urlAliases||{}, top: ".",
			tablet:Platform.tablet,
			mobile:Platform.mobile
		});
		if (typeof BuiltinAssets==="object") {
			for (k in BuiltinAssets) {
				WebSite.urlAliases[k]=BuiltinAssets[k];
			}
		}

		WebSite.tonyuHome="/Tonyu/";
		WebSite.projects=[P.rel(WebSite.tonyuHome,"Projects/")];
		if (loc.match(/localhost\/tonyu2/)) {
			WebSite.scriptServer="http://localhost/tonyu2/";
		} else {
			WebSite.scriptServer="https://edit.tonyu.jp/";
		}
		WebSite.pluginTop=WebSite.scriptServer+"js/plugins";
		WebSite.isNW=(typeof process=="object" && (process.__node_webkit||process.__nwjs));
		WebSite.PathSep="/";
		WebSite.compiledKernel=WebSite.scriptServer+"Kernel/js/concat.js";
		WebSite.compiledTools={
			mapEditor2: `${WebSite.scriptServer}Tools/MapEditor2/js/concat.js`,
		};
		setNS2DepSpec();
		FS.setEnvProvider(new FS.Env(WebSite));
		WebSite.sysVersion=VER;
		setDefaultResource(WebSite);
		return WebSite;
	case "multiHTML":
		Object.assign(WebSite,{
			urlAliases: WebSite.urlAliases||{}, top: ".",
			tablet:Platform.tablet,
			mobile:Platform.mobile
		});
		WebSite.tonyuHome="/Tonyu/";
		WebSite.pluginTop=WebSite.top+"/js/plugins";
		WebSite.isNW=(typeof process=="object" && (process.__node_webkit||process.__nwjs));
		WebSite.PathSep="/";
		WebSite.mp4Disabled=WebSite.isNW;
		if (WebSite.isNW) {
			WebSite.PathSep=getPathSep();
			WebSite.cwd=P.directorify(process.cwd().replace(/\\/g,"/"));
			WebSite.platform=process.platform;
		}
		// this sets at runScript2.js
		//WebSite.compiledKernel=WebSite.top+"/js/kernel.js";
		//-------------
		FS.setEnvProvider(new FS.Env(WebSite));
		WebSite.sysVersion=VER;
		return WebSite;
	case "manual":// for BitArrow
		return WebSite;
	default:
		throw new Error("WebSite.runType is not set");
	}
	function setNS2DepSpec() {
		WebSite.ns2depspec=[
	        {namespace:"kernel", url: WebSite.compiledKernel},
	        {namespace:"mapEditor2", url: WebSite.compiledTools.mapEditor2},
	    ];
	}
	function setDefaultResource(WebSite) {
		WebSite.defaultResource={
	       images:[
	          {name:"$pat_base", url: "images/base.png", pwidth:32, pheight:32},
	          {name:"$pat_sample", url: "images/Sample.png"},
	          {name:"$pat_neko", url: "images/neko.png", pwidth:32, pheight:32},
	          {name:"$pat_mapchip", url: "images/mapchip.png", pwidth:32, pheight:32}
	       ],
	       sounds:[]
	    };
	}
	//WebSite.blobPath=WebSite.serverTop+"/serveBlob";        //TODO: urlchange!
	/*WebSite.url={
		getDirInfo:WebSite.serverTop+"/getDirInfo",
		getFiles:WebSite.serverTop+"/File2LSSync",
		putFiles:WebSite.serverTop+"/LS2FileSync"
	};*/
	function getPathSep() {
		const requireTries=[
			()=>require("path"),
			()=>requirejs.nodeRequire("path"),
			()=>global.require("path")
		];
		for (let fsf of requireTries) {
			try {
				const path=fsf();
				if (typeof path.sep==="string") return path.sep;
			} catch(e){
			}
		}
		return "/";
	}
});
<|endoftext|>


<commit_before>(function (L) {
    var _this = null;
    L.DynamicUpstream = L.DynamicUpstream || {};
    _this = L.DynamicUpstream = {
        data: {
        },

        init: function () {
            L.Common.loadConfigs("dynamic_upstream", _this, true);
            _this.initEvents();
        },

        initEvents: function(){
            L.Common.initRuleAddDialog("dynamic_upstream", _this);//添加规则对话框
            L.Common.initRuleDeleteDialog("dynamic_upstream", _this);//删除规则对话框
            L.Common.initRuleEditDialog("dynamic_upstream", _this);//编辑规则对话框
            L.Common.initRuleSortEvent("dynamic_upstream", _this);

            L.Common.initSelectorAddDialog("dynamic_upstream", _this);
            L.Common.initSelectorDeleteDialog("dynamic_upstream", _this);
            L.Common.initSelectorEditDialog("dynamic_upstream", _this);
            L.Common.initSelectorSortEvent("dynamic_upstream", _this);
            L.Common.initSelectorClickEvent("dynamic_upstream", _this);

            L.Common.initSelectorTypeChangeEvent();//选择器类型选择事件
            L.Common.initConditionAddOrRemove();//添加或删除条件
            L.Common.initJudgeTypeChangeEvent();//judge类型选择事件
            L.Common.initConditionTypeChangeEvent();//condition类型选择事件

            L.Common.initExtractionAddOrRemove();//添加或删除条件
            L.Common.initExtractionTypeChangeEvent();//extraction类型选择事件
            L.Common.initExtractionAddBtnEvent();//添加提前项按钮事件
            L.Common.initExtractionHasDefaultValueOrNotEvent();//提取项是否有默认值选择事件

            L.Common.initViewAndDownloadEvent("dynamic_upstream", _this);
            L.Common.initSwitchBtn("dynamic_upstream", _this);//redirect关闭、开启
            L.Common.initSyncDialog("dynamic_upstream", _this);//编辑规则对话框
        },

        buildRule: function(){
            var result = {
                success: false,
                data: {
                    name: null,
                    judge:{},
                    extractor: {},
                    handle:{}
                }
            };

            //build name and judge
            var buildJudgeResult = L.Common.buildJudge();
            if(buildJudgeResult.success == true){
                result.data.name = buildJudgeResult.data.name;
                result.data.judge = buildJudgeResult.data.judge;
            }else{
                result.success = false;
                result.data = buildJudgeResult.data;
                return result;
            }

            //build extractor
            var buildExtractorResult = L.Common.buildExtractor();
            if (buildExtractorResult.success == true) {
                result.data.extractor = buildExtractorResult.data.extractor;
            } else {
                result.success = false;
                result.data = buildExtractorResult.data;
                return result;
            }

            //build handle
            var buildHandleResult = _this.buildHandle();
            if(buildHandleResult.success == true){
                result.data.handle = buildHandleResult.handle;
            }else{
                result.success = false;
                result.data = buildHandleResult.data;
                return result;
            }

            //enable or not
            var enable = $('#rule-enable').is(':checked');
            result.data.enable = enable;

            result.success = true;
            return result;
        },

        buildHandle: function(){
            var result = {};
            var handle = {};
            var uri_tmpl = $("#rule-handle-uri-template").val();
            if (!uri_tmpl) {
                result.success = false;
                result.data = "dynamic upstream 中 rewrite使用的uri template不得为空";
                return result;
            }
            handle.uri_tmpl = uri_tmpl;

            var upstream_name = $("#rule-handle-upstream-name").val();
            if (!upstream_name) {
                result.success = false;
                result.data = "dynamic upstream 使用的upstream name不得为空";
                return result;
            }
            handle.upstream_name = upstream_name;

            var host = $("#rule-handle-host").val();
            if (!host) {
               host = false
            }

            handle.host = host;

            handle.log = ($("#rule-handle-log").val() === "true");
            result.success = true;
            result.handle = handle;
            return result;
        },
    };
}(APP));
<commit_msg>[dynamic_upstream] trim space of uri_tmpl<commit_after>(function (L) {
    var _this = null;
    L.DynamicUpstream = L.DynamicUpstream || {};
    _this = L.DynamicUpstream = {
        data: {
        },

        init: function () {
            L.Common.loadConfigs("dynamic_upstream", _this, true);
            _this.initEvents();
        },

        initEvents: function(){
            L.Common.initRuleAddDialog("dynamic_upstream", _this);//添加规则对话框
            L.Common.initRuleDeleteDialog("dynamic_upstream", _this);//删除规则对话框
            L.Common.initRuleEditDialog("dynamic_upstream", _this);//编辑规则对话框
            L.Common.initRuleSortEvent("dynamic_upstream", _this);

            L.Common.initSelectorAddDialog("dynamic_upstream", _this);
            L.Common.initSelectorDeleteDialog("dynamic_upstream", _this);
            L.Common.initSelectorEditDialog("dynamic_upstream", _this);
            L.Common.initSelectorSortEvent("dynamic_upstream", _this);
            L.Common.initSelectorClickEvent("dynamic_upstream", _this);

            L.Common.initSelectorTypeChangeEvent();//选择器类型选择事件
            L.Common.initConditionAddOrRemove();//添加或删除条件
            L.Common.initJudgeTypeChangeEvent();//judge类型选择事件
            L.Common.initConditionTypeChangeEvent();//condition类型选择事件

            L.Common.initExtractionAddOrRemove();//添加或删除条件
            L.Common.initExtractionTypeChangeEvent();//extraction类型选择事件
            L.Common.initExtractionAddBtnEvent();//添加提前项按钮事件
            L.Common.initExtractionHasDefaultValueOrNotEvent();//提取项是否有默认值选择事件

            L.Common.initViewAndDownloadEvent("dynamic_upstream", _this);
            L.Common.initSwitchBtn("dynamic_upstream", _this);//redirect关闭、开启
            L.Common.initSyncDialog("dynamic_upstream", _this);//编辑规则对话框
        },

        buildRule: function(){
            var result = {
                success: false,
                data: {
                    name: null,
                    judge:{},
                    extractor: {},
                    handle:{}
                }
            };

            //build name and judge
            var buildJudgeResult = L.Common.buildJudge();
            if(buildJudgeResult.success == true){
                result.data.name = buildJudgeResult.data.name;
                result.data.judge = buildJudgeResult.data.judge;
            }else{
                result.success = false;
                result.data = buildJudgeResult.data;
                return result;
            }

            //build extractor
            var buildExtractorResult = L.Common.buildExtractor();
            if (buildExtractorResult.success == true) {
                result.data.extractor = buildExtractorResult.data.extractor;
            } else {
                result.success = false;
                result.data = buildExtractorResult.data;
                return result;
            }

            //build handle
            var buildHandleResult = _this.buildHandle();
            if(buildHandleResult.success == true){
                result.data.handle = buildHandleResult.handle;
            }else{
                result.success = false;
                result.data = buildHandleResult.data;
                return result;
            }

            //enable or not
            var enable = $('#rule-enable').is(':checked');
            result.data.enable = enable;

            result.success = true;
            return result;
        },

        buildHandle: function(){
            var result = {};
            var handle = {};
            var uri_tmpl = $("#rule-handle-uri-template").val();
            if (!uri_tmpl) {
                result.success = false;
                result.data = "dynamic upstream 中 rewrite使用的uri template不得为空";
                return result;
            }
            // refer this https://www.w3schools.com/jsref/jsref_trim_string.asp
            handle.uri_tmpl = function(x) {
                return x.replace(/^\s+|\s+$/gm,'');
            }(uri_tmpl);

            var upstream_name = $("#rule-handle-upstream-name").val();
            if (!upstream_name) {
                result.success = false;
                result.data = "dynamic upstream 使用的upstream name不得为空";
                return result;
            }
            handle.upstream_name = upstream_name;

            var host = $("#rule-handle-host").val();
            if (!host) {
               host = false
            }

            handle.host = host;

            handle.log = ($("#rule-handle-log").val() === "true");
            result.success = true;
            result.handle = handle;
            return result;
        },
    };
}(APP));
<|endoftext|>


<commit_before>import React from 'react';

import { blazyb, sref, Zerotorescue } from 'MAINTAINERS';

import Wrapper from 'common/Wrapper';
import ItemLink from 'common/ItemLink';
import ITEMS from 'common/ITEMS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';

export default [
  {
    date: new Date('2018-02-03'),
    changes: 'Added a Checklist to the top of the page, which is a more organized and better explained version of the Suggestion system.',
    contributors: [sref],
  },
  {
    date: new Date('2018-02-02'),
    changes: <Wrapper>Fixed a bug in <SpellLink id={SPELLS.CLEARCASTING_BUFF.id} icon /> Util module where overwritten procs weren't shown if player doesn't take <SpellLink id={SPELLS.MOMENT_OF_CLARITY_TALENT_RESTORATION.id} icon />.</Wrapper>,
  },
  {
    date: new Date('2018-01-26'),
    changes: <Wrapper>Updated HoT tracking framework to allow attribution of HoT extensions, allowing updates to <SpellLink id={SPELLS.FLOURISH_TALENT.id} icon />. Also added modules for <ItemLink id={ITEMS.EDRAITH_BONDS_OF_AGLAYA.id} icon />, <ItemLink id={ITEMS.AMANTHULS_WISDOM.id} icon />, and <SpellLink id={SPELLS.DEEP_ROOTED_TRAIT.id} icon />.</Wrapper>,
    contributors: [sref],
  },
  {
    date: new Date('2018-01-10'),
    changes: <Wrapper>Added framework for tracking HoT attribution, allowing updates to several modules: <ItemLink id={ITEMS.TEARSTONE_OF_ELUNE.id} icon />, <SpellLink id={SPELLS.POWER_OF_THE_ARCHDRUID.id} icon />, <SpellLink id={SPELLS.RESTO_DRUID_T21_2SET_BONUS_BUFF.id} icon />, and <SpellLink id={SPELLS.RESTO_DRUID_T21_4SET_BONUS_BUFF.id} icon />. Also added <SpellLink id={SPELLS.RESTO_DRUID_T19_4SET_BONUS_BUFF.id} icon />.</Wrapper>,
    contributors: [sref],
  },
  {
    date: new Date('2018-01-07'),
    changes: 'Clearcasting module now shows percent used instead of percent missed, tooltip updated, and now works with MoC',
    contributors: [sref],
  },
  {
    date: new Date('2017-12-24'),
    changes: 'Fixed crash with the new Insignia of the Grand Army module.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-12-07'),
    changes: 'Stat values show healing gained per 1 rating on hover.',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-12-05'),
    changes: 'Added throughput for some NLC traits.',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-10-23'),
    changes: 'Added Stat Weights calculator',
    contributors: [sref],
  },
  {
    date: new Date('2017-10-14'),
    changes: "Now uses the base Sephuz's Secret module, which displays average haste gained.",
    contributors: [sref],
  },
  {
    date: new Date('2017-10-06'),
    changes: 'Added Ironbark module.',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-29'),
    changes: 'Flourish display updates.',
    contributors: [blazyb, sref],
  },
  {
    date: new Date('2017-09-26'),
    changes: 'Added suggestions for Soul of the Forest / Archdruid WG usage',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-23'),
    changes: 'Updated Mastery calculations to be less friendly to overhealing. Expect lower results from Cultivation, Cenarion Ward, Spring Blossoms, and 2PT19.',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-22'),
    changes: 'Updated display of 2PT20 to show average refunded cooldown instead of throughput estimate.',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-21'),
    changes: 'Minor bug fix to clearcasting module.',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-19'),
    changes: 'Added Average HoTs statistic',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-18'),
    changes: "Added Essence of G'Hanir to the cooldowns tab",
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Added relic traits module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-07'),
    changes: "Added suggestion when high Nature's Essence overheal",
    contributors: [sref],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Added preliminary support for T21',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Activated low health healing module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Added suggestions for low healing from Cultivation and Spring Blossoms',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-04'),
    changes: 'Added Mastery calculations, with support for Spring Blossoms, Cenarion Ward, Cultivation, and the T19 2Set',
    contributors: [sref],
  },
  {
    date: new Date('2017-07-06'),
    changes: 'Added mana costs for spells, and the cooldown tab should now properly calculate mana costs',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-07-06'),
    changes: 'Fixed a bug in 4PT20 calculations, and added Swiftmend to the cast efficiency tab',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-23'),
    changes: "Added Essence of G'Hanir module",
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-20'),
    changes: 'Added Gnawed Thumb Ring module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-16'),
    changes: 'Adjustments to DMD:Promises module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-01'),
    changes: 'Updated T20 calculations based on PTR changes',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-30'),
    changes: 'Added Dreamwalker calculations (thanks @greatman)',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-30'),
    changes: 'Added SotF + SotA modules',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-26'),
    changes: 'Added non-healing time and downtime calculations',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-26'),
    changes: 'Added Chameleon Song module. Also updated Tree of Life module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-25'),
    changes: 'Added T20 module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-21'),
    changes: 'Added Resto Druid Analyzer',
    contributors: [blazyb],
  },
];
<commit_msg>Fixed merge mistake...<commit_after>import React from 'react';

import { blazyb, sref, Zerotorescue } from 'MAINTAINERS';

import Wrapper from 'common/Wrapper';
import ItemLink from 'common/ItemLink';
import ITEMS from 'common/ITEMS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';

export default [
  {
    date: new Date('2018-02-03'),
    changes: 'Added a Checklist to the top of the page, which is a more organized and better explained version of the Suggestion system.',
    contributors: [sref],
  },
  {
    date: new Date('2018-02-02'),
    changes: <Wrapper>Fixed a bug in <SpellLink id={SPELLS.CLEARCASTING_BUFF.id} icon /> Util module where overwritten procs weren't shown if player doesn't take <SpellLink id={SPELLS.MOMENT_OF_CLARITY_TALENT_RESTORATION.id} icon />.</Wrapper>,
    contributors: [sref],
  },
  {
    date: new Date('2018-01-26'),
    changes: <Wrapper>Updated HoT tracking framework to allow attribution of HoT extensions, allowing updates to <SpellLink id={SPELLS.FLOURISH_TALENT.id} icon />. Also added modules for <ItemLink id={ITEMS.EDRAITH_BONDS_OF_AGLAYA.id} icon />, <ItemLink id={ITEMS.AMANTHULS_WISDOM.id} icon />, and <SpellLink id={SPELLS.DEEP_ROOTED_TRAIT.id} icon />.</Wrapper>,
    contributors: [sref],
  },
  {
    date: new Date('2018-01-10'),
    changes: <Wrapper>Added framework for tracking HoT attribution, allowing updates to several modules: <ItemLink id={ITEMS.TEARSTONE_OF_ELUNE.id} icon />, <SpellLink id={SPELLS.POWER_OF_THE_ARCHDRUID.id} icon />, <SpellLink id={SPELLS.RESTO_DRUID_T21_2SET_BONUS_BUFF.id} icon />, and <SpellLink id={SPELLS.RESTO_DRUID_T21_4SET_BONUS_BUFF.id} icon />. Also added <SpellLink id={SPELLS.RESTO_DRUID_T19_4SET_BONUS_BUFF.id} icon />.</Wrapper>,
    contributors: [sref],
  },
  {
    date: new Date('2018-01-07'),
    changes: 'Clearcasting module now shows percent used instead of percent missed, tooltip updated, and now works with MoC',
    contributors: [sref],
  },
  {
    date: new Date('2017-12-24'),
    changes: 'Fixed crash with the new Insignia of the Grand Army module.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-12-07'),
    changes: 'Stat values show healing gained per 1 rating on hover.',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-12-05'),
    changes: 'Added throughput for some NLC traits.',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-10-23'),
    changes: 'Added Stat Weights calculator',
    contributors: [sref],
  },
  {
    date: new Date('2017-10-14'),
    changes: "Now uses the base Sephuz's Secret module, which displays average haste gained.",
    contributors: [sref],
  },
  {
    date: new Date('2017-10-06'),
    changes: 'Added Ironbark module.',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-29'),
    changes: 'Flourish display updates.',
    contributors: [blazyb, sref],
  },
  {
    date: new Date('2017-09-26'),
    changes: 'Added suggestions for Soul of the Forest / Archdruid WG usage',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-23'),
    changes: 'Updated Mastery calculations to be less friendly to overhealing. Expect lower results from Cultivation, Cenarion Ward, Spring Blossoms, and 2PT19.',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-22'),
    changes: 'Updated display of 2PT20 to show average refunded cooldown instead of throughput estimate.',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-21'),
    changes: 'Minor bug fix to clearcasting module.',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-19'),
    changes: 'Added Average HoTs statistic',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-18'),
    changes: "Added Essence of G'Hanir to the cooldowns tab",
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Added relic traits module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-07'),
    changes: "Added suggestion when high Nature's Essence overheal",
    contributors: [sref],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Added preliminary support for T21',
    contributors: [sref],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Activated low health healing module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-07'),
    changes: 'Added suggestions for low healing from Cultivation and Spring Blossoms',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-09-04'),
    changes: 'Added Mastery calculations, with support for Spring Blossoms, Cenarion Ward, Cultivation, and the T19 2Set',
    contributors: [sref],
  },
  {
    date: new Date('2017-07-06'),
    changes: 'Added mana costs for spells, and the cooldown tab should now properly calculate mana costs',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-07-06'),
    changes: 'Fixed a bug in 4PT20 calculations, and added Swiftmend to the cast efficiency tab',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-23'),
    changes: "Added Essence of G'Hanir module",
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-20'),
    changes: 'Added Gnawed Thumb Ring module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-16'),
    changes: 'Adjustments to DMD:Promises module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-06-01'),
    changes: 'Updated T20 calculations based on PTR changes',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-30'),
    changes: 'Added Dreamwalker calculations (thanks @greatman)',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-30'),
    changes: 'Added SotF + SotA modules',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-26'),
    changes: 'Added non-healing time and downtime calculations',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-26'),
    changes: 'Added Chameleon Song module. Also updated Tree of Life module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-25'),
    changes: 'Added T20 module',
    contributors: [blazyb],
  },
  {
    date: new Date('2017-05-21'),
    changes: 'Added Resto Druid Analyzer',
    contributors: [blazyb],
  },
];
<|endoftext|>


<commit_before>import React from 'react';

import { Oratio, Gao, Reglitch, hassebewlen, Zerotorescue, milesoldenburg, Gebuz } from 'CONTRIBUTORS';

import ItemLink from 'common/ItemLink';
import ITEMS from 'common/ITEMS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';

export default [
  {
    date: new Date('2018-07-26'),
    changes: <React.Fragment>Added support for the new <SpellLink id={SPELLS.PENANCE_CAST.id} /> event, thanks Blizzard.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2018-07-24'),
    changes: <React.Fragment>Fix crash when using <SpellLink id={SPELLS.LUMINOUS_BARRIER_TALENT.id} />.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2018-07-18'),
    changes: <React.Fragment>Now with 100% more Batle for Azeroth.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2018-04-08'),
    changes: <React.Fragment>Fixed a crash when using <ItemLink id={ITEMS.NERO_BAND_OF_PROMISES.id} />.</React.Fragment>,
    contributors: [Gebuz],
  },
  {
    date: new Date('2018-03-25'),
    changes: <React.Fragment>Fixed <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> and <SpellLink id={SPELLS.POWER_WORD_RADIANCE.id} /> cooldowns.</React.Fragment>,
    contributors: [Gebuz],
  },
  {
    date: new Date('2018-03-19'),
    changes: <React.Fragment>Updated the Atonement sources tab to show related information for some spells. Changed <ItemLink id={ITEMS.CARAFE_OF_SEARING_LIGHT.id} /> to use the more accurate Atonement source tracking.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2018-01-18'),
    changes: <React.Fragment>Added HPS, total healing and overheal to the atonement sources tab.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-12-29'),
    changes: 'Fixed display in the timeline and the inclusion in active time of Penance.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-12-07'),
    changes: 'Added healing per penance bolt in Atonement Source Tab.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-12-01'),
    changes: <React.Fragment>Added Support for <ItemLink id={ITEMS.CARAFE_OF_SEARING_LIGHT.id} />.</React.Fragment>,
    contributors: [Oratio],
  },
  {
    date: new Date('2017-11-15'),
    changes: <React.Fragment>Moved <ItemLink id={ITEMS.SOUL_OF_THE_HIGH_PRIEST.id} /> value to show the talent <SpellLink id={SPELLS.TWIST_OF_FATE_TALENT_DISCIPLINE.id} /></React.Fragment>,
    contributors: [Gao],
  },
  {
    date: new Date('2017-11-15'),
    changes: 'Added a absolute healing toggle in atonement sources',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-10-13'),
    changes: 'Fixed a bug with some trinket damage events causing a crash.',
    contributors: [Gao],
  },
  {
    date: new Date('2017-11-10'),
    changes: 'Added T21 4pc bonus.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-11-01'),
    changes: 'Added T21 2pc bonus.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-11-02'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.ESTEL_DEJAHNAS_INSPIRATION.id} /> Avg Haste bug</React.Fragment>,
    contributors: [Gao],
  },
  {
    date: new Date('2017-10-27'),
    changes: 'Refactored disc module.',
    contributors: [Gao],
  },
  {
    date: new Date('2017-10-26'),
    changes: 'Adjusted the T20 2pc bonus for the recent nerfs.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-10-27'),
    changes: 'Added an atonement normalizer to fix atonement ordering issues.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-10-16'),
    changes: 'Fixed T20 4pc bug, added a suggestion to utilise the buff.',
    contributors: [Reglitch],
  },
  {
    date: new Date('2017-09-15'),
    changes: 'Pet damage is now also taken into consideration for Atonement damage source.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-09-14'),
    changes: 'Fix issue where friendly fire damage was considered a valid Atonement damage source.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-09-14'),
    changes: 'Added atonement sources breakdown. This isn\'t very accurate yet, it\'s just showing the way it\'s assigned right now. Improved accuracy will come later.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-07-22'),
    changes: <React.Fragment>Added mana saved from the legendary <ItemLink id={ITEMS.INNER_HALLATION.id} />.</React.Fragment>,
    contributors: [hassebewlen],
  },
  {
    date: new Date('2017-06-18'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.TOUCH_OF_THE_GRAVE.id} /> statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-17'),
    changes: <React.Fragment><SpellLink id={SPELLS.EVANGELISM_TALENT.id} /> casts are now also shown under the cooldowns tab. <SpellLink id={SPELLS.RAPTURE.id} /> now shows the total abosrbs applied and the amount of damage absorbed. Fixed a few issues that caused too much healing to be assigned to <SpellLink id={SPELLS.EVANGELISM_TALENT.id} />.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-15'),
    changes: <React.Fragment>Fixed Wasted Penance bolts always assumed user had the <SpellLink id={SPELLS.CASTIGATION_TALENT.id} /> talent.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-15'),
    changes: <React.Fragment>Disabled suggestions for <SpellLink id={SPELLS.PAIN_SUPPRESSION.id} /> and <SpellLink id={SPELLS.POWER_WORD_BARRIER_CAST.id} />.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-11'),
    changes: <React.Fragment>Added extra suggestion to <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> description to add distinction to casts during <SpellLink id={SPELLS.RAPTURE.id} />.</React.Fragment>,
    contributors: [milesoldenburg],
  },
  {
    date: new Date('2017-06-05'),
    changes: <React.Fragment>Fix Atonement duration in cast efficiency not accounting for the Doomsayer trait.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-29'),
    changes: <React.Fragment>Show Rapture <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> casts seperate from regular <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> casts in Cast Efficiency.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-28'),
    changes: 'Added unused Power Word: Shield absorb statistic',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-25'),
    changes: 'Added Early Atonement refreshes statistic. Fixed Skjoldr sometimes not working properly. Both contributions were created by @Shadowdrizz. Thanks a lot for your contribution! Fix Shadowfiend showing with the Mindbender talent.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-18'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.SHADOW_WORD_PAIN.id} />/<SpellLink id={SPELLS.PURGE_THE_WICKED_TALENT.id} /> global uptime statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-17'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.TWIST_OF_FATE_TALENT_DISCIPLINE.id} /> healing statistic (damage gain is in the tooltip).</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-16'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.DARKMOON_DECK_PROMISES.id} /> statistic. Added Light's Wrath to cast efficiency. Changed Pain Suppression cooldown to take into account the Pain is in your Mind trait.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-16'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.NERO_BAND_OF_PROMISES.id} /> statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-15'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.XALAN_THE_FEAREDS_CLENCH.id} />  statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-15'),
    changes: <React.Fragment><ItemLink id={ITEMS.SKJOLDR_SANCTUARY_OF_IVAGONT.id} /> statistic now includes the healing gained via Share in the Light.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.SKJOLDR_SANCTUARY_OF_IVAGONT.id} /> statistic. This does not yet include the healing gained via Share in the Light.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.CORD_OF_MAIEV_PRIESTESS_OF_THE_MOON.id} /> statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: 'Added Disc Priest 2 set bonus statistic.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: <React.Fragment>Renamed Missed penance hits to Wasted Penance bolts. Wasted Penance bolts now accounts for (combat log) latency. Fixed Glyph of the Sha's Shadowfiend not being counted towards Shadowfiend casts. Fixed healing increases (most notably the 15% from Velen's) not working with Disc priest spells.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-05'),
    changes: <React.Fragment>Fixed issue where critical atonement healing was not being counted, fixed <ItemLink id={ITEMS.NERO_BAND_OF_PROMISES.id} /> being broken.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2017-05-14'),
    changes: 'Added Discipline Priest spec. Currently includes basic statistics for Dead GCD time (should be fully operational), shared legendaries, missed Penance hits, cast efficiencies and the other build in tools.',
    contributors: [Zerotorescue],
  },
];
<commit_msg>Added Change log stuff<commit_after>import React from 'react';

import { Oratio, Gao, Reglitch, hassebewlen, Zerotorescue, milesoldenburg, Gebuz } from 'CONTRIBUTORS';

import ItemLink from 'common/ItemLink';
import ITEMS from 'common/ITEMS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';

export default [
  {
    date: new Date('2018-07-31'),
    changes: <React.Fragment>Rework of the <SpellLink id={SPELLS.GRACE.id} /> module.</React.Fragment>,
    contributors: [Oratio],
  },
  {
    date: new Date('2018-07-26'),
    changes: <React.Fragment>Added support for the new <SpellLink id={SPELLS.PENANCE_CAST.id} /> event, thanks Blizzard.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2018-07-19'),
    changes: <React.Fragment>Fixed <SpellLink id={SPELLS.SINS_OF_THE_MANY_TALENT.id} /> bug.</React.Fragment>,
    contributors: [Oratio],
  },
  {
    date: new Date('2018-07-24'),
    changes: <React.Fragment>Fix crash when using <SpellLink id={SPELLS.LUMINOUS_BARRIER_TALENT.id} />.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2018-07-18'),
    changes: <React.Fragment>Now with 100% more Batle for Azeroth.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2018-05-25'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.DESPERATE_PRAYER.id} /> module.</React.Fragment>,
    contributors: [Oratio],
  },
  {
    date: new Date('2018-05-09'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.LENIENCE_TALENT.id} /> module.</React.Fragment>,
    contributors: [Oratio],
  },
  {
    date: new Date('2018-05-09'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.LUMINOUS_BARRIER_TALENT.id} /> module.</React.Fragment>,
    contributors: [Oratio],
  },
  {
    date: new Date('2018-04-08'),
    changes: <React.Fragment>Fixed a crash when using <ItemLink id={ITEMS.NERO_BAND_OF_PROMISES.id} />.</React.Fragment>,
    contributors: [Gebuz],
  },
  {
    date: new Date('2018-03-25'),
    changes: <React.Fragment>Fixed <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> and <SpellLink id={SPELLS.POWER_WORD_RADIANCE.id} /> cooldowns.</React.Fragment>,
    contributors: [Gebuz],
  },
  {
    date: new Date('2018-03-19'),
    changes: <React.Fragment>Updated the Atonement sources tab to show related information for some spells. Changed <ItemLink id={ITEMS.CARAFE_OF_SEARING_LIGHT.id} /> to use the more accurate Atonement source tracking.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2018-01-18'),
    changes: <React.Fragment>Added HPS, total healing and overheal to the atonement sources tab.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-12-29'),
    changes: 'Fixed display in the timeline and the inclusion in active time of Penance.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-12-07'),
    changes: 'Added healing per penance bolt in Atonement Source Tab.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-12-01'),
    changes: <React.Fragment>Added Support for <ItemLink id={ITEMS.CARAFE_OF_SEARING_LIGHT.id} />.</React.Fragment>,
    contributors: [Oratio],
  },
  {
    date: new Date('2017-11-15'),
    changes: <React.Fragment>Moved <ItemLink id={ITEMS.SOUL_OF_THE_HIGH_PRIEST.id} /> value to show the talent <SpellLink id={SPELLS.TWIST_OF_FATE_TALENT_DISCIPLINE.id} /></React.Fragment>,
    contributors: [Gao],
  },
  {
    date: new Date('2017-11-15'),
    changes: 'Added a absolute healing toggle in atonement sources',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-10-13'),
    changes: 'Fixed a bug with some trinket damage events causing a crash.',
    contributors: [Gao],
  },
  {
    date: new Date('2017-11-10'),
    changes: 'Added T21 4pc bonus.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-11-01'),
    changes: 'Added T21 2pc bonus.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-11-02'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.ESTEL_DEJAHNAS_INSPIRATION.id} /> Avg Haste bug</React.Fragment>,
    contributors: [Gao],
  },
  {
    date: new Date('2017-10-27'),
    changes: 'Refactored disc module.',
    contributors: [Gao],
  },
  {
    date: new Date('2017-10-26'),
    changes: 'Adjusted the T20 2pc bonus for the recent nerfs.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-10-27'),
    changes: 'Added an atonement normalizer to fix atonement ordering issues.',
    contributors: [Oratio],
  },
  {
    date: new Date('2017-10-16'),
    changes: 'Fixed T20 4pc bug, added a suggestion to utilise the buff.',
    contributors: [Reglitch],
  },
  {
    date: new Date('2017-09-15'),
    changes: 'Pet damage is now also taken into consideration for Atonement damage source.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-09-14'),
    changes: 'Fix issue where friendly fire damage was considered a valid Atonement damage source.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-09-14'),
    changes: 'Added atonement sources breakdown. This isn\'t very accurate yet, it\'s just showing the way it\'s assigned right now. Improved accuracy will come later.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-07-22'),
    changes: <React.Fragment>Added mana saved from the legendary <ItemLink id={ITEMS.INNER_HALLATION.id} />.</React.Fragment>,
    contributors: [hassebewlen],
  },
  {
    date: new Date('2017-06-18'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.TOUCH_OF_THE_GRAVE.id} /> statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-17'),
    changes: <React.Fragment><SpellLink id={SPELLS.EVANGELISM_TALENT.id} /> casts are now also shown under the cooldowns tab. <SpellLink id={SPELLS.RAPTURE.id} /> now shows the total abosrbs applied and the amount of damage absorbed. Fixed a few issues that caused too much healing to be assigned to <SpellLink id={SPELLS.EVANGELISM_TALENT.id} />.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-15'),
    changes: <React.Fragment>Fixed Wasted Penance bolts always assumed user had the <SpellLink id={SPELLS.CASTIGATION_TALENT.id} /> talent.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-15'),
    changes: <React.Fragment>Disabled suggestions for <SpellLink id={SPELLS.PAIN_SUPPRESSION.id} /> and <SpellLink id={SPELLS.POWER_WORD_BARRIER_CAST.id} />.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-11'),
    changes: <React.Fragment>Added extra suggestion to <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> description to add distinction to casts during <SpellLink id={SPELLS.RAPTURE.id} />.</React.Fragment>,
    contributors: [milesoldenburg],
  },
  {
    date: new Date('2017-06-05'),
    changes: <React.Fragment>Fix Atonement duration in cast efficiency not accounting for the Doomsayer trait.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-29'),
    changes: <React.Fragment>Show Rapture <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> casts seperate from regular <SpellLink id={SPELLS.POWER_WORD_SHIELD.id} /> casts in Cast Efficiency.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-28'),
    changes: 'Added unused Power Word: Shield absorb statistic',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-25'),
    changes: 'Added Early Atonement refreshes statistic. Fixed Skjoldr sometimes not working properly. Both contributions were created by @Shadowdrizz. Thanks a lot for your contribution! Fix Shadowfiend showing with the Mindbender talent.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-18'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.SHADOW_WORD_PAIN.id} />/<SpellLink id={SPELLS.PURGE_THE_WICKED_TALENT.id} /> global uptime statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-17'),
    changes: <React.Fragment>Added <SpellLink id={SPELLS.TWIST_OF_FATE_TALENT_DISCIPLINE.id} /> healing statistic (damage gain is in the tooltip).</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-16'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.DARKMOON_DECK_PROMISES.id} /> statistic. Added Light's Wrath to cast efficiency. Changed Pain Suppression cooldown to take into account the Pain is in your Mind trait.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-16'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.NERO_BAND_OF_PROMISES.id} /> statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-15'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.XALAN_THE_FEAREDS_CLENCH.id} />  statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-15'),
    changes: <React.Fragment><ItemLink id={ITEMS.SKJOLDR_SANCTUARY_OF_IVAGONT.id} /> statistic now includes the healing gained via Share in the Light.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.SKJOLDR_SANCTUARY_OF_IVAGONT.id} /> statistic. This does not yet include the healing gained via Share in the Light.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: <React.Fragment>Added <ItemLink id={ITEMS.CORD_OF_MAIEV_PRIESTESS_OF_THE_MOON.id} /> statistic.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: 'Added Disc Priest 2 set bonus statistic.',
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-05-14'),
    changes: <React.Fragment>Renamed Missed penance hits to Wasted Penance bolts. Wasted Penance bolts now accounts for (combat log) latency. Fixed Glyph of the Sha's Shadowfiend not being counted towards Shadowfiend casts. Fixed healing increases (most notably the 15% from Velen's) not working with Disc priest spells.</React.Fragment>,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-06-05'),
    changes: <React.Fragment>Fixed issue where critical atonement healing was not being counted, fixed <ItemLink id={ITEMS.NERO_BAND_OF_PROMISES.id} /> being broken.</React.Fragment>,
    contributors: [Reglitch],
  },
  {
    date: new Date('2017-05-14'),
    changes: 'Added Discipline Priest spec. Currently includes basic statistics for Dead GCD time (should be fully operational), shared legendaries, missed Penance hits, cast efficiencies and the other build in tools.',
    contributors: [Zerotorescue],
  },
];
<|endoftext|>


<commit_before>import { mat4, vec3 } from "pex-math";
import { orbiter as createOrbiter } from "pex-cam";

const tmpMatrix = mat4.create();

export default function createCameraSystem() {
  const cameraSystem = {
    type: "camera-system",
  };

  cameraSystem.update = (entities) => {
    for (let entity of entities) {
      if (entity.camera && entity.orbiter) {
        const orbiter = entity.orbiter;
        const camera = entity.camera;
        //TODO: remove proxy and just use camera as component
        if (!orbiter._orbiter) {
          const proxyCamera = {
            viewMatrix: mat4.create(),
            position: entity.orbiter.position || camera.position,
            target: camera.target,
            set({ position }) {
              vec3.set(camera.position, position);
              vec3.set(entity.transform.position, position);
              entity.transform = {
                ...entity.transform,
              };
            },
          };
          orbiter._orbiter = createOrbiter({
            element: entity.orbiter.element || document.body, //TODO: element used to default to ctx.gl.canvas
            autoUpdate: false,
            camera: proxyCamera,
            position: entity.orbiter.position || [0, 0, 2.5], //TODO: orbiter position vs distance is confusing, shoudln't it come from camera anyway?
            distance: entity.orbiter.distance || 2,
          });
        } else {
          entity.orbiter._orbiter.updateCamera();
          mat4.lookAt(
            camera.viewMatrix,
            camera.position,
            camera.target,
            camera.up
          );

          mat4.set(camera.inverseViewMatrix, camera.viewMatrix);
          mat4.invert(camera.inverseViewMatrix);
        }
      }
    }
  };
  return cameraSystem;
}
<commit_msg>feat(systems/camera-system): defaul maxDistance to camera.far<commit_after>import { mat4, vec3 } from "pex-math";
import { orbiter as createOrbiter } from "pex-cam";

const tmpMatrix = mat4.create();

export default function createCameraSystem() {
  const cameraSystem = {
    type: "camera-system",
  };

  cameraSystem.update = (entities) => {
    for (let entity of entities) {
      if (entity.camera && entity.orbiter) {
        const orbiter = entity.orbiter;
        const camera = entity.camera;
        //TODO: remove proxy and just use camera as component
        if (!orbiter._orbiter) {
          const proxyCamera = {
            viewMatrix: mat4.create(),
            position: entity.orbiter.position || camera.position,
            target: camera.target,
            set({ position }) {
              vec3.set(camera.position, position);
              vec3.set(entity.transform.position, position);
              entity.transform = {
                ...entity.transform,
              };
            },
          };
          orbiter._orbiter = createOrbiter({
            element: entity.orbiter.element || document.body, //TODO: element used to default to ctx.gl.canvas
            autoUpdate: false,
            camera: proxyCamera,
            position: entity.orbiter.position || [0, 0, 2.5], //TODO: orbiter position vs distance is confusing, shoudln't it come from camera anyway?
            maxDistance: camera.far * 0.9,
          });
        } else {
          entity.orbiter._orbiter.updateCamera();
          mat4.lookAt(
            camera.viewMatrix,
            camera.position,
            camera.target,
            camera.up
          );

          mat4.set(camera.inverseViewMatrix, camera.viewMatrix);
          mat4.invert(camera.inverseViewMatrix);
        }
      }
    }
  };
  return cameraSystem;
}
<|endoftext|>


<commit_before>app.factory('ParseJSONSchedule', [
	'WarningList',
	'DataTypes',
	'UNIXTime',
	function(WarningList, DataTypes, UNIXTime) {
		return {
			parseJSONSchedule: function(options, data, filename) {
				var sessions = options.sessions,
					annotaitons = options.annotations,
					location_map = options.locations,
					submissions = options.submissions,
					warnings = options.warnings,
					schedule = options.schedule,
					timezone = options.timezone;

				_.each(data, function(day) {
					var date = day.date,
						stripped_date = date.replace("th", "").replace("st", "").replace("nd", "").replace("rd", "");
					_.each(day.slots, function(slot) {
						var time = slot.time,
							times = time.split("-"),
							start_time = stripped_date + " " + times[0].trim(),
							end_time = stripped_date + " " + times[1].trim();

						date_problem = !Date.parse(stripped_date);
						if(date_problem) {
							warnings.add(filename, "Could not parse date '" + date + "'. Try using the format MMM DD, YYYY (e.g. Apr 29, 2013)");
						} else {
							if(!Date.parse(start_time)) {
								warnings.add(filename, "Could not parse time '" + start_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
							}

							if(!Date.parse(end_time)) {
								warnings.add(filename, "Could not parse end time '" + end_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
							}
						}

						var start_tstamp = UNIXTime.getUnixTime(start_time, timezone),
							end_tstamp = UNIXTime.getUnixTime(end_time, timezone),
							start = start_tstamp.unix(),
							end = end_tstamp.unix(),
							offset = start_tstamp._offset;

						_.each(slot.sessions, function(session_info) {
							var unique_id, loc;

							if(_.isString(session_info)) {
								unique_id = session_info;
							} else {
								unique_id = session_info.session;
								loc = location_map[session_info.room];
							}
							var session = sessions[unique_id];

							if(session) {
								session.start = start;
								session.end = end;
								session.offset = offset;
								session.location = session.location || loc;

								schedule.push(session);
							} else {
								warnings.add(filename, "Could not find session '" + unique_id + "'");
							}
						});
					});
				});
			}
		};
	}
]);
/*

var read_json = require('./read_json').read_json,
	data_type = require('../data_types'),
	moment = require('moment-timezone'),
	my_date_offset = (new Date()).getTimezoneOffset() * 60,
	obj_clone = function(obj) {
		var new_obj = {}, key, val;
		for(key in obj) {
			if(obj.hasOwnProperty(key)) {
				val = obj[key];
				new_obj[key] = val;
			}
		}
		return new_obj;
	},
	_ = require('underscore');

function handle_json_data(data, schedule, options, fname) {
	var sessions = options.sessions,
		annotaitons = options.annotations,
		conference = options.conference,
		location_map = options.location_map,
		submissions = options.submissions,
		addWarning = options.addWarning,
		timezone = conference.timezone;

	_.each(data, function(day) {
		var date = day.date,
			stripped_date = date.replace("th", "").replace("st", "").replace("nd", "").replace("rd", "");
		_.each(day.slots, function(slot) {
			var time = slot.time,
				times = time.split("-"),
				start_time = stripped_date + " " + times[0].trim(),
				end_time = stripped_date + " " + times[1].trim();

			date_problem = !Date.parse(stripped_date);
			if(date_problem) {
				addWarning(fname, "Could not parse date '" + date + "'. Try using the format MMM DD, YYYY (e.g. Apr 29, 2013)");
			} else {
				if(!Date.parse(start_time)) {
					addWarning(fname, "Could not parse time '" + start_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
				}

				if(!Date.parse(end_time)) {
					addWarning(fname, "Could not parse end time '" + end_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
				}
			}

			var format = "MMMM D, YYYY HH:mm:ss";

			var start_tstamp = moment.tz(start_time, format, timezone),
				end_tstamp = moment.tz(end_time, format, timezone),
				start = start_tstamp.unix(),
				end = end_tstamp.unix(),
				offset = start_tstamp._offset;

			_.each(slot.sessions, function(session_info) {
				var unique_id = session_info.session,
					session = sessions[unique_id],
					session_info_clone = obj_clone(session),
					loc = location_map[session_info.room];

				session_info_clone.location = session_info_clone.location || loc;
				session_info_clone.start = start;
				session_info_clone.end = end;
				session_info_clone.offset = offset;

				schedule.push(new data_type.Event(session_info_clone));
			});
		});
	});
}

exports.read_schedule = function(filenames, options) {
	var schedule = [],
		json_data = _.map(filenames, function(fname) {
			var data = options.contents[fname];
			handle_json_data(data, schedule, options, fname);
		});

	return Promise	.all(json_data)
					.then(function() {
						return schedule;
					});
};

*/<commit_msg>adding json location missing warning<commit_after>app.factory('ParseJSONSchedule', [
	'WarningList',
	'DataTypes',
	'UNIXTime',
	function(WarningList, DataTypes, UNIXTime) {
		return {
			parseJSONSchedule: function(options, data, filename) {
				var sessions = options.sessions,
					annotaitons = options.annotations,
					location_map = options.locations,
					submissions = options.submissions,
					warnings = options.warnings,
					schedule = options.schedule,
					timezone = options.timezone;

				_.each(data, function(day) {
					var date = day.date,
						stripped_date = date.replace("th", "").replace("st", "").replace("nd", "").replace("rd", "");
					_.each(day.slots, function(slot) {
						var time = slot.time,
							times = time.split("-"),
							start_time = stripped_date + " " + times[0].trim(),
							end_time = stripped_date + " " + times[1].trim();

						date_problem = !Date.parse(stripped_date);
						if(date_problem) {
							warnings.add(filename, "Could not parse date '" + date + "'. Try using the format MMM DD, YYYY (e.g. Apr 29, 2013)");
						} else {
							if(!Date.parse(start_time)) {
								warnings.add(filename, "Could not parse time '" + start_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
							}

							if(!Date.parse(end_time)) {
								warnings.add(filename, "Could not parse end time '" + end_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
							}
						}

						var start_tstamp = UNIXTime.getUnixTime(start_time, timezone),
							end_tstamp = UNIXTime.getUnixTime(end_time, timezone),
							start = start_tstamp.unix(),
							end = end_tstamp.unix(),
							offset = start_tstamp._offset;

						_.each(slot.sessions, function(session_info) {
							var unique_id, loc;

							if(_.isString(session_info)) {
								unique_id = session_info;
							} else {
								unique_id = session_info.session;
								loc = location_map[session_info.room];

								if(!loc) {
									warnings.add(filename, "Could not find room '" + session_info.room + "' for session '" + unique_id + "'");
								}
							}
							var session = sessions[unique_id];

							if(session) {
								session.start = start;
								session.end = end;
								session.offset = offset;
								session.location = session.location || loc;

								schedule.push(session);
							} else {
								warnings.add(filename, "Could not find session '" + unique_id + "'");
							}
						});
					});
				});
			}
		};
	}
]);
/*

var read_json = require('./read_json').read_json,
	data_type = require('../data_types'),
	moment = require('moment-timezone'),
	my_date_offset = (new Date()).getTimezoneOffset() * 60,
	obj_clone = function(obj) {
		var new_obj = {}, key, val;
		for(key in obj) {
			if(obj.hasOwnProperty(key)) {
				val = obj[key];
				new_obj[key] = val;
			}
		}
		return new_obj;
	},
	_ = require('underscore');

function handle_json_data(data, schedule, options, fname) {
	var sessions = options.sessions,
		annotaitons = options.annotations,
		conference = options.conference,
		location_map = options.location_map,
		submissions = options.submissions,
		addWarning = options.addWarning,
		timezone = conference.timezone;

	_.each(data, function(day) {
		var date = day.date,
			stripped_date = date.replace("th", "").replace("st", "").replace("nd", "").replace("rd", "");
		_.each(day.slots, function(slot) {
			var time = slot.time,
				times = time.split("-"),
				start_time = stripped_date + " " + times[0].trim(),
				end_time = stripped_date + " " + times[1].trim();

			date_problem = !Date.parse(stripped_date);
			if(date_problem) {
				addWarning(fname, "Could not parse date '" + date + "'. Try using the format MMM DD, YYYY (e.g. Apr 29, 2013)");
			} else {
				if(!Date.parse(start_time)) {
					addWarning(fname, "Could not parse time '" + start_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
				}

				if(!Date.parse(end_time)) {
					addWarning(fname, "Could not parse end time '" + end_time + "'. Try using the format HH:MM AM/PM (e.g. 1:00 PM)");
				}
			}

			var format = "MMMM D, YYYY HH:mm:ss";

			var start_tstamp = moment.tz(start_time, format, timezone),
				end_tstamp = moment.tz(end_time, format, timezone),
				start = start_tstamp.unix(),
				end = end_tstamp.unix(),
				offset = start_tstamp._offset;

			_.each(slot.sessions, function(session_info) {
				var unique_id = session_info.session,
					session = sessions[unique_id],
					session_info_clone = obj_clone(session),
					loc = location_map[session_info.room];

				session_info_clone.location = session_info_clone.location || loc;
				session_info_clone.start = start;
				session_info_clone.end = end;
				session_info_clone.offset = offset;

				schedule.push(new data_type.Event(session_info_clone));
			});
		});
	});
}

exports.read_schedule = function(filenames, options) {
	var schedule = [],
		json_data = _.map(filenames, function(fname) {
			var data = options.contents[fname];
			handle_json_data(data, schedule, options, fname);
		});

	return Promise	.all(json_data)
					.then(function() {
						return schedule;
					});
};

*/<|endoftext|>


<commit_before>// Copyright Collab 2014

(function(root, factory)
{
    if (typeof define === 'function' && define.amd)
    {
        define(['wavesurfer'], factory);
    }
    else
    {
        root.WaveSurfer.Videojs = factory(root.WaveSurfer);
    }
}(this, function(WaveSurfer)
{
    /**
     * Use waveform for audio files in video.js player.
     */
    videojs.Waveform = videojs.Component.extend({

        /**
         * The constructor function for the class.
         *
         * @param options: Player options.
         * @param ready: Ready callback function.
         */
        init: function(player, options, ready)
        {
            // run base component initializing with new options.
            videojs.Component.call(this, player, options, ready);

            this.waveReady = false;

            // indicates the number of seconds that is considered
            // the boundary value for displaying milliseconds in the
            // time controls. An audio clip with a total length of
            // 2 seconds with a msDisplayMax of 3 will be displayed
            // as M:SS:MMM. Clips longer than msDisplayMax will be
            // displayed as M:SS or HH:MM:SS.
            this.msDisplayMax = 3;

            // customize controls
            if (this.player().options().autoplay)
            {
                this.player().bigPlayButton.hide();
            }
            if (this.player().options().controls)
            {
                this.player().controlBar.progressControl.hide();

                // prevent controlbar fadeout
                this.player().on('userinactive', function(event)
                {
                   this.player().userActive(true);
                });
            }

            // hide fullscreen control until it's supported by
            // wavesurfer.js
            this.player().controlBar.fullscreenToggle.hide();
            this.player().controlBar.volumeControl.el().style.marginRight = '15px';

            // waveform events
            this.surfer = Object.create(WaveSurfer);
            this.surfer.on('ready', this.onWaveReady.bind(this));
            this.surfer.on('error', this.onWaveError.bind(this));
            this.surfer.on('progress', this.onWaveProgress.bind(this));
            this.surfer.on('seek', this.onWaveSeek.bind(this));

            // player events
            this.player().on('firstplay', this.onFirstPlay.bind(this));
            this.player().on('play', this.onPlay.bind(this));
            this.player().on('pause', this.onPause.bind(this));
            this.player().on('volumechange', this.onVolumeChange.bind(this));
            this.player().on('timeupdate', this.onTimeUpdate.bind(this));
        },

        /**
         * Initializes the waveform.
         *
         * @param opts: Plugin options.
         */
        initialize: function(opts)
        {
            // set waveform element and dimensions
            opts.container = this.el();
            opts.height = this.player().height() - this.player().controlBar.height();

            // customize waveform appearance
            this.surfer.init(opts);
        },

        /**
         * Start loading waveform data.
         *
         * @param url: URL of audio file to load.
         */
        load: function(url)
        {
            this.surfer.load(url);
        },

        /**
         * Start or resume playback.
         */
        play: function()
        {
            this.surfer.play();
        },

        /**
         * Pauses playback.
         */
        pause: function()
        {
            this.surfer.pause();
        },

        /**
         * Remove the player and waveform.
         */
        destroy: function()
        {
            this.surfer.destroy();
            this.player().dispose();
        },

        /**
         * Set the current volume of the media.
         *
         * @param volume: The new volume level.
         */
        setVolume: function(volume)
        {
            this.surfer.setVolume(volume);
        },

        /**
         * Updates the current time.
         */
        setCurrentTime: function()
        {
            var duration = this.surfer.backend.getDuration();
            var currentTime = this.surfer.backend.getCurrentTime()
            var time = Math.min(currentTime, duration);

            // update control
            this.player().controlBar.currentTimeDisplay.el(
                ).children[0].innerHTML = this.formatTime(
                time, duration);
        },

        /**
         * Updates the duration time.
         */
        setDuration: function()
        {
            var duration = this.surfer.backend.getDuration();

            // update control
            this.player().controlBar.durationDisplay.el(
                ).children[0].innerHTML = this.formatTime(
                duration, duration);
        },

        /**
         * Audio is loaded, decoded and the waveform is drawn.
         */
        onWaveReady: function()
        {
            this.waveReady = true;

            // update duration
            this.setDuration();

            // make sure the size of time controls is large enough to
            // display milliseconds
            if (this.surfer.backend.getDuration() < this.msDisplayMax)
            {
                this.player().controlBar.durationDisplay.el().style.width =
                    this.player().controlBar.currentTimeDisplay.el().style.width = '6em';
            }

            // remove loading spinner
            this.player().removeChild(this.player().loadingSpinner);

            // auto-play when ready
            this.play();
        },

        /**
         * Fires continuously during audio playback.
         *
         * @param percent: Percentage played so far.
         */
        onWaveProgress: function(percent)
        {
            // completed playback
            if (percent >= 1)
            {
                // check if player isn't paused already
                if (!this.player().paused())
                {
                    // check if loop is enabled
                    if (this.player().options().loop)
                    {
                        // stop and play
                        this.surfer.stop();
                        this.play();
                    }
                    else
                    {
                        // pause player
                        this.player().pause();
                    }
                }
            }
            else
            {
                this.setCurrentTime();
            }
        },

        /**
         * Fires during seeking of the waveform.
         */
        onWaveSeek: function()
        {
            this.setCurrentTime();
        },

        /**
         * Fired the first time a clip is played.
         */
        onFirstPlay: function()
        {
            var options = this.options().options;

            // init waveform
            this.initialize(options);

            // start loading
            if (options.src != undefined)
            {
                this.load(options.src);
            }
        },

        /**
         * Fired whenever the media in the player begins or resumes playback.
         */
        onPlay: function()
        {
            // don't start playing until waveform's ready
            if (this.waveReady)
            {
                this.play();
            }
        },

        /**
         * Fired whenever the media in the player has been paused.
         */
        onPause: function()
        {
            this.pause();
        },

        /**
         * Fired when the volume in the player changes.
         */
        onVolumeChange: function()
        {
            var volume = this.player().volume();
            if (this.player().muted())
            {
                // muted volume
                volume = 0;
            }

            this.setVolume(volume);
        },

        /**
         * Fired when the current playback position has changed.
         */
        onTimeUpdate: function()
        {
            this.setCurrentTime();
        },

        /**
         * Waveform error.
         */
        onWaveError: function(error)
        {
            console.warn(error);
        },

        /**
        * Format seconds as a time string, H:MM:SS, M:SS or M:SS:MMM.
        *
        * Supplying a guide (in seconds) will force a number of leading zeros
        * to cover the length of the guide.
        *
        * @param {Number} seconds Number of seconds to be turned into a string
        * @param {Number} guide Number (in seconds) to model the string after
        * @return {String} Time formatted as H:MM:SS, M:SS or M:SS:MMM.
        */
        formatTime: function(seconds, guide)
        {
            // Default to using seconds as guide
            guide = guide || seconds;
            var s = Math.floor(seconds % 60),
                m = Math.floor(seconds / 60 % 60),
                h = Math.floor(seconds / 3600),
                gm = Math.floor(guide / 60 % 60),
                gh = Math.floor(guide / 3600),
                ms = Math.floor((seconds - s) * 1000);

            // handle invalid times
            if (isNaN(seconds) || seconds === Infinity)
            {
                // '-' is false for all relational operators (e.g. <, >=) so this
                // setting will add the minimum number of fields specified by the
                // guide
                h = m = s = ms = '-';
            }

            // Check if we need to show milliseconds
            if (guide > 0 && guide < this.msDisplayMax)
            {
                if (ms < 100)
                {
                    if (ms < 10)
                    {
                        ms = '00' + ms;
                    }
                    else
                    {
                        ms = '0' + ms;
                    }
                }
                ms = ":" + ms;
            }
            else
            {
                ms = '';
            }

            // Check if we need to show hours
            h = (h > 0 || gh > 0) ? h + ':' : '';

            // If hours are showing, we may need to add a leading zero.
            // Always show at least one digit of minutes.
            m = (((h || gm >= 10) && m < 10) ? '0' + m : m) + ':';

            // Check if leading zero is need for seconds
            s = ((s < 10) ? '0' + s : s);

            return h + m + s + ms;
        }

    });

    var createWaveform = function()
    {
        var props = {
            className: 'vjs-waveform',
            tabIndex: 0
        };
        return videojs.Component.prototype.createEl(null, props);
    };

    function wavesurferPlugin(options)
    {
        // create new waveform
        this.waveform = new videojs.Waveform(this,
        {
            'el': createWaveform(),
            'options': options
        });

        // add waveform to dom
        this.el().appendChild(this.waveform.el());
    };

    // register the plugin
    videojs.plugin('wavesurfer', wavesurferPlugin);

    return wavesurferPlugin;

}));
<commit_msg>add blob/file support for src option<commit_after>// Copyright Collab 2014

(function(root, factory)
{
    if (typeof define === 'function' && define.amd)
    {
        define(['wavesurfer'], factory);
    }
    else
    {
        root.WaveSurfer.Videojs = factory(root.WaveSurfer);
    }
}(this, function(WaveSurfer)
{
    /**
     * Use waveform for audio files in video.js player.
     */
    videojs.Waveform = videojs.Component.extend({

        /**
         * The constructor function for the class.
         *
         * @param options: Player options.
         * @param ready: Ready callback function.
         */
        init: function(player, options, ready)
        {
            // run base component initializing with new options.
            videojs.Component.call(this, player, options, ready);

            this.waveReady = false;

            // indicates the number of seconds that is considered
            // the boundary value for displaying milliseconds in the
            // time controls. An audio clip with a total length of
            // 2 seconds with a msDisplayMax of 3 will be displayed
            // as M:SS:MMM. Clips longer than msDisplayMax will be
            // displayed as M:SS or HH:MM:SS.
            this.msDisplayMax = 3;

            // customize controls
            if (this.player().options().autoplay)
            {
                this.player().bigPlayButton.hide();
            }
            if (this.player().options().controls)
            {
                this.player().controlBar.progressControl.hide();

                // prevent controlbar fadeout
                this.player().on('userinactive', function(event)
                {
                   this.player().userActive(true);
                });
            }

            // hide fullscreen control until it's supported by
            // wavesurfer.js
            this.player().controlBar.fullscreenToggle.hide();
            this.player().controlBar.volumeControl.el().style.marginRight = '15px';

            // waveform events
            this.surfer = Object.create(WaveSurfer);
            this.surfer.on('ready', this.onWaveReady.bind(this));
            this.surfer.on('error', this.onWaveError.bind(this));
            this.surfer.on('progress', this.onWaveProgress.bind(this));
            this.surfer.on('seek', this.onWaveSeek.bind(this));

            // player events
            this.player().on('firstplay', this.onFirstPlay.bind(this));
            this.player().on('play', this.onPlay.bind(this));
            this.player().on('pause', this.onPause.bind(this));
            this.player().on('volumechange', this.onVolumeChange.bind(this));
            this.player().on('timeupdate', this.onTimeUpdate.bind(this));
        },

        /**
         * Initializes the waveform.
         *
         * @param opts: Plugin options.
         */
        initialize: function(opts)
        {
            // set waveform element and dimensions
            opts.container = this.el();
            opts.height = this.player().height() - this.player().controlBar.height();

            // customize waveform appearance
            this.surfer.init(opts);
        },

        /**
         * Start loading waveform data.
         *
         * @param url: Either the URL of the audio file, or a Blob or File
         *             object.
         */
        load: function(url)
        {
            if (url instanceof Blob || url instanceof File)
            {
                this.surfer.loadArrayBuffer(url);
            }
            else
            {
                this.surfer.load(url);
            }
        },

        /**
         * Start or resume playback.
         */
        play: function()
        {
            this.surfer.play();
        },

        /**
         * Pauses playback.
         */
        pause: function()
        {
            this.surfer.pause();
        },

        /**
         * Remove the player and waveform.
         */
        destroy: function()
        {
            this.surfer.destroy();
            this.player().dispose();
        },

        /**
         * Set the current volume of the media.
         *
         * @param volume: The new volume level.
         */
        setVolume: function(volume)
        {
            this.surfer.setVolume(volume);
        },

        /**
         * Updates the current time.
         */
        setCurrentTime: function()
        {
            var duration = this.surfer.backend.getDuration();
            var currentTime = this.surfer.backend.getCurrentTime()
            var time = Math.min(currentTime, duration);

            // update control
            this.player().controlBar.currentTimeDisplay.el(
                ).children[0].innerHTML = this.formatTime(
                time, duration);
        },

        /**
         * Updates the duration time.
         */
        setDuration: function()
        {
            var duration = this.surfer.backend.getDuration();

            // update control
            this.player().controlBar.durationDisplay.el(
                ).children[0].innerHTML = this.formatTime(
                duration, duration);
        },

        /**
         * Audio is loaded, decoded and the waveform is drawn.
         */
        onWaveReady: function()
        {
            this.waveReady = true;

            // update duration
            this.setDuration();

            // make sure the size of time controls is large enough to
            // display milliseconds
            if (this.surfer.backend.getDuration() < this.msDisplayMax)
            {
                this.player().controlBar.durationDisplay.el().style.width =
                    this.player().controlBar.currentTimeDisplay.el().style.width = '6em';
            }

            // remove loading spinner
            this.player().removeChild(this.player().loadingSpinner);

            // auto-play when ready
            this.play();
        },

        /**
         * Fires continuously during audio playback.
         *
         * @param percent: Percentage played so far.
         */
        onWaveProgress: function(percent)
        {
            // completed playback
            if (percent >= 1)
            {
                // check if player isn't paused already
                if (!this.player().paused())
                {
                    // check if loop is enabled
                    if (this.player().options().loop)
                    {
                        // stop and play
                        this.surfer.stop();
                        this.play();
                    }
                    else
                    {
                        // pause player
                        this.player().pause();
                    }
                }
            }
            else
            {
                this.setCurrentTime();
            }
        },

        /**
         * Fires during seeking of the waveform.
         */
        onWaveSeek: function()
        {
            this.setCurrentTime();
        },

        /**
         * Fired the first time a clip is played.
         */
        onFirstPlay: function()
        {
            var options = this.options().options;

            // init waveform
            this.initialize(options);

            // start loading
            if (options.src != undefined)
            {
                this.load(options.src);
            }
        },

        /**
         * Fired whenever the media in the player begins or resumes playback.
         */
        onPlay: function()
        {
            // don't start playing until waveform's ready
            if (this.waveReady)
            {
                this.play();
            }
        },

        /**
         * Fired whenever the media in the player has been paused.
         */
        onPause: function()
        {
            this.pause();
        },

        /**
         * Fired when the volume in the player changes.
         */
        onVolumeChange: function()
        {
            var volume = this.player().volume();
            if (this.player().muted())
            {
                // muted volume
                volume = 0;
            }

            this.setVolume(volume);
        },

        /**
         * Fired when the current playback position has changed.
         */
        onTimeUpdate: function()
        {
            this.setCurrentTime();
        },

        /**
         * Waveform error.
         */
        onWaveError: function(error)
        {
            console.warn(error);
        },

        /**
        * Format seconds as a time string, H:MM:SS, M:SS or M:SS:MMM.
        *
        * Supplying a guide (in seconds) will force a number of leading zeros
        * to cover the length of the guide.
        *
        * @param {Number} seconds Number of seconds to be turned into a string
        * @param {Number} guide Number (in seconds) to model the string after
        * @return {String} Time formatted as H:MM:SS, M:SS or M:SS:MMM.
        */
        formatTime: function(seconds, guide)
        {
            // Default to using seconds as guide
            guide = guide || seconds;
            var s = Math.floor(seconds % 60),
                m = Math.floor(seconds / 60 % 60),
                h = Math.floor(seconds / 3600),
                gm = Math.floor(guide / 60 % 60),
                gh = Math.floor(guide / 3600),
                ms = Math.floor((seconds - s) * 1000);

            // handle invalid times
            if (isNaN(seconds) || seconds === Infinity)
            {
                // '-' is false for all relational operators (e.g. <, >=) so this
                // setting will add the minimum number of fields specified by the
                // guide
                h = m = s = ms = '-';
            }

            // Check if we need to show milliseconds
            if (guide > 0 && guide < this.msDisplayMax)
            {
                if (ms < 100)
                {
                    if (ms < 10)
                    {
                        ms = '00' + ms;
                    }
                    else
                    {
                        ms = '0' + ms;
                    }
                }
                ms = ":" + ms;
            }
            else
            {
                ms = '';
            }

            // Check if we need to show hours
            h = (h > 0 || gh > 0) ? h + ':' : '';

            // If hours are showing, we may need to add a leading zero.
            // Always show at least one digit of minutes.
            m = (((h || gm >= 10) && m < 10) ? '0' + m : m) + ':';

            // Check if leading zero is need for seconds
            s = ((s < 10) ? '0' + s : s);

            return h + m + s + ms;
        }

    });

    var createWaveform = function()
    {
        var props = {
            className: 'vjs-waveform',
            tabIndex: 0
        };
        return videojs.Component.prototype.createEl(null, props);
    };

    function wavesurferPlugin(options)
    {
        // create new waveform
        this.waveform = new videojs.Waveform(this,
        {
            'el': createWaveform(),
            'options': options
        });

        // add waveform to dom
        this.el().appendChild(this.waveform.el());
    };

    // register the plugin
    videojs.plugin('wavesurfer', wavesurferPlugin);

    return wavesurferPlugin;

}));
<|endoftext|>