Source: src/tatLogDiver/tatLogDiver-Init.js

import * as Util from '../util/util.js';
import * as TimeSeries from '../timeSeries/timeSeries.js';
import * as Simulator from '../simulator/simulator.js';
import {HJN} from './tatLogDiver-HJN.js';
import {Copyright} from "./tatLogDiver-Copyright.js";
import Graph from './tatLogDiver-Graph.js';
import Plot  from './tatLogDiver-Plot.js';
import MenuConfigDetailGraph from './tatLogDiver-MenuConfigDetailGraph.js';


/* *****1*********2*********3*********4*********5*********6*********7******* */
/**
 * HTMLから呼び出すAPI
 * 
 * @memberof HJN
 * @class Init
 * @param {string}
 *            [chartName=HJN.chartName="chart"] グラフを作成するHTMLタグ名
 * @return {ETAT} 終了時刻のTAT(応答時間)時系列データ
 * @example <!doctype html> <html> <head> <meta charset="UTF-8"> <link
 *          type="text/css" rel="stylesheet" href="../libs/dygraph.css"> <link
 *          type="text/css" rel="stylesheet" href="./tatLogDiver.css"> </head>
 *          <body> <div id="hjn_chart"></div> <script src="../libs/dygraph.js"></script>
 *          <script src="../libs/extras/synchronizer.js"></script> <script
 *          type="module"> import {HJN_init_ChartRegist} from
 *          "./tatLogDiver-Init.js";
 *          window.addEventListener("DOMContentLoaded",function(eve){
 *          Bundle("chart"); // チャートを作成する }); </script> </body> </html>
 */
export default function Init(chartName){ // #70
    // グローバル登録
    HJN.Util = Util; // #95
	// 引数1 :デフォルトHJN.chartName("chart")
	HJN.chartName = chartName = chartName || HJN.chartName;
	// タイトルを設定する #57
	document.title = "tat log diver " + Copyright.Version;
	// htmlを作成する #52
	var html_chart = document.getElementById("hjn_" + chartName) || document.body;
	html_chart.innerHTML = ''
		+ '<div id="' + chartName + '"></div>'
		+ '<div id="' + chartName + 'D"></div>'
	// CONCデータ表示領域 #78
		+ '<div class="lineViewer hjnDraggableBox hjnCode" >' // #79
		+ '<div id="lineViewer" readonly>logdata</div></div>';
	// 手前にメニュ-用htmlを作成する #52
	var html_nav = document.createElement('nav');
	html_nav.innerHTML = ''
		+ '<header>'
		// シミュレータ JSON Edit画面 領域 #53
		+ '<div id="Simulator" class="hjnDraggableBox" style="visibility:hidden"></div>'
		// ハンバーガーメニュー 領域
		+ '<div class="hjnBurgerTray">'
			// ステータスバー(ログ表示)領域
		+	'<div class="statusbar hjnDraggableBox">'
		+	'<iframe id="fileInfo" style="height:100%;"></iframe></div>' // #79
			// ×ボタン
		+	'<input id="hjnBoxBuger" type="checkbox" class="hjnBurger hjnResize" checked="checked" />'
		+	 '<label for="hjnBoxBuger" class="hjnCtrlBox"><span></span></label>'
		// グラフZoomリセットボタン #78
		+	 '<input id="chartZoom" type="buttom" class="hjnBoxSwitch hjnResize" '
		+	 'onClick="HJN.init.ResetStyle();" />'
		+		'<label for="chartZoom" class="hjnCtrlBox"><span></span></label>'
			// メニュー上部タイトル
		+	'<div class="hjnBurgerTitle">'
				// メニューオーバレイモード変更 ボタン
		+	 '<input id="hjnBoxPlaceOn" type="checkbox" class="hjnBoxSwitch hjnResize" />'
		+		'<label for="hjnBoxPlaceOn" class="hjnCtrlBox"><span></span></label>'
				// メニュー上部テキスト 領域
		+	 '<p>'
					// ツール名称&JSDocリンク
		+		'<a class="hjnLabel4Input" href="../jsdoc/index.html" '
		+				 'target=”_hirosejnJSDoc3”>TAT log diver</a><BR>'
		+	 '</p>'
		+	'</div>'
		// メニュー画面本体(左右開閉ラッパー)
		+	'<div class="hjnBurgerWrap">'
			// アコーディオンメニュー
		+	 '<div class="hjnAccordion">'
		+		'<div id="' + chartName + '_menu"></div>'
		+		'<div id="' + chartName + 'D_menu"></div>'
		+	 '</div>'
		+	'</div>'
		+ '</div>'
		+ '</header>';
	html_chart.parentNode.insertBefore(html_nav, html_chart);
	HJN.init.ResetStyle(true); // #79

	var dropFieldName = chartName;	// ファイルドロップを受け付けるタグ名
	Util.Logger.ShowLogTextInit(); // 処理時間計測の初期化

	// コンフィグプロパティを初期化する #74
	// HJN.Config = HJN.Config || Util.Config;
	TimeSeries.MenuConfigFile.config();
	TimeSeries.MenuConfigFilter.config();
	Simulator.MenuConfig.config();
	MenuConfigDetailGraph.config();

	// グラフのインスタンスを作成し初期化する
	HJN.chart = new Graph(chartName, "HJN.chart");
	HJN.chart.init();
	HJN.chartD = new Graph(chartName + "D", "HJN.chartD");
	HJN.chartD.init();
	// ドロップフィールドに、処理を登録する(注:dygraphはイベントリスナーを登録しないとクリック時にエラーが出る)
	Init.DropField(dropFieldName);
	Init.DropField(dropFieldName+ "D");

	// 初期表示データを自動生成する // #53
	Util.Config.GetConfig("Simulator").getFunctionByKey("S_SIMU")(); // #53

	// イベントハンドラを登録する
	Util.Element.enableDraggableClass();
}

