-<!DOCTYPE html>
+<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>各種報告書 - KancolleSniffer</title>
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.7/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.7/css/jquery.dataTables.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/i18n/datepicker-ja.min.js"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/riot/3.5.0/riot+compiler.min.js"></script>
<style>
body {
font-family:'Lucida Grande','Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
}
.tab {overflow: hidden; list-style-type: none; margin: 0 0 2em 1em; padding: 0;}
.tab li {background: #eee; padding: 0.3em 1.5em; float: left; margin-right: 2px;}
-.tab1 li {padding: 0.3em 1em;}
.tab li.select {background: #ccc;}
-.contents {list-style-type: none; margin: 0; padding: 0;}
-.hide {display: none;}
+.tabsub li {padding: 0.3em 1em;}
.c3 .tick {font-family:'Lucida Grande','Hiragino Kaku Gothic ProN', Meiryo, sans-serif; font-size: 12px;}
.c3-legend-item {font-family:'Lucida Grande','Hiragino Kaku Gothic ProN', Meiryo, sans-serif; font-size: 14px;}
#loading {
</style>
</head>
<body>
-<script>
-/* global moment, c3, $ */
-
-var oneDay = 3600 * 24 * 1000;
-
-function showLog() {
- var query = "?from=" + moment().subtract(1, 'months').valueOf();
- if ($('input[name=term]:eq(1)').prop('checked')) {
- var from = $('#term_from').datepicker("getDate");
- var to = $('#term_to').datepicker("getDate");
- if (from != null)
- query = "?from=" + from.valueOf();
- if (to != null)
- query += "&to=" + (to.valueOf() + oneDay);
- }
- var jsons = [
- "海戦・ドロップ報告書.json",
- "海戦・ドロップ報告書.json",
- "遠征報告書.json",
- "開発報告書.json",
- "建造報告書.json",
- "改修報告書.json",
- "資材ログ.json"
- ];
- $('#loading').show();
- var url = jsons[selectedTable] + query;
- $('#log' + selectedTable).DataTable().ajax.url(url).load();
-}
-
-var tables = 7;
+<div id="loading"><img src="https://kancollesniffer.osdn.jp/ajax-loader.gif" alt="読み込み中..."></div>
-function initTables() {
- for (var t = 0; t < tables; t++) {
- var opts = {
- destroy: true,
- deferRender: true,
- stateSave: true,
- order: [[0, "desc"]],
- pageLength: 50,
- lengthMenu: [[50, 100, 200, -1], [50, 100, 200, "All"]],
- drawCallback: function () {
- $('#loading').hide();
- }
- };
- if (t === 0) {
- opts.columns = [{ data: 0 }, { data: 1 }, { data: 2 }, { data: 3 }, { data: 4 }, { data: 9 }, { data: 10 }];
- } else if (t === 1) {
- var entries = [];
- for (var i = 0; i < 38; i++) {
- if (i === 9 || i === 10)
- continue;
- entries.push({ data: i });
- }
- opts.columns = entries;
- }
- $('#log' + t).dataTable(opts);
- }
-}
+<script>
+/* global moment, riot */
var timeFormat = "YYYY-MM-DD HH:mm:ss";
-function parseDate(d) {
- return moment(d, timeFormat);
-}
-
-function toString(d) {
- return d.format(timeFormat);
-}
-
-function to5am(tick) {
- return tick - tick % (3600 * 24000) - 3600 * 4000;
-}
-
-var selectedTable = 0;
-var seqChartRange = 0;
-var diffChartRange = 0;
-var chartType = 0;
-var showChart = false;
-var currentPickedData;
-
-function drawChart(data) {
- if (data == null) {
- $('#loading').show();
- $.get("./資材ログ.json?time=" + Date.now(), function (d) { drawChart(d); }, "json");
- return;
- }
- var picked, header;
- if (chartType === 0) {
- picked = pickChartData(data.data, seqChartRange);
- header = ["日付", "燃料", "弾薬", "鋼材", "ボーキ", "高速建造材", "高速修復材", "開発資材", "改修資材"];
- picked.data.unshift(header);
- drawSeqChart(picked);
- currentPickedData = picked;
- } else {
- picked = pickDiffChartData(data.data, diffChartRange);
- header = ["日付", "燃料", "弾薬", "鋼材", "ボーキ"];
- picked.data.unshift(header);
- drawDiffChart(picked);
- currentPickedData = picked;
- }
-}
-
-var chartSeq, chartDiff;
-
-function redrawChart() {
- if (!currentPickedData)
- return;
- $('#loading').show();
- setTimeout(function () {
- if (chartType === 0)
- chartSeq.resize(chartSize());
- if (chartType === 1)
- chartDiff.resize(chartSize());
- });
-}
-
-var timer;
-$(window).resize(function () {
- if (timer)
- clearTimeout(timer);
- timer = setTimeout(function () {
- if (showChart)
- redrawChart();
- }, 200);
-});
-
-function chartSize() {
- var pxPerEm = Number($('#chart').css('fontSize').match(/(\d*(\.\d*)?)px/)[1]);
- return {
- height: Math.max($(document).height() - 15 * pxPerEm, 400),
- width: Math.max($(document).width() - 6 * pxPerEm, 800)
- };
-}
-
-var seqChartUnselected = {};
-
-function drawSeqChart(picked) {
- var size = chartSize();
- chartSeq = c3.generate({
- bindto: '#chart',
- size: {
- height: size.height,
- width: size.width
- },
- data: {
- x: '日付',
- xFormat: '%Y-%m-%d %X',
- rows: picked.data,
- axes: {
- 燃料: 'y',
- 弾薬: 'y',
- 鋼材: 'y',
- ボーキ: 'y',
- 高速建造材: 'y2',
- 高速修復材: 'y2',
- 開発資材: 'y2',
- 改修資材: 'y2'
- }
- },
- point: {
- show: false
- },
- tooltip: {
- show: $('#tooltip').prop('checked')
- },
- grid: {
- x: {
- lines: picked.grid
- }
- },
- axis: {
- x: {
- type: 'timeseries',
- tick: {
- rotate: 30,
- format: function (x) { return moment(x).format("MM-DD HH:mm"); },
- values: picked.tick
- }
- },
- y2: {
- show: true
- }
- },
- legend: {
- item: {
- onclick: function(id) {
- seqChartUnselected[id] = !seqChartUnselected[id];
- chartSeq.toggle(id);
- }
- }
- },
- onrendered: function () { $('#loading').hide(); }
- });
- for (var id in seqChartUnselected) {
- if (seqChartUnselected.hasOwnProperty(id) && seqChartUnselected[id])
- chartSeq.toggle(id);
- }
-}
-
-function pickChartData(data, range) {
- var newdata = [];
- var ticks = [];
- var grid = [];
- var first = moment(data[0][0]).valueOf();
- var last = moment(data[data.length - 1][0]).valueOf();
- var interval, tickInterval, lastTick;
- switch (range) {
- case 0:
- first = moment(last).subtract(24, 'hours').valueOf();
- break;
- case 1:
- first = moment(last).subtract(7, 'days').valueOf();
- break;
- case 2:
- first = moment(last).subtract(1, 'months').valueOf();
- break;
- case 3:
- first = moment(last).subtract(3, 'months').valueOf();
- break;
- case 4:
- first = moment(data[0][0]).valueOf();
- break;
- case 5:
- var fromDate = $('#chart_from').datepicker("getDate");
- var toDate = $('#chart_to').datepicker("getDate");
- if (fromDate == null || toDate == null)
- return { data: [], tick: [], grid: [] };
- var from = fromDate.valueOf() + 3600 * 5000;
- var to = toDate.valueOf() + oneDay + 3600 * 5000;
- first = Math.max(first, from);
- last = Math.min(last, to);
- break;
- }
- if (last <= first + oneDay) {
- interval = 1000;
- tickInterval = 3600 * 1000;
- lastTick = last - last % tickInterval;
- } else if (last <= first + oneDay * 21) {
- interval = 1000;
- tickInterval = oneDay;
- lastTick = to5am(last);
- } else if (last <= first + oneDay * 63) {
- interval = 3600 * 1000;
- tickInterval = oneDay * 7;
- lastTick = to5am(moment(last).day(1).valueOf());
- } else if (last <= first + oneDay * 126) {
- interval = 3600 * 6000;
- tickInterval = oneDay * 14;
- lastTick = to5am(moment(last).day(1).valueOf());
- } else {
- interval = 3600 * 12000;
- tickInterval = oneDay * 28;
- lastTick = to5am(moment(last).day(1).valueOf());
- }
- var lastData;
- for (var i = data.length - 1; i >= 0; i--) {
- var row = data[i];
- var date = parseDate(row[0]).valueOf();
- if (date > first) {
- if (date <= last) {
- var v = date - date % interval;
- if (lastData !== v) {
- newdata.unshift(row);
- lastData = v;
- }
- }
- } else {
- break;
- }
- }
- for (var tick = lastTick; tick > lastData; tick -= tickInterval) {
- var str = toString(moment(tick));
- ticks.unshift(str);
- grid.unshift({ value: str });
- }
- return { data: newdata, tick: ticks, grid: grid };
-}
-
-var diffChartUnselected = {};
-
-function drawDiffChart(picked) {
- var size = chartSize();
- chartDiff = c3.generate({
- bindto: '#chart',
- size: {
- height: size.height,
- width: size.width
- },
- data: {
- x: '日付',
- xFormat: '%Y-%m-%d %X',
- rows: picked.data,
- axes: {
- 燃料: 'y',
- 弾薬: 'y',
- 鋼材: 'y',
- ボーキ: 'y'
- },
- type: 'bar',
- groups: [["燃料", "弾薬", "鋼材", "ボーキ"]]
- },
- bar: {
- width: {
- ratio: picked.width
- }
- },
- tooltip: {
- show: $('#tooltip').prop('checked')
- },
- grid: {
- x: {
- lines: picked.grid
- },
- y: {
- lines: [
- { value: 0 }
- ]
- }
- },
- axis: {
- x: {
- type: 'timeseries',
- tick: {
- rotate: 30,
- format: function (x) { return moment(x).format("MM-DD HH:mm"); },
- values: picked.tick
- }
- }
- },
- legend: {
- item: {
- onclick: function(id) {
- diffChartUnselected[id] = !diffChartUnselected[id];
- chartDiff.toggle(id);
- }
- }
- },
- onrendered: function () { $('#loading').hide(); }
- });
- for (var id in diffChartUnselected) {
- if (diffChartUnselected.hasOwnProperty(id) && diffChartUnselected[id])
- chartDiff.toggle(id);
- }
-}
-
-function pickDiffChartData(data, range) {
- var newdata = [];
- var ticks = [];
- var grid = [];
- var first = moment(data[0][0]).valueOf();
- var last = moment(data[data.length - 1][0]).valueOf();
- var interval, tickInterval, lastTick;
- var oneDay = 3600 * 24 * 1000;
- switch (range) {
- case 0:
- first = moment(last).subtract(1, 'months').valueOf();
- break;
- case 1:
- first = moment(last).subtract(3, 'months').valueOf();
- break;
- case 2:
- first = moment(last).subtract(6, 'months').subtract(1, 'weeks').valueOf();
- break;
- case 3:
- first = moment(data[0][0]).valueOf();
- break;
- case 4:
- var fromDate = $('#chart_from').datepicker("getDate");
- var toDate = $('#chart_to').datepicker("getDate");
- if (fromDate == null || toDate == null)
- return { data: [], tick: [], grid: [] };
- var from = fromDate.valueOf() + 3600 * 5000;
- var to = toDate.valueOf() + oneDay + 3600 * 5000;
- first = Math.max(first, from);
- last = Math.min(last, to);
- break;
- }
- var barWidth;
- if (last <= first + oneDay * 2 * 31) {
- interval = oneDay;
- tickInterval = oneDay * 2;
- lastTick = to5am(last);
- barWidth = 0.3;
- } else if (last <= first + oneDay * 3 * 31) {
- interval = oneDay;
- tickInterval = oneDay * 7;
- lastTick = to5am(last);
- barWidth = 0.1;
- } else {
- interval = oneDay * 7;
- tickInterval = oneDay * 28;
- lastTick = to5am(moment(last).day(1).valueOf());
- barWidth = 0.1;
- if (last <= first + oneDay * 6 * 38) {
- tickInterval = oneDay * 14;
- barWidth = 0.3;
- }
- }
- var lastDate = lastTick;
- var prevRow;
- for (var i = data.length - 1; i >= 0; i--) {
- var row = data[i];
- var date = parseDate(row[0]).valueOf();
- if (date > first && date <= last) {
- if (!prevRow) {
- prevRow = row;
- continue;
- }
- if (date <= lastDate) {
- var newrow = [prevRow[0]];
- for (var r = 1; r < 5; r++) {
- newrow.push(prevRow[r] - row[r]);
- }
- newdata.unshift(newrow);
- lastDate = lastDate - interval;
- prevRow = row;
- }
- } else {
- break;
- }
- }
- if (tickInterval >= oneDay * 7)
- lastTick = moment(lastTick).day(1).hour(5).minute(0).valueOf();
- for (var tick = lastTick; tick > lastDate; tick -= tickInterval) {
- var str = toString(moment(tick));
- ticks.unshift(str);
- grid.unshift({ value: str });
- }
- return { data: newdata, tick: ticks, grid: grid, width: barWidth };
-}
-
-var sortieStatRange = 0;
-
-function loadSortieData() {
- var from, to;
- if (sortieStatRange === 0) {
- from = moment().subtract(1, 'months').subtract(1, 'day').valueOf();
- to = new Date().valueOf();
- } else {
- var fromDate = $('#sortie_stat_from').datepicker("getDate");
- var toDate = $('#sortie_stat_to').datepicker("getDate");
- if (fromDate == null || toDate == null) {
- setSortieStat([]);
- return;
- }
- from = fromDate.valueOf();
- to = toDate.valueOf() + oneDay;
- }
- $.ajax({
- url: "./海戦・ドロップ報告書.json?from=" + from + "&to=" + to,
- success: function (data) { setSortieStat(data.data); },
- dataType: "json", cache: false
- });
-}
-
-function initSortieStatResult() {
- var now = moment();
- var r;
- if (sortieStatRange === 0) {
- r = {
- day: { stat: {} },
- week: { stat: {} },
- month: { stat: {} }
+var mixin = {
+ mainTabs: [
+ "ドロップ",
+ "海戦",
+ "遠征",
+ "開発",
+ "建造",
+ "改修",
+ "資材",
+ "資材グラフ",
+ "戦果",
+ "出撃統計"
+ ],
+ logTables: 7,
+ oneDay: 3600 * 24 * 1000,
+ parseDate: function(d) {
+ return moment(d, timeFormat);
+ },
+ toDateString: function(d) {
+ return d.format(timeFormat);
+ },
+ to5am: function(tick) {
+ return tick - tick % (3600 * 24000) - 3600 * 4000;
+ },
+ chartSize: function() {
+ var pxPerEm = Number($('#chart').css('fontSize').match(/(\d*(\.\d*)?)px/)[1]);
+ return {
+ height: Math.max($(document).height() - 15 * pxPerEm, 400),
+ width: Math.max($(document).width() - 6 * pxPerEm, 800)
};
- r.day.begin = moment(now).hour(5).minute(0).second(0);
- if (now.hour() < 5) {
- r.day.begin.subtract(1, 'days');
- }
- r.week.begin = moment(now).day(1).hour(5).minute(0).second(0);
- if (now.day() === 0 || now.day() === 1 && now.hour() < 5) {
- r.week.begin.subtract(1, 'weeks');
- }
- if (moment(now).endOf('month').date() === now.date() &&
- now.hour() >= 22) { // 月末22時以降
- r.month.begin = moment(now).hour(22).minute(0).second(0);
- } else {
- r.month.begin =
- moment(now).date(1).subtract(1, 'days').
- hour(22).minute(0).second(0);
- }
- } else {
- r = { all: { stat: {} } };
- r.all.begin = moment(0);
}
- return r;
-}
-
-function gatherSortieStat(data) {
- var initStat = function () {
- return { start: "-", S: 0, A: 0, B: 0, C: 0, D: 0, R: 0 }
- };
- var r = initSortieStatResult();
- for (var i = 0; i < data.length; i++) {
- var row = data[i];
- var date = moment(row[0]);
- var map = row[1];
- var isBoss = row[3].indexOf("ボス") !== -1;
- var isStart = row[3].indexOf("出撃") !== -1;
- var resR = 0;
- for (var j = 23; j < row.length; j++) {
- if (/^輸送/.test(row[j]) && /^0\//.test(row[j + 1]))
- resR++;
- }
- var res = row[4];
- if (res === "E")
- res = "D";
- for (var term in r) {
- if (!r.hasOwnProperty(term))
- continue;
- var to = r[term];
- if (to.begin.isAfter(date))
- continue;
- for (var b = 0; b < 4; b++) {
- var name = b < 2 ? "合計" : map;
- if (b === 1 || b === 3) {
- if (!isBoss)
- continue;
- name = name + " - ボス";
- }
- var mo = to.stat[name];
- if (!mo) {
- mo = to.stat[name] = initStat();
- if (name === "合計")
- to.stat["合計 - ボス"] = initStat();
- }
- mo["R"] += resR;
- mo[res]++;
- if ((b === 0 || b === 2) && isStart) {
- if (mo.start === "-")
- mo.start = 0;
- mo.start++;
- }
- }
- }
- }
- return r;
-}
+};
-function arrangeSortieStatTable(r) {
- for (var term in r) {
- if (!r.hasOwnProperty(term))
- continue;
- var table = [];
- var pushed = {};
- for (var map in r[term].stat) {
- if (!r[term].stat.hasOwnProperty(map))
- continue;
- if (pushed[map])
- continue;
- var e = r[term].stat[map];
- e.map = map;
- table.push(e);
- pushed[map] = 1;
- var boss = map + " - ボス";
- e = r[term].stat[boss];
- if (!e)
- continue;
- e.map = boss;
- table.push(e);
- pushed[boss] = 1;
- }
- r[term].table = table;
- }
-}
-
-function setSortieStat(data) {
- if (!data) {
- $('#loading').show();
- loadSortieData();
- return;
- }
- var r = gatherSortieStat(data);
- arrangeSortieStatTable(r);
- for (var term in r) {
- if (!r.hasOwnProperty(term))
- continue;
- var dt = $("#sortie_stat_" + term).DataTable();
- dt.clear();
- dt.rows.add(r[term].table).draw();
- }
- $('#loading').hide();
-}
-
-function initSortieStatTables() {
- var terms = ['day', 'week', 'month', 'all'];
- for (var i = 0; i < terms.length; i++) {
- $("#sortie_stat_" + terms[i]).dataTable({
- paging: false,
- searching: false,
- ordering: false,
- columns: terms[i] !== 'month' ? [
- { data: "map" },
- { data: "start" },
- { data: "S" },
- { data: "A" },
- { data: "B" },
- { data: "C" },
- { data: "D" },
- { data: "R" }
- ] : [
- { data: "map" },
- { data: "start" },
- { data: "S" },
- { data: "A" },
- { data: "B" },
- { data: "C" },
- { data: "D" }
- ]
- });
- }
-}
-
-function showAchivementTable(data) {
- var expPerAch = 10000 / 7.0;
- if (!data) {
- $('#loading').show();
- $.ajax({
- url: "./戦果.json",
- success: function (data) {
- showAchivementTable(data.data);
- },
- dataType: 'json',
- cache: false
- });
- return;
- }
- var result = [];
- var dayEo = 0;
- var endOfMonth = moment(0);
- var monthExp = 0;
- var monthEo = 0;
- var endOfYear = moment(0);
- var yearExp = 0;
- var carryOverAch = 0;
- var carryOverEo = 0;
- var prevExp = null;
- var lastDate = moment(0);
- var lastExp = -1;
- var nextDate = moment(0);
- for (var i = 0; i < data.length; i++) {
- var row = data[i];
- var date = parseDate(row[0]);
- var exp = row[1] - 0;
- var eo = row[2] - 0;
- var isNewYear = date.isSameOrAfter(endOfYear);
- var isNewMonth = date.isSameOrAfter(endOfMonth);
- var isNewDate = date.isSameOrAfter(nextDate);
- if (isNewDate || isNewMonth || isNewYear) {
- if (lastDate.add('hour', 1).isSameOrBefore(date)) {
- // 2時を過ぎて最初のexpを戦果の計算に使うと、2時をまたいだ出撃の戦果が前日に加算される。
- // そこで2時前のexpを使って戦果を計算するが、2時前のexpが正しく出力されていない場合は
- // 戦果を正しく計算できない。記録の間隔が1時間以上空いているときは、2時をまたいだ出撃が
- // 行われていない可能性が高いので計算には今のexpを使うことにする。
- // これは5時基準で出力された過去のデータで、妥当な戦果を計算するために必要な処理である。
- lastExp = exp;
- }
- if (nextDate.valueOf() != 0) {
- result.push([
- (isNewDate ? nextDate.subtract(1, 'days') : endOfMonth).format("YYYY-MM-DD"),
- new Number((lastExp - prevExp) / expPerAch).toFixed(1), dayEo,
- new Number((lastExp - monthExp) / expPerAch + monthEo + carryOverAch + carryOverEo).toFixed(1)
- ]);
- }
- prevExp = lastExp === -1 ? exp : lastExp;
- if (isNewYear) {
- endOfYear = date.clone().endOf('year').hour(22).startOf('hour');
- if (endOfYear.isSameOrBefore(date))
- endOfYear.add(1, 'year').endOf('year');
- yearExp = lastExp === -1 ? exp : lastExp;
- }
- if (isNewMonth) {
- endOfMonth = date.clone().endOf('month').hour(22).startOf('hour');
- if (endOfMonth.isSameOrBefore(date))
- endOfMonth.add(1, 'month').endOf('month');
- monthExp = lastExp === -1 ? exp : lastExp;
- carryOverEo = monthEo * expPerAch / 50000;
- carryOverAch = (monthExp - yearExp) / 50000;
- monthEo = 0;
- result.push([endOfMonth.format("YYYY-MM 引継"),
- carryOverAch.toFixed(1), carryOverEo.toFixed(1), (carryOverAch + carryOverEo).toFixed(1)]);
- }
- dayEo = 0;
- nextDate = date.clone().hour(2).startOf('hour');
- if (date.hour() >= 2)
- nextDate.add(1, 'days');
- if (nextDate.date() === 1)
- nextDate.add(1, 'days');
- }
- if (date.isBefore(date.clone().endOf('month').hour(22).startOf('hour'))) {
- // 月末22時から翌0時までのEOのボーナス戦果は消える。
- dayEo += eo;
- monthEo += eo;
- }
- lastDate = date;
- lastExp = exp;
- }
- var dt = $('#achivement_table').DataTable();
- dt.clear();
- dt.rows.add(result).draw();
- $('#loading').hide();
-}
-
-function initAchievementTable() {
- $("#achivement_table").dataTable({
- destroy: true,
- deferRener: true,
- stateSave: true,
- order: [[0, "desc"]],
- pageLength: 50,
- lengthMenu: [[50, 100, 200, -1], [50, 100, 200, "All"]],
- drawCallback: function () {
- $('#loading').hide();
- }
- });
-}
-
-function selectTopTab(i) {
- var chart = tables;
- showChart = false;
- if (i < tables) {
- selectedTable = i;
- showLog();
- } else if (i === chart) {
- showChart = true;
- drawChart();
- } else if (i === chart + 1) {
- showAchivementTable();
- } else if (i === chart + 2) {
- setSortieStat();
- }
- if (i < tables)
- $('#term').show();
- else
- $('#term').hide();
- var tab = $('.tab0 li');
- tab.removeClass('select');
- tab.eq(i).addClass('select');
- $('#main_contents .hide').hide();
- $('#main_contents .hide').eq(i).show();
-}
-
-function initAction() {
- $('.tab0 li').click(function () {
- var tab = $('.tab0 li');
- var i = tab.index(this);
- selectTopTab(i);
- sessionStorage.setItem('prevTab', i);
- });
- $('#range_seq li').click(function () {
- var tab = $('#range_seq li');
- var i = tab.index(this);
- seqChartRange = i;
- chartType = 0;
- drawChart();
- tab.removeClass('select');
- tab.eq(i).addClass('select');
- sessionStorage.setItem('prevSeqRange', i);
- });
- $('#range_diff li').click(function () {
- var tab = $('#range_diff li');
- var i = tab.index(this);
- diffChartRange = i;
- chartType = 1;
- drawChart();
- tab.removeClass('select');
- tab.eq(i).addClass('select');
- sessionStorage.setItem('prevDiffRange', i);
- });
- $('input[name="chart_type"]:radio').change(function () {
- if ($(this).val() === "0") {
- $("#range_seq").show();
- $("#range_diff").hide();
- chartType = 0;
- } else {
- $("#range_seq").hide();
- $("#range_diff").show();
- chartType = 1;
- }
- drawChart();
- sessionStorage.setItem('chartType', $(this).val());
- });
- $('#tooltip').change(function () {
- drawChart();
- sessionStorage.setItem('chartTooltip', $(this).prop("checked"));
- });
- $('#range_sortie_stat li').click(function () {
- var tab = $('#range_sortie_stat li');
- var i = tab.index(this);
- sortieStatRange = i;
- tab.removeClass('select');
- tab.eq(i).addClass('select');
- if (sortieStatRange === 0) {
- $('#sortie_stat_recent_tables').show();
- $('#sortie_stat_all_table').hide();
- } else {
- $('#sortie_stat_recent_tables').hide();
- $('#sortie_stat_all_table').show();
- }
- setSortieStat();
- });
-}
-
-function initTableDatePicker() {
- $('#term_from').datepicker({
- defaultDate: moment().subtract(1, 'months').toDate(),
- onClose: function () { $('input[name=term]').val(['1']) }
- });
- $('#term_to').datepicker({
- onClose: function () { $('input[name=term]').val(['1']) }
- });
- $('#term_apply').click(showLog);
-}
-
-function initChartDatePicker() {
- $('#chart_from').datepicker({
- onClose: function () { if (useChartDatePicker()) drawChart(); }
- });
- $('#chart_to').datepicker({
- onClose: function () { if (useChartDatePicker()) drawChart(); }
- });
-}
-
-function useChartDatePicker() {
- return (chartType === 0 && seqChartRange === 5) ||
- (chartType === 1 && diffChartRange === 4);
-}
-
-function initSortieStatDatePicker() {
- $('#sortie_stat_from').datepicker({
- onClose: function () { if (sortieStatRange === 1) setSortieStat(); }
- });
- $('#sortie_stat_to').datepicker({
- onClose: function () { if (sortieStatRange === 1) setSortieStat(); }
- });
-}
-
-function restoreChartSettings() {
- var type = sessionStorage.getItem('chartType');
- chartType = type == null ? 0 : +type;
- var tooltip = sessionStorage.getItem('chartTooltip');
- $('#tooltip').prop('checked', tooltip === "true");
- var range = sessionStorage.getItem('prevSeqRange');
- seqChartRange = range == null ? 0 : +range;
- range = sessionStorage.getItem('prevDiffRange');
- diffChartRange = range == null ? 0 : +range;
- $('input[name="chart_type"]:radio').eq(chartType).prop('checked', true);
- if (chartType === 0) {
- $('#range_seq').show();
- $('#range_diff').hide();
- } else {
- $('#range_seq').hide();
- $('#range_diff').show();
- }
- $('#range_diff li').removeClass('select');
- $('#range_diff li').eq(diffChartRange).addClass('select');
- $('#range_seq li').removeClass('select');
- $('#range_seq li').eq(seqChartRange).addClass('select');
-}
-
-$(function () {
- $.fn.dataTable.ext.errMode = 'throw';
- initAction();
- initTableDatePicker();
- initChartDatePicker();
- initSortieStatDatePicker();
- $('table').addClass('display compact cell-border');
- initTables();
- initAchievementTable();
- initSortieStatTables();
- $('#range_sortie_stat li').removeClass('select');
- $('#range_sortie_stat li').eq(sortieStatRange).addClass('select');
- restoreChartSettings();
- var prev = sessionStorage.getItem('prevTab');
- selectTopTab(prev == null ? 0 : +prev);
-});
</script>
-<div id="loading"><img src="https://kancollesniffer.osdn.jp/ajax-loader.gif" alt="読み込み中..."></div>
-
-<ul class="tab tab0">
-<li>ドロップ</li>
-<li>海戦</li>
-<li>遠征</li>
-<li>開発</li>
-<li>建造</li>
-<li>改修</li>
-<li>資材</li>
-<li>資材グラフ</li>
-<li>戦果</li>
-<li>出撃統計</li>
-</ul>
-
-<form id="term">
-<p>
-<label><input type="radio" name="term" value="0" checked="checked">直近一か月</label>
-<label><input type="radio" name="term" value="1">期間指定: </label>
-<input type="text" id="term_from" style="width: 7em">~<input type="text" id="term_to" style="width: 7em">
-<input type="button" id="term_apply" value="適用">
-</p>
-</form>
-
-<ul class="contents" id="main_contents">
-<li class="hide">
-<table id="log0">
-<thead>
-<tr><th>日付</th><th>海域</th><th>マス</th><th>ボス</th><th>ランク</th><th>ドロップ艦種</th><th>ドロップ艦娘</th></tr>
-</thead>
-</table>
+<main-tab></main-tab>
-<li class="hide">
-<table id="log1">
-<thead>
-<tr><th>日付</th><th>海域</th><th>マス</th><th>ボス</th><th>ランク</th><th>艦隊行動</th><th>味方陣形</th><th>敵陣形</th><th>敵艦隊</th><th>味方艦1</th><th>味方艦1HP</th><th>味方艦2</th><th>味方艦2HP</th><th>味方艦3</th><th>味方艦3HP</th><th>味方艦4</th><th>味方艦4HP</th><th>味方艦5</th><th>味方艦5HP</th><th>味方艦6</th><th>味方艦6HP</th><th>敵艦1</th><th>敵艦1HP</th><th>敵艦2</th><th>敵艦2HP</th><th>敵艦3</th><th>敵艦3HP</th><th>敵艦4</th><th>敵艦4HP</th><th>敵艦5</th><th>敵艦5HP</th><th>敵艦6</th><th>敵艦6HP</th><th>味方制空値</th><th>敵制空値</th><th>制空状態</th></tr>
-</thead>
-</table>
+<log-term></log-term>
-<li class="hide">
-<table id="log2">
-<thead>
-<tr><th>日付</th><th>結果</th><th>遠征</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>開発資材</th><th>高速修復材</th><th>高速建造材</th></tr>
-</thead>
-</table>
+<log-tables></log-tables>
-<li class="hide">
-<table id="log3">
-<thead>
-<tr><th>日付</th><th>開発装備</th><th>種別</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>秘書艦</th><th>司令部Lv</th></tr>
-</thead>
-</table>
+<chart-type></chart-type>
-<li class="hide">
-<table id="log4">
-<thead>
-<tr><th>日付</th><th>種類</th><th>名前</th><th>艦種</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>開発資材</th><th>空きドック</th><th>秘書艦</th><th>司令部Lv</th></tr>
-</thead>
-</table>
+<chart-range></chart-range>
-<li class="hide">
-<table id="log5">
-<thead>
-<tr><th>日付</th><th>改修装備</th><th>レベル</th><th>成功</th><th>確実化</th><th>消費装備</th><th>消費数</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>開発資材</th><th>改修資材</th><th>秘書艦</th><th>二番艦</th></tr>
-</thead>
-</table>
+<sequential-chart></sequential-chart>
-<li class="hide">
-<table id="log6">
-<thead>
-<tr><th>日付</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>高速建造材</th><th>高速修復材</th><th>開発資材</th><th>改修資材</th></tr>
-</thead>
-</table>
+<differential-chart></differential-chart>
-<li class="hide">
-<form id="chart_type">
-<div style="margin: 0 0 0.5em 1em;">
-<label><input type="radio" name="chart_type" value="0" checked="checked">連続</label>
-<label><input type="radio" name="chart_type" value="1">差分</label>
-</div>
-</form>
-<ul class="tab tab1" id="range_seq" style="float: left; margin-right: 0.2em">
-<li>一日</li>
-<li>一週間</li>
-<li>一か月</li>
-<li>三か月</li>
-<li>すべて</li>
-<li>期間指定</li>
-</ul>
-<ul class="tab tab1" id="range_diff" style="float: left; margin-right: 0.2em">
-<li>一か月(日)</li>
-<li>三か月(日)</li>
-<li>半年(週)</li>
-<li>すべて(週)</li>
-<li>期間指定</li>
-</ul>
-<div style="padding: 0.2em 0;">
-<input type="text" id="chart_from" style="width: 7em">~<input type="text" id="chart_to" style="width: 7em">
-<label><input type="checkbox" id="tooltip" value="" style="margin-left: 2em;">ツールチップ</label>
-</div>
-<div id="chart" style="clear: both; margin: 1em;"></div>
+<material-chart></material-chart>
-<li class="hide">
-<table id="achivement_table">
-<thead>
-<tr><th>日付</th><th>戦果</th><th>EO</th><th>月毎</th></tr>
-</thead>
-</table>
+<achivement-table></achivement-table>
-<li class="hide">
-<ul class="tab tab1" id="range_sortie_stat" style="float: left; margin-right: 0.2em">
-<li>直近</li>
-<li>期間指定</li>
-</ul>
-<div style="padding: 0.2em 0;">
-<input type="text" id="sortie_stat_from" style="width: 7em">~<input type="text" id="sortie_stat_to" style="width: 7em">
-</div>
+<sortie-stat></sortie-stat>
-<div id="sortie_stat_recent_tables" style="clear: both;">
+<script src="tags.html" type="riot/tag"></script>
-<h3>今日</h3>
-<table id="sortie_stat_day">
-<thead>
-<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th><th>輸送船</th></tr>
-</thead>
-</table>
-
-<h3>今週</h3>
-<table id="sortie_stat_week">
-<thead>
-<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th><th>輸送船</th></tr>
-</table>
-
-<h3>今月</h3>
-<table id="sortie_stat_month">
-<thead>
-<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th></tr>
-</table>
-</div>
-
-<div id="sortie_stat_all_table" style="display: none;">
-<table id="sortie_stat_all">
-<thead>
-<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th><th>輸送船</th></tr>
-</thead>
-</table>
-</div>
-
-</ul>
+<script>
+var obs = riot.observable();
+var spec = new Object();
+riot.mixin(mixin);
+riot.mount("log-term", {observable: obs});
+riot.mount("log-tables", {observable: obs});
+riot.mount("chart-type", {observable: obs, chartSpec: spec});
+riot.mount("chart-range", {observable: obs, chartSpec: spec});
+riot.mount("sequential-chart", {observable: obs, chartSpec: spec});
+riot.mount("differential-chart", {observable: obs, chartSpec: spec});
+riot.mount("material-chart", {observable: obs, chartSpec: spec});
+riot.mount("achivement-table", {observable: obs});
+riot.mount("sortie-stat", {observable: obs});
+riot.mount("main-tab", {observable: obs});
+</script>
</body>
</html>
--- /dev/null
+<main-tab>
+<ul class="tab">
+ <li each={name, i in mainTabs} class={select: mainTab === i} onclick={parent.changeTab}>{name}</li>
+</ul>
+
+<script>
+/* global moment, c3, opts */
+
+this.mainTab = +sessionStorage.getItem('prevTab');
+opts.observable.trigger("mainTabChanged", this.mainTab);
+
+this.changeTab = function(e) {
+ this.mainTab = e.item.i;
+ sessionStorage.setItem('prevTab', e.item.i);
+ opts.observable.trigger("mainTabChanged", e.item.i);
+}.bind(this);
+</script>
+</main-tab>
+
+<log-term>
+<form id="term" show={enabled}>
+<p>
+<label><input type="radio" name="term" value="0" checked="checked">直近一か月</label>
+<label><input type="radio" name="term" value="1">期間指定: </label>
+<input type="text" id="term_from" style="width: 7em">~<input type="text" id="term_to" style="width: 7em">
+<input type="button" id="term_apply" value="適用">
+</p>
+</form>
+
+<script>
+this.on("mount", function() {
+ $('#term_from').datepicker({
+ defaultDate: moment().subtract(1, 'months').toDate(),
+ onClose: function() { $('input[name=term]').val(['1']); }
+ });
+ $('#term_to').datepicker({
+ onClose: function() { $('input[name=term]').val(['1']); }
+ });
+ $('#term_apply').click(function() {
+ opts.observable.trigger("termApplied");
+ });
+});
+
+this.enabled = false;
+var self = this;
+
+opts.observable.on("mainTabChanged", function(idx) {
+ self.update({enabled: idx >= 0 && idx < self.logTables});
+});
+</script>
+</log-term>
+
+<log-tables>
+<div each={header, i in tables} show={mainTab === i}>
+<table class="display compact cell-border" id={"log" + i}>
+<thead>
+<tr></tr>
+</thead>
+</table>
+</div>
+
+<script>
+this.tables = [
+"<th>日付</th><th>海域</th><th>マス</th><th>ボス</th><th>ランク</th><th>ドロップ艦種</th><th>ドロップ艦娘", // ドロップ
+"<th>日付</th><th>海域</th><th>マス</th><th>ボス</th><th>ランク</th><th>艦隊行動</th><th>味方陣形</th><th>敵陣形</th><th>敵艦隊</th><th>味方艦1</th><th>味方艦1HP</th><th>味方艦2</th><th>味方艦2HP</th><th>味方艦3</th><th>味方艦3HP</th><th>味方艦4</th><th>味方艦4HP</th><th>味方艦5</th><th>味方艦5HP</th><th>味方艦6</th><th>味方艦6HP</th><th>敵艦1</th><th>敵艦1HP</th><th>敵艦2</th><th>敵艦2HP</th><th>敵艦3</th><th>敵艦3HP</th><th>敵艦4</th><th>敵艦4HP</th><th>敵艦5</th><th>敵艦5HP</th><th>敵艦6</th><th>敵艦6HP</th><th>味方制空値</th><th>敵制空値</th><th>制空状態</th>", // 海戦
+"<th>日付</th><th>結果</th><th>遠征</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>開発資材</th><th>高速修復材</th><th>高速建造材</th>", // 遠征
+"<th>日付</th><th>開発装備</th><th>種別</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>秘書艦</th><th>司令部Lv</th>", // 開発
+"<th>日付</th><th>種類</th><th>名前</th><th>艦種</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>開発資材</th><th>空きドック</th><th>秘書艦</th><th>司令部Lv</th>", // 建造
+"<th>日付</th><th>改修装備</th><th>レベル</th><th>成功</th><th>確実化</th><th>消費装備</th><th>消費数</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>開発資材</th><th>改修資材</th><th>秘書艦</th><th>二番艦</th>", // 改修
+"<th>日付</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>高速建造材</th><th>高速修復材</th><th>開発資材</th><th>改修資材</th>" // 戦果
+];
+
+this.jsons = [
+ "海戦・ドロップ報告書.json",
+ "海戦・ドロップ報告書.json",
+ "遠征報告書.json",
+ "開発報告書.json",
+ "建造報告書.json",
+ "改修報告書.json",
+ "資材ログ.json"
+];
+
+this.on("mount", function() {
+ var records = this.root.querySelectorAll("tr");
+ for (var i = 0; i < records.length; i++)
+ records[i].innerHTML = this.tables[i];
+ this.init();
+});
+
+this.mainTab = 0;
+var self = this;
+
+opts.observable.on("mainTabChanged", function(idx) {
+ self.update({mainTab: idx});
+ self.show();
+});
+
+opts.observable.on("termApplied", function() {
+ self.show();
+});
+
+this.init = function() {
+ for (var t = 0; t < this.tables.length; t++) {
+ var opts = {
+ destroy: true,
+ deferRender: true,
+ stateSave: true,
+ order: [[0, "desc"]],
+ pageLength: 50,
+ lengthMenu: [[50, 100, 200, -1], [50, 100, 200, "All"]],
+ drawCallback: function() {
+ $('#loading').hide();
+ }
+ };
+ if (t === 0) {
+ opts.columns = [{ data: 0 }, { data: 1 }, { data: 2 }, { data: 3 }, { data: 4 }, { data: 9 }, { data: 10 }];
+ } else if (t === 1) {
+ var entries = [];
+ for (var i = 0; i < 38; i++) {
+ if (i === 9 || i === 10)
+ continue;
+ entries.push({ data: i });
+ }
+ opts.columns = entries;
+ }
+ $('#log' + t).dataTable(opts);
+ }
+};
+
+this.show = function() {
+ if (this.mainTab >= this.jsons.length)
+ return;
+ var query = "?from=" + moment().subtract(1, 'months').valueOf();
+ if ($('input[name=term]:eq(1)').prop('checked')) {
+ var from = $('#term_from').datepicker("getDate");
+ var to = $('#term_to').datepicker("getDate");
+ if (from !== null)
+ query = "?from=" + from.valueOf();
+ if (to !== null)
+ query += "&to=" + (to.valueOf() + this.oneDay);
+ }
+ $('#loading').show();
+ var url = this.jsons[this.mainTab] + query;
+ $('#log' + this.mainTab).DataTable().ajax.url(url).load();
+};
+</script>
+</log-tables>
+
+<chart-type>
+<form id="chart_type" show={mainTabs[mainTab] === "資材グラフ"}>
+<div style="margin: 0 0 0.5em 1em;">
+<label><input type="radio" name="chart_type" value="0" checked={opts.chartSpec.type === 0} onchange={chartTypeChange}>連続</label>
+<label><input type="radio" name="chart_type" value="1" checked={opts.chartSpec.type === 1} onchange={chartTypeChange}>差分</label>
+</div>
+</form>
+
+<script>
+this.mainTab = 0;
+opts.chartSpec.type = +sessionStorage.getItem('chartType');
+var self = this;
+
+this.chartTypeChange = function(e) {
+ opts.chartSpec.type = +e.target.value;
+ sessionStorage.setItem('chartType', opts.chartSpec.type);
+ opts.observable.trigger("chartTypeChanged");
+ opts.observable.trigger("chartSpecChanged");
+};
+
+opts.observable.on("mainTabChanged", function(idx) {
+ self.update({mainTab: idx});
+});
+</script>
+</chart-type>
+
+<chart-range>
+<div show={mainTabs[mainTab] === "資材グラフ"}>
+<ul class="tab tabsub" style="float: left; margin-right: 0.2em" show={chartSpec.type === 0}>
+ <li each={name, i in seqChartRanges} class={select: chartSpec.seqRange === i} onclick={parent.rangeTabChange}>{name}</li>
+</ul>
+
+<ul class="tab tabsub" style="float: left; margin-right: 0.2em" show={chartSpec.type === 1}>
+ <li each={name, i in diffChartRanges} class={select: chartSpec.diffRange === i} onclick={parent.rangeTabChange}>{name}</li>
+</ul>
+<div style="padding: 0.2em 0;">
+<input type="text" id="chart_from" style="width: 7em">~<input type="text" id="chart_to" style="width: 7em">
+<label><input type="checkbox" id="tooltip" value="" style="margin-left: 2em;" onchange={tooltipChange} checked={opts.chartSpec.tooltip === 1}>ツールチップ</label>
+</div>
+</div>
+
+<script>
+this.seqChartRanges = [
+"一日",
+"一週間",
+"一か月",
+"三か月",
+"すべて",
+"期間指定"
+];
+
+this.diffChartRanges = [
+"一か月(日)",
+"三か月(日)",
+"半年(週)",
+"すべて(週)",
+"期間指定"
+];
+
+opts.chartSpec.seqRange = +sessionStorage.getItem('seqChartRange');
+opts.chartSpec.diffRange = +sessionStorage.getItem('diffChartRange');
+opts.chartSpec.tooltip = +sessionStorage.getItem('chartTooltip');
+this.chartSpec = opts.chartSpec;
+
+this.rangeTabChange = function(e) {
+ if (opts.chartSpec.type === 0) {
+ opts.chartSpec.seqRange = e.item.i;
+ sessionStorage.setItem('seqChartRange', e.item.i);
+ } else {
+ opts.chartSpec.diffRange = e.item.i;
+ sessionStorage.setItem('diffChartRange', e.item.i);
+ }
+ opts.observable.trigger("chartSpecChanged");
+};
+
+this.tooltipChange = function(e) {
+ opts.chartSpec.tooltip = +e.target.checked;
+ sessionStorage.setItem('chartTooltip', this.tooltip);
+ opts.observable.trigger("chartSpecChanged");
+};
+
+this.useDatePicker = function() {
+ return opts.chartSpec.type === 0 && opts.chartSpec.seqRange === 5 ||
+ opts.chartSpec.type === 1 && opts.chartSpec.diffRange === 4;
+};
+
+this.init = function() {
+ $('#chart_from').datepicker({
+ onClose: function() { if (self.useDatePicker()) self.drawChart(); }
+ });
+ $('#chart_to').datepicker({
+ onClose: function() { if (self.useDatePicker()) self.drawChart(); }
+ });
+};
+
+this.on("mount", this.init);
+
+this.mainTab = 0;
+var self = this;
+
+opts.observable.on("mainTabChanged", function(idx) {
+ self.update({mainTab: idx});
+});
+
+opts.observable.on("chartTypeChanged", function() {
+ self.update();
+});
+</script>
+</chart-range>
+
+<sequential-chart>
+<script>
+var self = this;
+
+opts.observable.on("chartSpecChanged", function() {
+ if (opts.chartSpec.type === 0)
+ self.drawChart();
+});
+
+opts.observable.on("chartSizeChanged", function() {
+ if (opts.chartSpec.type === 0)
+ self.resize();
+});
+
+this.resize = function() {
+ if (!self.chart)
+ return;
+ $('#loading').show();
+ setTimeout(function() {
+ self.chart.resize(self.chartSize());
+ });
+};
+
+this.drawChart = function(data) {
+ if (!data) {
+ $('#loading').show();
+ $.ajax({
+ url: "./資材ログ.json",
+ success: function(d) { self.drawChart(d); },
+ dataType: "json", cache: false
+ });
+ return;
+ }
+ var picked, header;
+ picked = this.pickChartData(data.data, opts.chartSpec.seqRange);
+ header = ["日付", "燃料", "弾薬", "鋼材", "ボーキ", "高速建造材", "高速修復材", "開発資材", "改修資材"];
+ picked.data.unshift(header);
+ this.drawSeqChart(picked);
+};
+
+this.unselected = {};
+
+this.drawSeqChart = function(picked) {
+ var size = this.chartSize();
+ this.chart = c3.generate({
+ bindto: '#chart',
+ size: {
+ height: size.height,
+ width: size.width
+ },
+ data: {
+ x: '日付',
+ xFormat: '%Y-%m-%d %X',
+ rows: picked.data,
+ axes: {
+ 燃料: 'y',
+ 弾薬: 'y',
+ 鋼材: 'y',
+ ボーキ: 'y',
+ 高速建造材: 'y2',
+ 高速修復材: 'y2',
+ 開発資材: 'y2',
+ 改修資材: 'y2'
+ }
+ },
+ point: {
+ show: false
+ },
+ tooltip: {
+ show: opts.chartSpec.tooltip
+ },
+ grid: {
+ x: {
+ lines: picked.grid
+ }
+ },
+ axis: {
+ x: {
+ type: 'timeseries',
+ tick: {
+ rotate: 30,
+ format: function(x) { return moment(x).format("MM-DD HH:mm"); },
+ values: picked.tick
+ }
+ },
+ y2: {
+ show: true
+ }
+ },
+ legend: {
+ item: {
+ onclick: function(id) {
+ self.unselected[id] = !self.unselected[id];
+ self.chart.toggle(id);
+ }
+ }
+ },
+ onrendered: function() { $('#loading').hide(); }
+ });
+ for (var id in self.unselected) {
+ if (self.unselected.hasOwnProperty(id) && self.unselected[id])
+ self.chart.toggle(id);
+ }
+};
+
+this.pickChartData = function(data, range) {
+ var newdata = [];
+ var ticks = [];
+ var grid = [];
+ var first = moment(data[0][0]).valueOf();
+ var last = moment(data[data.length - 1][0]).valueOf();
+ var interval, tickInterval, lastTick;
+ switch (range) {
+ case 0:
+ first = moment(last).subtract(24, 'hours').valueOf();
+ break;
+ case 1:
+ first = moment(last).subtract(7, 'days').valueOf();
+ break;
+ case 2:
+ first = moment(last).subtract(1, 'months').valueOf();
+ break;
+ case 3:
+ first = moment(last).subtract(3, 'months').valueOf();
+ break;
+ case 4:
+ first = moment(data[0][0]).valueOf();
+ break;
+ case 5:
+ var fromDate = $('#chart_from').datepicker("getDate");
+ var toDate = $('#chart_to').datepicker("getDate");
+ if (fromDate === null || toDate === null)
+ return { data: [], tick: [], grid: [] };
+ var from = fromDate.valueOf() + 3600 * 5000;
+ var to = toDate.valueOf() + this.oneDay + 3600 * 5000;
+ first = Math.max(first, from);
+ last = Math.min(last, to);
+ break;
+ }
+ if (last <= first + this.oneDay) {
+ interval = 1000;
+ tickInterval = 3600 * 1000;
+ lastTick = last - last % tickInterval;
+ } else if (last <= first + this.oneDay * 21) {
+ interval = 1000;
+ tickInterval = this.oneDay;
+ lastTick = this.to5am(last);
+ } else if (last <= first + this.oneDay * 63) {
+ interval = 3600 * 1000;
+ tickInterval = this.oneDay * 7;
+ lastTick = this.to5am(moment(last).day(1).valueOf());
+ } else if (last <= first + this.oneDay * 126) {
+ interval = 3600 * 6000;
+ tickInterval = this.oneDay * 14;
+ lastTick = this.to5am(moment(last).day(1).valueOf());
+ } else {
+ interval = 3600 * 12000;
+ tickInterval = this.oneDay * 28;
+ lastTick = this.to5am(moment(last).day(1).valueOf());
+ }
+ var lastData;
+ for (var i = data.length - 1; i >= 0; i--) {
+ var row = data[i];
+ var date = this.parseDate(row[0]).valueOf();
+ if (date > first) {
+ if (date <= last) {
+ var v = date - date % interval;
+ if (lastData !== v) {
+ newdata.unshift(row);
+ lastData = v;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ for (var tick = lastTick; tick > lastData; tick -= tickInterval) {
+ var str = self.toDateString(moment(tick));
+ ticks.unshift(str);
+ grid.unshift({ value: str });
+ }
+ return { data: newdata, tick: ticks, grid: grid };
+};
+</script>
+</sequential-chart>
+
+<differential-chart>
+<script>
+var self = this;
+
+opts.observable.on("chartSpecChanged", function() {
+ if (opts.chartSpec.type === 1)
+ self.drawChart();
+});
+
+opts.observable.on("chartSizeChanged", function() {
+ if (opts.chartSpec.type === 1)
+ self.resize();
+});
+
+this.resize = function() {
+ if (!self.chart)
+ return;
+ $('#loading').show();
+ setTimeout(function() {
+ self.chart.resize(self.chartSize());
+ });
+};
+
+this.drawChart = function(data) {
+ if (!data) {
+ $('#loading').show();
+ $.ajax({
+ url: "./資材ログ.json",
+ success: function(d) { self.drawChart(d); },
+ dataType: "json", cache: false
+ });
+ return;
+ }
+ var picked, header;
+ picked = this.pickChartData(data.data, opts.chartSpec.diffRange);
+ header = ["日付", "燃料", "弾薬", "鋼材", "ボーキ"];
+ picked.data.unshift(header);
+ this.drawDiffChart(picked);
+};
+
+this.unselected = {};
+
+this.drawDiffChart = function(picked) {
+ var size = this.chartSize();
+ this.chart = c3.generate({
+ bindto: '#chart',
+ size: {
+ height: size.height,
+ width: size.width
+ },
+ data: {
+ x: '日付',
+ xFormat: '%Y-%m-%d %X',
+ rows: picked.data,
+ axes: {
+ 燃料: 'y',
+ 弾薬: 'y',
+ 鋼材: 'y',
+ ボーキ: 'y'
+ },
+ type: 'bar',
+ groups: [["燃料", "弾薬", "鋼材", "ボーキ"]]
+ },
+ bar: {
+ width: {
+ ratio: picked.width
+ }
+ },
+ tooltip: {
+ show: opts.chartSpec.tooltip
+ },
+ grid: {
+ x: {
+ lines: picked.grid
+ },
+ y: {
+ lines: [
+ { value: 0 }
+ ]
+ }
+ },
+ axis: {
+ x: {
+ type: 'timeseries',
+ tick: {
+ rotate: 30,
+ format: function(x) { return moment(x).format("MM-DD HH:mm"); },
+ values: picked.tick
+ }
+ }
+ },
+ legend: {
+ item: {
+ onclick: function(id) {
+ self.unselected[id] = !self.unselected[id];
+ self.chart.toggle(id);
+ }
+ }
+ },
+ onrendered: function() { $('#loading').hide(); }
+ });
+ for (var id in self.unselected) {
+ if (self.unselected.hasOwnProperty(id) && self.unselected[id])
+ self.chart.toggle(id);
+ }
+};
+
+this.pickChartData = function(data, range) {
+ var newdata = [];
+ var ticks = [];
+ var grid = [];
+ var first = moment(data[0][0]).valueOf();
+ var last = moment(data[data.length - 1][0]).valueOf();
+ var interval, tickInterval, lastTick;
+ switch (range) {
+ case 0:
+ first = moment(last).subtract(1, 'months').valueOf();
+ break;
+ case 1:
+ first = moment(last).subtract(3, 'months').valueOf();
+ break;
+ case 2:
+ first = moment(last).subtract(6, 'months').subtract(1, 'weeks').valueOf();
+ break;
+ case 3:
+ first = moment(data[0][0]).valueOf();
+ break;
+ case 4:
+ var fromDate = $('#chart_from').datepicker("getDate");
+ var toDate = $('#chart_to').datepicker("getDate");
+ if (fromDate === null || toDate === null)
+ return { data: [], tick: [], grid: [] };
+ var from = fromDate.valueOf() + 3600 * 5000;
+ var to = toDate.valueOf() + this.oneDay + 3600 * 5000;
+ first = Math.max(first, from);
+ last = Math.min(last, to);
+ break;
+ }
+ var barWidth;
+ if (last <= first + this.oneDay * 2 * 31) {
+ interval = this.oneDay;
+ tickInterval = this.oneDay * 2;
+ lastTick = this.to5am(last);
+ barWidth = 0.3;
+ } else if (last <= first + this.oneDay * 3 * 31) {
+ interval = this.oneDay;
+ tickInterval = this.oneDay * 7;
+ lastTick = this.to5am(last);
+ barWidth = 0.1;
+ } else {
+ interval = this.oneDay * 7;
+ tickInterval = this.oneDay * 28;
+ lastTick = this.to5am(moment(last).day(1).valueOf());
+ barWidth = 0.1;
+ if (last <= first + this.oneDay * 6 * 38) {
+ tickInterval = this.oneDay * 14;
+ barWidth = 0.3;
+ }
+ }
+ var lastDate = lastTick;
+ var prevRow;
+ for (var i = data.length - 1; i >= 0; i--) {
+ var row = data[i];
+ var date = this.parseDate(row[0]).valueOf();
+ if (date > first && date <= last) {
+ if (!prevRow) {
+ prevRow = row;
+ continue;
+ }
+ if (date <= lastDate) {
+ var newrow = [prevRow[0]];
+ for (var r = 1; r < 5; r++) {
+ newrow.push(prevRow[r] - row[r]);
+ }
+ newdata.unshift(newrow);
+ lastDate = lastDate - interval;
+ prevRow = row;
+ }
+ } else {
+ break;
+ }
+ }
+ if (tickInterval >= this.oneDay * 7)
+ lastTick = moment(lastTick).day(1).hour(5).minute(0).valueOf();
+ for (var tick = lastTick; tick > lastDate; tick -= tickInterval) {
+ var str = self.toDateString(moment(tick));
+ ticks.unshift(str);
+ grid.unshift({ value: str });
+ }
+ return { data: newdata, tick: ticks, grid: grid, width: barWidth };
+};
+</script>
+</differential-chart>
+
+<material-chart>
+<div show={mainTabs[mainTab] === "資材グラフ"}>
+<div id="chart" style="clear: both; margin: 1em;"></div>
+</div>
+
+<script>
+this.mainTab = 0;
+var self = this;
+
+opts.observable.on("mainTabChanged", function(idx) {
+ self.update({mainTab: idx});
+ if (self.mainTabs[idx] === "資材グラフ")
+ opts.observable.trigger("chartSpecChanged");
+});
+
+this.timer = null;
+$(window).resize(function() {
+ if (self.timer)
+ clearTimeout(self.timer);
+ self.timer = setTimeout(function() {
+ if (self.mainTabs[self.mainTab] === "資材グラフ")
+ opts.observable.trigger("chartSizeChanged");
+ }, 200);
+});
+</script>
+</material-chart>
+
+<achivement-table>
+<div show={mainTabs[mainTab] === "戦果"}>
+<table id="achivement_table" class="display compact cell-border">
+<thead>
+<tr><th>日付</th><th>戦果</th><th>EO</th><th>月毎</th></tr>
+</thead>
+</table>
+</div>
+
+<script>
+this.on("mount", function() {
+ $("#achivement_table").dataTable({
+ destroy: true,
+ deferRener: true,
+ stateSave: true,
+ order: [[0, "desc"]],
+ pageLength: 50,
+ lengthMenu: [[50, 100, 200, -1], [50, 100, 200, "All"]],
+ drawCallback: function() {
+ $('#loading').hide();
+ }
+ });
+});
+
+var self = this;
+
+opts.observable.on("mainTabChanged", function(idx) {
+ self.update({mainTab: idx});
+ if (self.mainTabs[self.mainTab] === "戦果")
+ self.show();
+});
+
+this.show = function(data) {
+ var expPerAch = 10000 / 7.0;
+ if (!data) {
+ $('#loading').show();
+ $.ajax({
+ url: "./戦果.json",
+ success: function(data) {
+ self.show(data.data);
+ },
+ dataType: 'json',
+ cache: false
+ });
+ return;
+ }
+ var result = [];
+ var dayEo = 0;
+ var endOfMonth = moment(0);
+ var monthExp = 0;
+ var monthEo = 0;
+ var endOfYear = moment(0);
+ var yearExp = 0;
+ var carryOverAch = 0;
+ var carryOverEo = 0;
+ var prevExp = null;
+ var lastDate = moment(0);
+ var lastExp = -1;
+ var nextDate = moment(0);
+ for (var i = 0; i < data.length; i++) {
+ var row = data[i];
+ var date = this.parseDate(row[0]);
+ var exp = row[1] - 0;
+ var eo = row[2] - 0;
+ var isNewYear = date.isSameOrAfter(endOfYear);
+ var isNewMonth = date.isSameOrAfter(endOfMonth);
+ var isNewDate = date.isSameOrAfter(nextDate);
+ if (isNewDate || isNewMonth || isNewYear) {
+ if (lastDate.add(1, 'hours').isSameOrBefore(date)) {
+ // 2時を過ぎて最初のexpを戦果の計算に使うと、2時をまたいだ出撃の戦果が前日に加算される。
+ // そこで2時前のexpを使って戦果を計算するが、2時前のexpが正しく出力されていない場合は
+ // 戦果を正しく計算できない。記録の間隔が1時間以上空いているときは、2時をまたいだ出撃が
+ // 行われていない可能性が高いので計算には今のexpを使うことにする。
+ // これは5時基準で出力された過去のデータで、妥当な戦果を計算するために必要な処理である。
+ lastExp = exp;
+ }
+ if (nextDate.valueOf() !== 0) {
+ result.push([
+ (isNewDate ? nextDate.subtract(1, 'days') : endOfMonth).format("YYYY-MM-DD"),
+ new Number((lastExp - prevExp) / expPerAch).toFixed(1), dayEo,
+ new Number((lastExp - monthExp) / expPerAch + monthEo + carryOverAch + carryOverEo).toFixed(1)
+ ]);
+ }
+ prevExp = lastExp === -1 ? exp : lastExp;
+ if (isNewYear) {
+ endOfYear = date.clone().endOf('year').hour(22).startOf('hour');
+ if (endOfYear.isSameOrBefore(date))
+ endOfYear.add(1, 'year').endOf('year');
+ yearExp = lastExp === -1 ? exp : lastExp;
+ }
+ if (isNewMonth) {
+ endOfMonth = date.clone().endOf('month').hour(22).startOf('hour');
+ if (endOfMonth.isSameOrBefore(date))
+ endOfMonth.add(1, 'month').endOf('month');
+ monthExp = lastExp === -1 ? exp : lastExp;
+ carryOverEo = monthEo * expPerAch / 50000;
+ carryOverAch = (monthExp - yearExp) / 50000;
+ monthEo = 0;
+ result.push([endOfMonth.format("YYYY-MM 引継"),
+ carryOverAch.toFixed(1), carryOverEo.toFixed(1), (carryOverAch + carryOverEo).toFixed(1)]);
+ }
+ dayEo = 0;
+ nextDate = date.clone().hour(2).startOf('hour');
+ if (date.hour() >= 2)
+ nextDate.add(1, 'days');
+ if (nextDate.date() === 1)
+ nextDate.add(1, 'days');
+ }
+ if (date.isBefore(date.clone().endOf('month').hour(22).startOf('hour'))) {
+ // 月末22時から翌0時までのEOのボーナス戦果は消える。
+ dayEo += eo;
+ monthEo += eo;
+ }
+ lastDate = date;
+ lastExp = exp;
+ }
+ var dt = $('#achivement_table').DataTable();
+ dt.clear();
+ dt.rows.add(result).draw();
+ $('#loading').hide();
+};
+</script>
+</achivement-table>
+
+<sortie-stat>
+<div show={mainTabs[mainTab] === "出撃統計"}>
+
+<ul class="tab tabsub" style="float: left; margin-right: 0.2em">
+<li each={tabs} class={select: parent.type === type} onclick={parent.changeTab}>{label}</li>
+</ul>
+
+<div style="padding: 0.2em 0;">
+<input type="text" id="sortie_stat_from" style="width: 7em">~<input type="text" id="sortie_stat_to" style="width: 7em">
+</div>
+
+<div style="clear: both;" show={type === "recent"}>
+<h3>今日</h3>
+<table id="sortie_stat_day">
+<thead>
+<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th><th>輸送船</th></tr>
+</thead>
+</table>
+<h3>今週</h3>
+<table id="sortie_stat_week">
+<thead>
+<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th><th>輸送船</th></tr>
+</table>
+<h3>今月</h3>
+<table id="sortie_stat_month">
+<thead>
+<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th></tr>
+</table>
+</div>
+
+<div id="sortie_stat_all_table" show={type === "range"}>
+<table id="sortie_stat_all">
+<thead>
+<tr><th>マップ</th><th>出撃</th><th>S</th><th>A</th><th>B</th><th>C</th><th>D以下</th><th>輸送船</th></tr>
+</thead>
+</table>
+</div>
+
+</div>
+
+<script>
+this.tabs = [
+ {
+ type: "recent",
+ label: "直近"
+ },
+ {
+ type: "range",
+ label: "期間指定"
+ }
+];
+this.type = "recent";
+this.changeTab = function(e) {
+ this.type = e.item.type;
+ this.show();
+}.bind(this);
+
+this.mainTab = 0;
+var self = this;
+
+this.on("mount", function() {
+ $("[id^=sortie]").addClass('display compact cell-border');
+ this.init();
+});
+
+opts.observable.on("mainTabChanged", function(idx) {
+ self.update({mainTab: idx});
+ if (self.mainTabs[self.mainTab] === "出撃統計")
+ self.show();
+});
+
+this.init = function() {
+ this.initTable();
+ this.initDatePicker();
+};
+
+this.initTable = function() {
+ var terms = ['day', 'week', 'month', 'all'];
+ for (var i = 0; i < terms.length; i++) {
+ $("#sortie_stat_" + terms[i]).dataTable({
+ paging: false,
+ searching: false,
+ ordering: false,
+ columns: terms[i] !== 'month' ? [
+ { data: "map" },
+ { data: "start" },
+ { data: "S" },
+ { data: "A" },
+ { data: "B" },
+ { data: "C" },
+ { data: "D" },
+ { data: "R" }
+ ] : [
+ { data: "map" },
+ { data: "start" },
+ { data: "S" },
+ { data: "A" },
+ { data: "B" },
+ { data: "C" },
+ { data: "D" }
+ ]
+ });
+ }
+};
+
+this.initDatePicker = function() {
+ $('#sortie_stat_from').datepicker({
+ onClose: function() { if (self.type === "range") self.show(); }
+ });
+ $('#sortie_stat_to').datepicker({
+ onClose: function() { if (self.type === "range") self.show(); }
+ });
+};
+
+var self = this;
+
+this.loadData = function() {
+ var from, to;
+ if (this.type === "recent") {
+ from = moment().subtract(1, 'months').subtract(1, 'day').valueOf();
+ to = new Date().valueOf();
+ } else {
+ var fromDate = $('#sortie_stat_from').datepicker("getDate");
+ var toDate = $('#sortie_stat_to').datepicker("getDate");
+ if (fromDate === null || toDate === null) {
+ this.show([]);
+ return;
+ }
+ from = fromDate.valueOf();
+ to = toDate.valueOf() + this.oneDay;
+ }
+ $.ajax({
+ url: "./海戦・ドロップ報告書.json?from=" + from + "&to=" + to,
+ success: function(data) { self.show(data.data); },
+ dataType: "json", cache: false
+ });
+};
+
+this.initResult = function() {
+ var now = moment();
+ var r;
+ if (this.type === "recent") {
+ r = {
+ day: { stat: {} },
+ week: { stat: {} },
+ month: { stat: {} }
+ };
+ r.day.begin = moment(now).hour(5).minute(0).second(0);
+ if (now.hour() < 5) {
+ r.day.begin.subtract(1, 'days');
+ }
+ r.week.begin = moment(now).day(1).hour(5).minute(0).second(0);
+ if (now.day() === 0 || now.day() === 1 && now.hour() < 5) {
+ r.week.begin.subtract(1, 'weeks');
+ }
+ if (moment(now).endOf('month').date() === now.date() &&
+ now.hour() >= 22) { // 月末22時以降
+ r.month.begin = moment(now).hour(22).minute(0).second(0);
+ } else {
+ r.month.begin =
+ moment(now).date(1).subtract(1, 'days').
+ hour(22).minute(0).second(0);
+ }
+ } else {
+ r = { all: { stat: {} } };
+ r.all.begin = moment(0);
+ }
+ return r;
+};
+
+this.gatherData = function(data) {
+ var initStat = function() {
+ return { start: "-", S: 0, A: 0, B: 0, C: 0, D: 0, R: 0 };
+ };
+ var r = this.initResult();
+ for (var i = 0; i < data.length; i++) {
+ var row = data[i];
+ var date = moment(row[0]);
+ var map = row[1];
+ var isBoss = row[3].indexOf("ボス") !== -1;
+ var isStart = row[3].indexOf("出撃") !== -1;
+ var resR = 0;
+ for (var j = 23; j < row.length; j++) {
+ if (/^輸送/.test(row[j]) && /^0\x2f/.test(row[j + 1]))
+ resR++;
+ }
+ var res = row[4];
+ if (res === "E")
+ res = "D";
+ for (var term in r) {
+ if (!r.hasOwnProperty(term))
+ continue;
+ var to = r[term];
+ if (to.begin.isAfter(date))
+ continue;
+ for (var b = 0; b < 4; b++) {
+ var name = b < 2 ? "合計" : map;
+ if (b === 1 || b === 3) {
+ if (!isBoss)
+ continue;
+ name = name + " - ボス";
+ }
+ var mo = to.stat[name];
+ if (!mo) {
+ mo = to.stat[name] = initStat();
+ if (name === "合計")
+ to.stat["合計 - ボス"] = initStat();
+ }
+ mo["R"] += resR;
+ mo[res]++;
+ if ((b === 0 || b === 2) && isStart) {
+ if (mo.start === "-")
+ mo.start = 0;
+ mo.start++;
+ }
+ }
+ }
+ }
+ return r;
+};
+
+this.arrangeTable = function(r) {
+ for (var term in r) {
+ if (!r.hasOwnProperty(term))
+ continue;
+ var table = [];
+ var pushed = {};
+ for (var map in r[term].stat) {
+ if (!r[term].stat.hasOwnProperty(map))
+ continue;
+ if (pushed[map])
+ continue;
+ var e = r[term].stat[map];
+ e.map = map;
+ table.push(e);
+ pushed[map] = 1;
+ var boss = map + " - ボス";
+ e = r[term].stat[boss];
+ if (!e)
+ continue;
+ e.map = boss;
+ table.push(e);
+ pushed[boss] = 1;
+ }
+ r[term].table = table;
+ }
+};
+
+this.show = function(data) {
+ if (!data) {
+ $('#loading').show();
+ this.loadData();
+ return;
+ }
+ var r = this.gatherData(data);
+ this.arrangeTable(r);
+ for (var term in r) {
+ if (!r.hasOwnProperty(term))
+ continue;
+ var dt = $("#sortie_stat_" + term).DataTable();
+ dt.clear();
+ dt.rows.add(r[term].table).draw();
+ }
+ $('#loading').hide();
+};
+</script>
+</sortie-stat>