/**
 * スタイルを初期設定する(Reset zoomボタンからも呼ばれる)
 * 
 * @memberof HJN.init
 * @param {Boolean}
 *            [isInit=false] リセット時true:初期設定値も再設定する
 *            シミュレーション条件JSONテキスト、もしくはサンプルJSON番号
 * 
 */
HJN.init.ResetStyle = Init.ResetStyle = function(isInit){ // #79
	// 指定クラス名が設定された要素にスタイルを設定する
	var elements = document.getElementsByClassName("lineViewer");
	for(var i = 0; i < elements.length; i++){
		Util.Element.SetStyles(elements[i],
				{ bottom: 0, left: "", top: "", width: "70%", height: "10vh"});
	}
	elements = document.getElementsByClassName("statusbar");
	for(var i = 0; i < elements.length; i++){
		Util.Element.SetStyles(elements[i],
				{ left: "10px", top: 0, width: "155px", height: "40px"});
	}
	Util.Element.SetStyles(document.getElementById("Simulator"),
				{ left: 0, top: 0, width: "190px", height: 0});
	// 別途初期設定される値を再設定する
	if (!isInit) {
		HJN.chart.graph.resetZoom();
		HJN.chartD.graph.resetZoom();
	}
}
/**
 * データを自動生成し表示する
 * 
 * @memberof HJN.init
 * @param {String|Number}
 *            [json = Simulator.virtualSystemByJson.GetJsonConfig(0)]
 *            シミュレーション条件JSONテキスト、もしくはサンプルJSON番号
 */
export function CreateSampleTatLogAndChartShow(json){ // #53
	var jsonText;
	if (typeof(json) === "number") { // #53
		jsonText = Simulator.virtualSystemByJson.GetJsonConfig(json);
	} else{
		jsonText = json || Simulator.virtualSystemByJson.GetJsonConfig(0);
	}
	// JSON Editorを更新する
	document.getElementById("SimulatorEditor").value = jsonText;
	// 初期表示データを自動生成する
	HJN.chart.eTatOriginal = Simulator.virtualSystemByJson.Execute(jsonText);
	// データを表示する
	Init.ChartShow(HJN.chart.eTatOriginal);
}

/**
 * 終了時刻とtatの配列をグラフ表示する(Menuイベントから呼び出される)
 * 
 * @memberof HJN.init
 * @param {ETAT}
 *            HJN.chart.eTatOriginal 終了時刻とtatを含む配列
 */
HJN.init.ChartShow = Init.ChartShow = function(eTatOriginal){
	// フィルタしたeTatを取得する #34
	var eTat = HJN.chart.fileParser.createFilter().filter(eTatOriginal);

	// グラフを初期表示する
	HJN.Plot.List = []; // #53
	// 上段
	if (eTat.length === 0) eTat = [{x:0, y:0}]; // #72
	var tat = new TimeSeries.Tat(eTat); // #75
	HJN.chart.setSeriesSet(tat);
	HJN.chart.update();
	showLogForUpperGraph("Simulator"); // #79

	// 下段(非同期)
	Util.setZeroTimeout( function(){
		HJN.chartD.update(Init.ChartRegistDetail(HJN.chart.cTps));
		HJN.chart.showBalloon();	// 上段のBalloonを描画する
		if (HJN.chartD.eTat){
			showLogForLowerGraph("Simulator"); // #79
		} else { // #72
			Util.Logger.ShowLogText("<mark>表示データがありません</mark>", "msg");
		}
		// 上下段のマウス操作同期設定 #49
		var sync = Dygraph.synchronize(
				 [ HJN.chart.graph, HJN.chartD.graph ],
				 {selection: true, zoom: false});
	});
}
function showLogForUpperGraph(fileName){
	// 上段グラフの処理時間をログに出力
	// Util.Logger.ShowLogText("+ ", "elaps");
}
function showLogForLowerGraph(fileName){
	// 下段グラフの処理時間をログに出力
	// Util.Logger.ShowLogText("", "elaps", true);

	// ファイル名、取込レコード数のログ編集
	var text = "<mark>" + /* fileName + */ " ["
			+ (HJN.chart.eTat.length.toString() + "")
				.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') // 整数文字列のカンマ編集
			+ "]行</mark> ";
	// 下段の表示レコード数をログに出力
	if (HJN.chartD.eTat){
		text += "下段["
				+ (HJN.chartD.eTat.length + "")
					.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') // 整数文字列のカンマ編集
				+ "]行";
	}else{
		text = "" +
				"表示データなし 要確認:Charset, delimiter, Line feed code"
	}
	// ログ出力
	Util.Logger.ShowLogText(text, "msg");
}

/**
 * HTMLタグに、CSVファイルのドロップを受付けイベントを登録する
 * 
 * @memberof HJN.init
 * @param {string}
 *            dropFieldName ファイルのドロップイベントを受けるフィールド名
 */
Init.DropField = function (dropFieldName) {
	// 第一引数で指定された名前の ID 属性のエレメントを取得する
	var element = document.getElementById(dropFieldName);

	// ドラッグ操作中に実行されるイベント(マウスカーソルが要素内に滞在中)
	element.addEventListener("dragover" , function (e){
		e.preventDefault();	// ドロップを許可し受け入れを表明
	});

	// ドロップ時に実行されるイベント
	element.addEventListener("drop", function (e){
			var data_transfer = e.dataTransfer;		// DataTransfer オブジェクトを取得する
			if(!data_transfer.types) return;		// ファイルのコンテンツタイプを取得できたことを確認する
			var files = data_transfer.files;	// ファイルのリストを取得する
			Init.FileReader(files);
			e.preventDefault();		// デフォルトのドロップ機能を無効化
	});
};

/**
 * 指定されたファイルを読込んで処理する
 * 
 * @memberof HJN.init
 * @param {Object}
 *            files ファイルハンドラ
 */
HJN.init.FileReader = Init.FileReader = function (files){  // #15
	for(var i = 0; i < files.length; i++){	// データを順番に取得する
		try{
			// ファイルを取得する
			var file = files[i];
			// ログ出力用にファイル名、ファイルサイズ、ファイル更新日時を編集する
			var textArray = "<mark><b>" + file.name + "</b></mark><BR>"
						+ (file.size + "")
							.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') // 整数文字列のカンマ編集
						+ "Kbyte<BR>"
						+ file.lastModifiedDate.toLocaleString() + "<BR>";
			// ファイルの読み込みに成功したら、その内容をドロップエリアに追記して表示する
			var reader = new FileReader();
			reader.onloadend = funcOnloadend.bind(this, files[i], i);
			// ファイルにArrayBufferで参照を取得する(loadイベントを起こす)
			reader.readAsArrayBuffer(files[i]);
		}catch(e){
			// 第一引数のテキストアレイの内容を#fileInfoのiframeに表示する
			var msg = "The " + i + "th dropped object is not a file";
			Util.Logger.ShowText( ["<mark>"+msg+"</mark>"] );
			console.error("[%o]%o",msg,e );
		}
	}

	// 内部関数:ファイルを読み込みグラフを表示する(指定ファイルを読み込んだ後に呼び出される) #23
	function funcOnloadend(file, i, evt) {
		if (evt.target.readyState === FileReader.DONE) { // DONE == 2
			var filesIdx = HJN.files.length;
			// ファイルの先頭2行をログ表示する
			HJN.filesArrayBuffer[filesIdx] = evt.target.result;
			Util.Logger.ResetTimestamp();			  // 情報表示 : 初期化
			Util.Logger.ShowLogText(textArray, "msg");  // 情報表示:ドロップファイル情報
			// 指定ファイルを読み込む
			// CSVファイルを上段用eTatに展開する[{x:, y:,pos:,len:},...] 全件展開する
			if (i === 0 && HJN.chart.fileParser.isNewETAT()){
				// 新規モードかつ、同時複数ファイル指定時の最初のファイルのとき、新たに作成する
				HJN.files = [file];
				HJN.chart.eTatOriginal = getTatLogArray(HJN.filesArrayBuffer, filesIdx);
			} else { // 2件目以降のファイルのとき、もしくは、追加モード"ADDDATA"のとき、追加する
				HJN.files.push(file);
				HJN.chart.eTatOriginal = HJN.chart.eTatOriginal.concat(
						getTatLogArray(HJN.filesArrayBuffer, filesIdx));
			}

			// 全ファイルを読み込んだらグラフを描画する
			if (HJN.files[HJN.files.length - 1] === file){ // 指定ファイル群の最後のファイルを処理しているとき
				// フィルタしたeTatを取得する #34
				var eTat = HJN.chart.fileParser.createFilter().filter(HJN.chart.eTatOriginal);

				// 上段グラフを描画する( eTatから上段用 時系列分析データ(seriesSet)を展開する)
				if (eTat.length === 0) eTat = [{x:0, y:0}]; // #72
				var tat = new TimeSeries.Tat(eTat); // #75
				HJN.chart.setSeriesSet(tat);
				HJN.chart.update();
				showLogForUpperGraph(HJN.files[0].name); // #79

				// 下段用データの展開とグラフ描画(非同期処理)
				HJN.Plot.List = [];
				Util.setZeroTimeout(function(){
					if (HJN.chart.cTps) {
						// 下段グラフを描画する(下段用 時系列分析データ(seriesSet)を展開する)
						HJN.chartD.update(Init.ChartRegistDetail(HJN.chart.cTps));
						// 上段のBalloonを描画する(上段update時にはplots登録されていないので、ここで処理)
						HJN.chart.showBalloon();
						showLogForLowerGraph(HJN.files[0].name); // #79
					} else { // #72
						Util.Logger.ShowLogText("<mark>表示データがありません</mark>", "msg");
					}
				});
			}
		}
	}

	// 内部関数: 指定ファイルの先頭n行を、改行文字<BR> のテキストに変換してリターンする
	function topLines(file, n) {
		var fileInfo = "",
			line;
		try{	// 先頭からnレコード取得
			var getterOfLine = HJN.chart.fileParser.createGetterOfLine(file);
			for (var i = 0; i < n; i++) {
				line = getterOfLine.next();
				fileInfo += line.str + "<BR>";
			}
		}catch (e) {
			alert("[Init.DropField 改行コードの無いファイルは扱えません]");
			console.error(e);
		}
		return fileInfo;
	}

	// 内部関数: CSVファイルを読み込み、TatLog用アレイ[{x:日時, y:値, pos:レコード開始位置,
	// len:レコード長},...]に展開する
	function getTatLogArray(files, idx) { // arg0:csvファイルのファイルパス
		Util.Logger.ShowLogText("----- read file -----------------------------","calc");
		var file = files[idx], // #23
			eTat = [],
			xy = {date: 0, value: 0, isError: false },
			i = 0,  // timelog用
			getterOfLine = HJN.chart.fileParser.createGetterOfLine(file),
			getterOfXY = HJN.chart.fileParser.createGetterOfXY(),
			isDataType_TatStart = (Util.Config.File.getConfig("DATATYPE") == "DATATYPE_TATSTART"),
			isDataType_StartEnd = (Util.Config.File.getConfig("DATATYPE") == "DATATYPE_START_END"), // #89
			line = getterOfLine.next();	 // 先頭行の初期処理
		while (!line.isEoF) {				// 以降最終行まで処理する
			try {
				Util.Logger.ByInterval(i++, line); // 一定時刻毎に進捗を出力する
				xy = getterOfXY.parse(line);
				if(!xy.isError){
					if (isDataType_TatStart){ // #81 DATATYPE_TATSTART
						eTat.push( {x: (xy.x + xy.y), y: xy.y, fileIdx: idx, // #93
							pos: line.pos, len: line.array.byteLength, sTatIdx: 0} );
					} else { // DATATYPE_TATEND
						eTat.push( {x: xy.x, y: xy.y, fileIdx: idx, // #23
							pos: line.pos, len: line.array.byteLength, sTatIdx: 0} );
					}
				}
				line = getterOfLine.next(); // #24
			} catch (e) {	/* 改行だけレコードをスキップ */
				console.error("err: %o",e); // #93
			}
		}
		Util.Logger.ShowLogText("[0:file readed & got eTat]---------------","calc");
		return eTat;
	}
};


/**
 * 詳細グラフ用機能: 表示対象期間のcTpsから、eTps範囲を取得し、詳細Seriesを生成する。併せてPlotsを登録する。
 * 
 * @memberof HJN.init
 * @param {xMs}
 *            cTps 日時(ミリ秒単位)
 * @return {seriesSet} dygraph用時系列データ配列
 */
Init.ChartRegistDetail = function(cTps){
	// CTPSの最大値となるplotを取得する
	var maxY =Number.MIN_VALUE,
		maxYIdx = -1;
	cTps.forEach(function(c, i){
		if (maxY < c.y){
			maxY = c.y;
			maxYIdx = i;
		}
	});
	if(0 <= maxYIdx){	// #26
		// 秒単位より大きいとき、最大値を含む時刻(秒)に補正する #38
		var x = cTps[maxYIdx].x;
		if(HJN.chart.cTpsUnit.unit >= 1000){
			var cTpsIdx = HJN.chart.conc.findIndex(function(e,i){return e.y === cTps[maxYIdx].y;});
			x = HJN.chart.conc[cTpsIdx].x;
		}
		// slider rangeに、下段の表示時刻を設定する
		HJN.init.SetDetailDateTime(x);
		// eTpsの範囲を取得し、詳細用seriesSet(HJN.chartD.seriesSet)を設定する
		var tat = new TimeSeries.Tat(HJN.init.GetSliderRangedEtat()); // #75
		HJN.chartD.setSeriesSet(tat);
		// plotsアイコン用 HJN.Plot.Listに、下段表示したplotを登録する
		HJN.Plot.Add(HJN.Tat.CTPS.N, cTps[maxYIdx].x, cTps[maxYIdx].y);
	}
	Util.Logger.ShowLogText("[6:Plot added] " + HJN.Plot.List.length + " plots","calc");

	return HJN.chartD.seriesSet;
};
/**
 * 詳細グラフ用機能: sliderRangeで指定された範囲のeTatを返却する(グラフの点クリックイベント時に呼び出される)
 * 
 * @memberof HJN.init
 * @return {ETAT} 詳細グラフ用eTat
 */
HJN.init.GetSliderRangedEtat = function() {
	// 指定時刻(dt ± range)を取得し、HJNグローバル変数に退避する #27
	var rangeTagPlus  = Util.Config.DetailGraph.getConfig("D_RANGE_PLUS");
	var rangeTagMinus = Util.Config.DetailGraph.getConfig("D_RANGE_MINUS");
	var rangeTagUnit  = Util.Config.DetailGraph.getConfig("D_UNIT"); // #48
	var rangeCycle = HJN.chart.cTpsUnit.unit / 1000; // 変動する #38

	rangeTagPlus = rangeTagPlus  ? +rangeTagPlus : 1 + rangeCycle;	 // 幅(秒)
	rangeTagMinus = rangeTagMinus ? +rangeTagMinus : rangeCycle;	 // 幅(秒)
	rangeTagUnit = rangeTagUnit  ? +rangeTagUnit : TimeSeries.Tat.CYCLE; // #48

	Util.Config.DetailGraph.setValueByKey("D_RANGE_PLUS",rangeTagPlus);
	Util.Config.DetailGraph.setValueByKey("D_RANGE_MINUS",rangeTagMinus);
	Util.Config.DetailGraph.setValueByKey("D_UNIT",rangeTagUnit);

	var detailDateTime = Util.Config.DetailGraph.getValueByKey("D_TIME");
	var eTatDetail = (new TimeSeries.ETat(HJN.chart.eTat)).sliceByRangeUnit(detailDateTime,
				rangeTagMinus, rangeTagPlus, rangeTagUnit); // #75

	Util.Logger.ShowLogText("[0:HJN.init.GetSliderRangedEtat] ","calc");
	return eTatDetail;  // 詳細グラフ用eTatを返却する
};
/**
 * 詳細グラフ用機能: 指定日時を秒単位に丸めて、FORMのslider Rangeに設定する(Plotから呼び出される)
 * 
 * @memberof HJN.init
 * @param {xMs}
 *            date 日時(ミリ秒単位)
 */
HJN.init.SetDetailDateTime=function(date) {
	Util.Config.DetailGraph.setValueByKey("D_TIME",
			Math.floor(date / 1000) * 1000); // 秒単位に丸める #27
};

/**
 * 使い方HTMLを取得する(Menuイベントから呼び出される) #84
 * 
 * @memberof HJN.init
 * @return {String} str 使い方HTML
 */
HJN.init.Usage=function(){
    var UsageHtmlFile = "tatLogDiver-Usage.html";
    var UsageHtmlURL = "./tatLogDiver/" + UsageHtmlFile;
    // requireメソッドの有無で、webpack環境下か判定する
    if (typeof require === "undefined") {
        // webpack でパッケージ化していないとき、ダイアログ内にhtmlへのパスを表示する
        var url = "https://github.com/hirosejn/HJN/wiki/Usage(JP)";
        var label = "GitHub wiki : " + url;
        var html = 'Usage of TAT log diver'
            + '<br>'
            + 'webpack 未適用モード'
            + '<br>'
            + '<a class="hjnLabel4Input" href="' + url + '"'
            + 'target=”_hirosejnUsage”>' + label +'</a><BR>'
            + '<br>'
            + '<a class="hjnLabel4Input" href="' + UsageHtmlURL + '"'
            + 'target=”_hirosejnUsage”>' + UsageHtmlURL +'</a><BR>';
        return html;
    } else {
        // webpack でパッケージ化するとき、ダイアログ内にhtmlを表示する
        return require("./" + UsageHtmlFile); // #94
    }
};

/**
 * 著作権表記文字を取得する(Menuイベントから呼び出される)
 * 
 * @memberof HJN.init
 * @return {String} str 著作権表記文字
 */
HJN.init.Copyright=function(){
	return Copyright.getAboutText();
};