6 <li each={name, i in mainTabs} class={select: mainTab === i} onclick={parent.changeTab}>{name}</li>
10 /* global moment, c3, opts */
12 this.mainTab = +sessionStorage.getItem('prevTab');
13 opts.observable.trigger("mainTabChanged", this.mainTab);
15 this.changeTab = function(e) {
16 this.mainTab = e.item.i;
17 sessionStorage.setItem('prevTab', e.item.i);
18 opts.observable.trigger("mainTabChanged", e.item.i);
24 <div id="term" show={enabled}>
25 <ul class="tab tabsub" style="float: left; margin-right: 0.2em">
26 <li each={name, i in rangeTabs} class={select: opts.logRange.val === i} onclick={parent.rangeTabChange}>{name}</li>
28 <div style="padding: 0.2em 0;">
29 <input type="text" id="term_from" style="width: 10em">~<input type="text" id="term_to" style="width: 10em">
46 opts.observable.on("mainTabChanged", function(idx) {
47 self.update({enabled: idx >= 0 && idx < self.logTables});
50 var val = sessionStorage.getItem('logRange');
51 opts.logRange.val = val === null ? 2 : +val;
53 this.init = function() {
54 self.initPicker('#term_from', '#term_to', function() {
55 if (opts.logRange.val === 4)
56 opts.observable.trigger( "logRangeChanged");
60 this.on("mount", this.init);
62 this.rangeTabChange = function(e) {
63 sessionStorage.setItem("logRange", e.item.i);
64 opts.logRange.val = e.item.i;
65 opts.observable.trigger("logRangeChanged");
72 <div each={header, i in tables} show={mainTab === i}>
73 <table class="display compact cell-border" id={"log" + i}>
85 "<th>日付</th><th>海域</th><th>マップ</th><th>マス</th><th>ボス</th><th>ランク</th><th>ドロップ艦種</th><th>ドロップ艦娘", // ドロップ
86 "<th>日付</th><th style=\"min-width: 3.2em;\">海域</th><th>マップ</th><th>マス</th><th>ボス</th><th>ランク</th><th>艦隊行動</th><th>味方陣形</th><th>敵陣形</th><th style=\"min-width: 3.2em;\">敵艦隊</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 style=\"min-width: 2.2em;\">敵艦1</th><th>敵艦1HP</th><th style=\"min-width: 2.2em;\">敵艦2</th><th>敵艦2HP</th><th style=\"min-width: 2.2em;\">敵艦3</th><th>敵艦3HP</th><th style=\"min-width: 2.2em;\">敵艦4</th><th>敵艦4HP</th><th style=\"min-width: 2.2em;\">敵艦5</th><th>敵艦5HP</th><th style=\"min-width: 2.2em;\">敵艦6</th><th>敵艦6HP</th><th>味方制空値</th><th>敵制空値</th><th>制空状態</th>", // 海戦
87 "<th>日付</th><th>結果</th><th>遠征</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>開発資材</th><th>高速修復材</th><th>高速建造材</th><th>改修資材</th>", // 遠征
88 "<th>日付</th><th>開発装備</th><th>種別</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>秘書艦</th><th>司令部Lv</th>", // 開発
89 "<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>", // 建造
90 "<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>", // 改修
91 "<th>日付</th><th>燃料</th><th>弾薬</th><th>鋼材</th><th>ボーキ</th><th>高速建造材</th><th>高速修復材</th><th>開発資材</th><th>改修資材</th>" // 戦果
104 this.on("mount", function() {
111 opts.observable.on("mainTabChanged", function(idx) {
112 self.update({mainTab: idx});
116 opts.observable.on("logRangeChanged", function() {
120 this.init = function() {
121 for (var t = 0; t < this.tables.length; t++) {
122 var table = $('#log' + t);
123 self.setHeaderAndFooter(table, self.tables[t]);
124 var dTable = table.DataTable(self.tableOptions(t));
125 self.searchSetup(dTable);
129 this.tableOptions = function(tableId) {
134 order: [[0, "desc"]],
136 lengthMenu: [[50, 100, 200, -1], [50, 100, 200, "All"]],
137 drawCallback: function() {
138 $('#loading').hide();
142 opts.columns = self.dropColumns();
143 } else if (tableId === 1) {
144 opts.columns = self.sortieColumns();
149 this.dropColumns = function() {
150 return [{data: 0}, {data: 1}, {data: 40}, {data: 2}, {data: 3}, {data: 4}, {data: 9}, {data: 10}];
153 this.sortieColumns = function() {
155 for (var i = 0; i < 38; i++) {
157 entries.push({data: 40});
158 if (i === 9 || i === 10)
161 entries.push({data: 38});
162 entries.push({data: 39});
164 entries.push({data: i});
169 this.setHeaderAndFooter = function(table, header) {
170 table.find("thead tr").first().html(header);
171 var footer = table.find("tfoot tr");
172 table.find("th").each(function(index) {
174 '<th style="padding: 1px"><input style="min-width: 100%" size="1" type="search" placeholder="Search ' +
175 $(this).text() + '"/></th>');
179 this.searchSetup = function(dTable) {
180 self.setupCellSearch(dTable);
181 self.setupGlobalSearch(dTable);
184 this.setupCellSearch = function(dTable) {
185 dTable.columns().every(function() {
187 that.search(""); // reset
188 $('input', this.footer()).on("input search", function() {
189 that.search(this.value, true, false).draw();
194 this.setupGlobalSearch = function(dTable) {
195 var searchLabel = $(dTable.table().container()).find(".dataTables_filter label").first();
196 searchLabel.html('Search: <input type="search">');
197 searchLabel.children("input").first().on("input search", function() {
198 dTable.search(this.value, true, false).draw();
200 dTable.search(""); // reset
203 this.show = function() {
204 if (this.mainTab >= this.jsons.length)
208 var query = "?from=";
209 switch (opts.logRange.val){
211 from = now.clone().startOf('day').hours(5);
213 from.subtract(1, 'days');
214 query += from.valueOf();
217 from = now.clone().day(1).startOf('day').hours(5);
218 if (now.day() === 0 || now.day() === 1 && now.hour() < 5)
219 from.subtract(1, 'weeks');
220 query += from.valueOf();
223 if (now.hours() >= 22 &&
224 now.date() === now.clone().endOf('month').date()) {
225 from = now.clone().startOf('day').hours(22);
227 from = now.clone().startOf('month').subtract(1, 'days').hours(22);
229 query += from.valueOf();
235 from = $('#term_from').datetimepicker("getValue");
236 var to = $('#term_to').datetimepicker("getValue");
239 query += from.valueOf();
241 query += "&to=" + to.valueOf();
244 $('#loading').show();
245 var url = this.jsons[this.mainTab] + query;
246 $('#log' + this.mainTab).DataTable().ajax.url(url).load();
252 <form id="chart_type" show={mainTabs[mainTab] === "資材グラフ"}>
253 <div style="margin: 0 0 0.5em 1em;">
254 <label><input type="radio" name="chart_type" value="0" checked={opts.chartSpec.type === 0} onchange={chartTypeChange}>連続</label>
255 <label><input type="radio" name="chart_type" value="1" checked={opts.chartSpec.type === 1} onchange={chartTypeChange}>差分</label>
261 opts.chartSpec.type = +sessionStorage.getItem('chartType');
264 this.chartTypeChange = function(e) {
265 opts.chartSpec.type = +e.target.value;
266 sessionStorage.setItem('chartType', opts.chartSpec.type);
267 opts.observable.trigger("chartTypeChanged");
268 opts.observable.trigger("chartSpecChanged");
271 opts.observable.on("mainTabChanged", function(idx) {
272 self.update({mainTab: idx});
278 <div show={mainTabs[mainTab] === "資材グラフ"}>
279 <ul class="tab tabsub" style="float: left; margin-right: 0.2em" show={chartSpec.type === 0}>
280 <li each={name, i in seqChartRanges} class={select: chartSpec.seqRange === i} onclick={parent.rangeTabChange}>{name}</li>
283 <ul class="tab tabsub" style="float: left; margin-right: 0.2em" show={chartSpec.type === 1}>
284 <li each={name, i in diffChartRanges} class={select: chartSpec.diffRange === i} onclick={parent.rangeTabChange}>{name}</li>
286 <div style="padding: 0.2em 0;">
287 <input type="text" id="chart_from" style="width: 10em">~<input type="text" id="chart_to" style="width: 10em">
288 <label><input type="checkbox" id="tooltip" value="" style="margin-left: 2em;" onchange={tooltipChange} checked={opts.chartSpec.tooltip === 1}>ツールチップ</label>
293 this.seqChartRanges = [
302 this.diffChartRanges = [
310 opts.chartSpec.seqRange = +sessionStorage.getItem('seqChartRange');
311 opts.chartSpec.diffRange = +sessionStorage.getItem('diffChartRange');
312 opts.chartSpec.tooltip = +sessionStorage.getItem('chartTooltip');
313 this.chartSpec = opts.chartSpec;
315 this.rangeTabChange = function(e) {
316 if (opts.chartSpec.type === 0) {
317 opts.chartSpec.seqRange = e.item.i;
318 sessionStorage.setItem('seqChartRange', e.item.i);
320 opts.chartSpec.diffRange = e.item.i;
321 sessionStorage.setItem('diffChartRange', e.item.i);
323 opts.observable.trigger("chartSpecChanged");
326 this.tooltipChange = function(e) {
327 opts.chartSpec.tooltip = +e.target.checked;
328 sessionStorage.setItem('chartTooltip', +e.target.checked);
329 opts.observable.trigger("chartSpecChanged");
332 this.useDatePicker = function() {
333 return opts.chartSpec.type === 0 && opts.chartSpec.seqRange === 5 ||
334 opts.chartSpec.type === 1 && opts.chartSpec.diffRange === 4;
339 this.init = function() {
340 self.initPicker('#chart_from', '#chart_to', function() {
341 if (self.useDatePicker())
342 opts.observable.trigger("chartSpecChanged");
348 this.on("mount", self.init);
350 opts.observable.on("mainTabChanged", function(idx) {
351 self.update({mainTab: idx});
354 opts.observable.on("chartTypeChanged", function() {
364 opts.observable.on("chartSpecChanged", function() {
365 if (opts.chartSpec.type === 0)
369 opts.observable.on("chartSizeChanged", function() {
370 if (opts.chartSpec.type === 0)
374 this.header = ["日付", "燃料", "弾薬", "鋼材", "ボーキ", "高速建造材", "高速修復材", "開発資材", "改修資材"];
376 opts.observable.on("offAllLegends", function() {
377 if (opts.chartSpec.type !== 0)
380 self.header.slice(1).forEach(function(c) {
381 self.unselected[c] = true;
385 this.resize = function() {
388 $('#loading').show();
389 setTimeout(function() {
390 self.chart.resize(self.chartSize());
394 this.drawChart = function(data) {
395 var range = this.calcRange(opts.chartSpec.seqRange);
396 if (range.last === 0)
399 $('#loading').show();
401 url: "./資材ログ.json?number=true" +
402 "&from=" + range.first + "&to=" + range.last,
403 success: function(d) { self.drawChart(d); },
404 dataType: "json", cache: false
409 picked = this.pickChartData(data.data, range);
410 picked.data.unshift(self.header);
411 this.drawSeqChart(picked);
414 this.calcRange = function(range) {
416 var last = (new Date()).valueOf();
419 first = moment(last).subtract(24, 'hours').valueOf();
422 first = moment(last).subtract(7, 'days').valueOf();
425 first = moment(last).subtract(1, 'months').valueOf();
428 first = moment(last).subtract(3, 'months').valueOf();
433 var fromDate = $('#chart_from').datetimepicker("getValue");
434 var toDate = $('#chart_to').datetimepicker("getValue");
435 if (fromDate === null || toDate === null)
436 return {first: 0, last:0};
437 first = fromDate.valueOf();
438 last = toDate.valueOf();
441 return {first: first, last: last};
444 this.unselected = {};
446 this.drawSeqChart = function(picked) {
447 var size = this.chartSize();
448 this.chart = c3.generate({
456 xFormat: '%Y-%m-%d %H:%M:%S',
473 show: opts.chartSpec.tooltip
485 format: "%m-%d %H:%M",
495 onclick: function(id) {
496 self.unselected[id] = !self.unselected[id];
497 self.chart.toggle(id);
501 onrendered: function() {
502 $('#loading').hide();
503 opts.observable.trigger("chartRendered");
506 self.chart.hide(Object.keys(self.unselected).filter(function(e) {
507 return self.unselected[e];
511 this.pickChartData = function(data, range) {
515 var first = range.first;
516 var last = range.last;
517 var interval, tickInterval, lastTick;
518 if (last <= first + this.oneDay) {
520 tickInterval = 3600 * 1000;
521 lastTick = last - last % tickInterval;
522 } else if (last <= first + this.oneDay * 21) {
524 tickInterval = this.oneDay;
525 lastTick = this.to5am(last);
526 } else if (last <= first + this.oneDay * 63) {
527 interval = 3600 * 1000;
528 tickInterval = this.oneDay * 7;
529 lastTick = this.to5am(moment(last).day(1).valueOf());
530 } else if (last <= first + this.oneDay * 126) {
531 interval = 3600 * 6000;
532 tickInterval = this.oneDay * 14;
533 lastTick = this.to5am(moment(last).day(1).valueOf());
535 var magn = Math.ceil((last - data[0][0]) / (this.oneDay * 365) / 2);
536 interval = this.oneDay * magn;
537 tickInterval = this.oneDay * 28 * magn;
538 lastTick = this.to5am(moment(last).day(1).valueOf());
541 for (var i = data.length - 1; i >= 0; i--) {
546 var v = date - date % interval;
547 if (lastData !== v) {
548 newdata.unshift(row);
556 for (var tick = lastTick; tick > lastData; tick -= tickInterval) {
557 var str = self.toDateString(moment(tick));
559 grid.unshift({ value: str });
561 return { data: newdata, tick: ticks, grid: grid };
570 opts.observable.on("chartSpecChanged", function() {
571 if (opts.chartSpec.type === 1)
575 opts.observable.on("chartSizeChanged", function() {
576 if (opts.chartSpec.type === 1)
580 this.header = ["日付", "燃料", "弾薬", "鋼材", "ボーキ"];
582 opts.observable.on("offAllLegends", function() {
583 if (opts.chartSpec.type !== 1)
586 self.header.slice(1).forEach(function(c) {
587 self.unselected[c] = true;
591 this.resize = function() {
594 $('#loading').show();
595 setTimeout(function() {
596 self.chart.resize(self.chartSize());
600 this.drawChart = function(data) {
601 var range = this.calcRange(opts.chartSpec.diffRange);
602 if (range.last === 0)
605 $('#loading').show();
607 url: "./資材ログ.json?number=true" +
608 "&from=" + range.first + "&to=" + range.last,
609 success: function(d) { self.drawChart(d); },
610 dataType: "json", cache: false
615 picked = this.pickChartData(data.data, range);
616 picked.data.unshift(self.header);
617 this.drawDiffChart(picked);
620 this.calcRange = function(range) {
622 var last = (new Date()).valueOf();
625 first = moment(last).subtract(1, 'months').valueOf();
628 first = moment(last).subtract(3, 'months').valueOf();
631 first = moment(last).subtract(6, 'months').subtract(1, 'weeks').valueOf();
636 var fromDate = $('#chart_from').datetimepicker("getValue");
637 var toDate = $('#chart_to').datetimepicker("getValue");
638 if (fromDate === null || toDate === null)
639 return {first: 0, last: 0};
640 first = Math.max(first, fromDate.valueOf());
641 last = Math.min(last, toDate.valueOf());
644 return {first: first, last: last};
647 this.unselected = {};
649 this.drawDiffChart = function(picked) {
650 var size = this.chartSize();
651 this.chart = c3.generate({
667 groups: [["燃料", "弾薬", "鋼材", "ボーキ"]]
675 show: opts.chartSpec.tooltip
692 format: picked.monthly ? "%Y-%m" : "%m-%d %H:%M",
699 onclick: function(id) {
700 self.unselected[id] = !self.unselected[id];
701 self.chart.toggle(id);
705 onrendered: function() {
706 $('#loading').hide();
707 opts.observable.trigger("chartRendered");
710 self.chart.hide(Object.keys(self.unselected).filter(function(e) {
711 return self.unselected[e];
715 this.pickChartData = function(data, range) {
719 var first = range.first;
720 var last = range.last;
721 var interval, tickInterval, lastTick;
724 return this.pickMonthlyChartData(data);
725 if (last <= first + this.oneDay * 2 * 31) {
726 interval = this.oneDay;
727 tickInterval = this.oneDay * 2;
728 lastTick = this.to5am(last);
730 } else if (last <= first + this.oneDay * 3 * 31) {
731 interval = this.oneDay;
732 tickInterval = this.oneDay * 7;
733 lastTick = this.to5am(last);
736 interval = this.oneDay * 7;
737 tickInterval = this.oneDay * 28;
738 lastTick = this.to5am(moment(last).day(1).valueOf());
740 if (last <= first + this.oneDay * 6 * 38) {
741 tickInterval = this.oneDay * 14;
745 var lastDate = lastTick;
747 for (var i = data.length - 1; i >= 0; i--) {
756 if (date <= lastDate) {
757 var newrow = [lastDate];
758 for (var r = 1; r < 5; r++) {
759 newrow.push(prevRow[r] - row[r]);
761 newdata.unshift(newrow);
762 lastDate = lastDate - interval;
770 if (tickInterval >= this.oneDay * 7)
771 lastTick = moment(lastTick).day(1).hour(5).minute(0).valueOf();
772 for (var tick = lastTick; tick > lastDate; tick -= tickInterval) {
774 grid.unshift({ value: tick });
776 return { data: newdata, tick: ticks, grid: grid, width: barWidth };
779 this.pickMonthlyChartData = function(data) {
787 for (var i = data.length - 1; i >= 0; i--) {
791 var eom = moment(row[0]).endOf('month');
792 prevRow[0] = eom.valueOf();
793 prevMonth = eom.month();
796 date = new Date(row[0]);
797 if (prevMonth !== date.getMonth()) {
798 var newrow = [prevRow[0]];
799 for (var r = 1; r < 5; r++)
800 newrow.push(prevRow[r] - row[r]);
801 newdata.unshift(newrow);
802 ticks.unshift(prevRow[0]);
803 grid.unshift({ value: prevRow[0] });
805 prevMonth = date.getMonth();
808 if (prevRow && date !== prevRow[0]) {
809 newrow = [prevRow[0]];
810 for (r = 1; r < 5; r++)
811 newrow.push(prevRow[r] - row[r]);
812 newdata.unshift(newrow);
813 ticks.unshift(prevRow[0]);
814 grid.unshift({ value: prevRow[0] });
816 return { monthly: true, data: newdata, tick: ticks, grid: grid, width: 0.5 };
819 </differential-chart>
822 <div show={mainTabs[mainTab] === "資材グラフ"}>
823 <span class="c3-legend-item" id="off-all-legends" style="text-decoration: underline; cursor: pointer; z-index: 10; position: absolute; display: none;" onclick={offAllLegends} >全解除</span>
824 <div id="chart" style="clear: both; margin: 1em;"></div>
831 opts.observable.on("mainTabChanged", function(idx) {
832 self.update({mainTab: idx});
833 if (self.mainTabs[idx] === "資材グラフ")
834 opts.observable.trigger("chartSpecChanged");
837 opts.observable.on("chartRendered", function() {
839 if (opts.chartSpec.type === 0) {
840 legend = $(".c3-legend-item-改修資材>text").offset();
843 legend = $(".c3-legend-item-ボーキ>text").offset();
847 $("#off-all-legends").offset({top: legend.top, left: legend.left + offset}).show();
850 this.offAllLegends = function() {
851 opts.observable.trigger("offAllLegends");
855 $(window).resize(function() {
857 clearTimeout(self.timer);
858 self.timer = setTimeout(function() {
859 if (self.mainTabs[self.mainTab] === "資材グラフ")
860 opts.observable.trigger("chartSizeChanged");
861 else if (self.mainTabs[self.mainTab] === "戦果")
862 opts.observable.trigger("achivementChartSizeChanged");
869 <div show={mainTabs[mainTab] === "戦果"}>
870 <span style="margin-left: 1em;">期間: </span><select style="width: 7em; margin-bottom: 1em;" name="月" onchange={monthChange}>
871 <option each={m, i in months} value={m}>{m}</option>
873 <table id="achivement_table" class="display compact cell-border">
875 <tr><th>日付</th><th>戦果</th><th>月毎</th><th>EO</th><th>月毎(EO込)</th></tr>
878 <div id="achivementChart" style="margin: 1em;"></div>
882 this.on("mount", function() {
883 $("#achivement_table").dataTable({
887 order: [[0, "desc"]],
891 drawCallback: function() {
892 $('#loading').hide();
899 opts.observable.on("mainTabChanged", function(idx) {
900 self.update({mainTab: idx});
901 if (self.mainTabs[self.mainTab] === "戦果")
906 this.selectedIndex = 0;
908 this.monthChange = function(event) {
909 this.selectedIndex = event.target.selectedIndex;
910 if (this.selectedIndex === 0) {
917 this.calcResult = function(data) {
919 var expPerAch = 10000 / 7.0;
921 var endOfMonth = moment(0);
924 var endOfYear = moment(0);
926 var carryOverAch = 0;
929 var lastDate = moment(0);
931 var nextDate = moment(0);
932 for (var i = 0; i < data.length; i++) {
934 var date = this.parseDate(row[0]);
935 var exp = row[1] - 0;
937 var isNewYear = date.isSameOrAfter(endOfYear);
938 var isNewMonth = date.isSameOrAfter(endOfMonth);
939 var isNewDate = date.isSameOrAfter(nextDate);
940 if (isNewDate || isNewMonth || isNewYear) {
941 if (lastDate.add(1, 'hours').isSameOrBefore(date)) {
942 // 2時を過ぎて最初のexpを戦果の計算に使うと、2時をまたいだ出撃の戦果が前日に加算される。
943 // そこで2時前のexpを使って戦果を計算するが、2時前のexpが正しく出力されていない場合は
944 // 戦果を正しく計算できない。記録の間隔が1時間以上空いているときは、2時をまたいだ出撃が
945 // 行われていない可能性が高いので計算には今のexpを使うことにする。
946 // これは5時基準で出力された過去のデータで、妥当な戦果を計算するために必要な処理である。
949 if (nextDate.valueOf() !== 0) {
950 var d = isNewDate ? nextDate.subtract(1, 'days') : endOfMonth;
951 var m = d.format("YYYY-MM");
954 var perMonth = (lastExp - monthExp) / expPerAch + carryOverAch;
955 this.result[m].push([
956 d.format("YYYY-MM-DD"),
957 ((lastExp - prevExp) / expPerAch).toFixed(1),
958 perMonth.toFixed(1), dayEo,
959 (perMonth + monthEo + carryOverEo).toFixed(1)
962 prevExp = lastExp === -1 ? exp : lastExp;
964 endOfYear = date.clone().endOf('year').hour(22).startOf('hour');
965 if (endOfYear.isSameOrBefore(date))
966 endOfYear.add(1, 'year');
967 yearExp = lastExp === -1 ? exp : lastExp;
971 endOfMonth = date.clone().endOf('month');
972 if (date.date() === endOfMonth.date())
973 endOfMonth.add(1, 'months').endOf('month');
974 endOfMonth.hour(22).startOf('hour');
975 monthExp = lastExp === -1 ? exp : lastExp;
976 carryOverEo = monthEo * expPerAch / 50000;
977 carryOverAch = (monthExp - yearExp) / 50000;
979 m = endOfMonth.format("YYYY-MM");
982 this.result[m].push([endOfMonth.format("YYYY-MM 引継"),
983 carryOverAch.toFixed(1), carryOverAch.toFixed(1), carryOverEo.toFixed(1), (carryOverAch + carryOverEo).toFixed(1)]);
986 nextDate = date.clone().hour(2).startOf('hour');
987 if (date.hour() >= 2)
988 nextDate.add(1, 'days');
989 if (nextDate.date() === 1)
990 nextDate.add(1, 'days');
992 if (date.isBefore(date.clone().endOf('month').hour(22).startOf('hour'))) {
993 // 月末22時から翌0時までのEOのボーナス戦果は消える。
1000 if (lastDate.isBefore(endOfMonth)) {
1001 var eom = endOfMonth.format("YYYY-MM");
1002 var ave = (perMonth - carryOverAch) / lastDate.date();
1003 var estimate = perMonth + ave * (endOfMonth.date() - lastDate.date());
1004 this.result[eom].push([endOfMonth.format("YYYY-MM-DD 予測"), ave.toFixed(1) + " 平均", estimate.toFixed(1) + " 予測", monthEo + " 合計", (estimate + monthEo).toFixed(1) + " 予測"]);
1008 this.calcChartData = function() {
1009 this.chartData = {};
1010 for (var month in this.result) {
1011 var data = this.chartData[month] = [];
1012 var result = this.result[month];
1015 data.push(["日付", "戦果", "EO", "月毎"]);
1016 for (var i = 0; i < result.length; i++) {
1017 var row = result[i];
1018 if (row[0].match(/予測/))
1020 if (row[0].match(/引継/)) {
1022 data.push([0, row[1], row[3], row[4]]);
1025 d = moment(row[0], "YYYY-MM-DD").date();
1027 data.push([d, row[2], eo, row[4]]);
1029 var endOfMonth = moment(month, "YYYY-MM").endOf("month").date();
1030 while (d < endOfMonth) {
1032 data.push([d, null, null, null]);
1037 this.chartSize = function() {
1038 var width = Math.max($(window).width() - 6 * this.pxPerEm, 800);
1040 height: width * 0.4,
1045 opts.observable.on("achivementChartSizeChanged", function() {
1048 $('#loading').show();
1049 setTimeout(function() {
1050 self.chart.resize(self.chartSize());
1054 this.showChart = function(month) {
1055 this.chart = c3.generate({
1056 bindto: "#achivementChart",
1057 size: this.chartSize(),
1060 rows: this.chartData[month],
1067 onrendered: function() { $('#loading').hide(); }
1071 this.updateData = function(data) {
1073 $('#loading').show();
1076 success: function(data) {
1077 self.updateData(data.data);
1084 this.calcResult(data);
1085 this.calcChartData();
1086 this.months = Object.keys(this.result).sort(function(a, b) {
1097 this.show = function() {
1098 if (this.result === undefined){
1102 var dt = $('#achivement_table').DataTable();
1104 dt.rows.add(this.result[this.months[this.selectedIndex]]).draw();
1105 this.showChart(this.months[this.selectedIndex]);
1111 <div show={mainTabs[mainTab] === "出撃統計"}>
1113 <ul class="tab tabsub" style="float: left; margin-right: 0.2em">
1114 <li each={tabs} class={select: parent.type === type} onclick={parent.changeTab}>{label}</li>
1117 <div style="padding: 0.2em 0;">
1118 <input type="text" id="sortie_stat_from" style="width: 10em">~<input type="text" id="sortie_stat_to" style="width: 10em">
1121 <div style="clear: both;" show={type === "recent"}>
1123 <table id="sortie_stat_day">
1126 <table id="sortie_stat_week">
1129 <table id="sortie_stat_month">
1133 <div show={type === "range"}>
1134 <table id="sortie_stat_all" style="width: 100%;">
1151 this.type = "recent";
1152 this.changeTab = function(e) {
1153 this.type = e.item.type;
1160 this.on("mount", function() {
1161 $("[id^=sortie]").addClass('display compact cell-border');
1165 opts.observable.on("mainTabChanged", function(idx) {
1166 self.update({mainTab: idx});
1167 if (self.mainTabs[self.mainTab] === "出撃統計")
1171 this.init = function() {
1172 self.initPicker('#sortie_stat_from', '#sortie_stat_to', function() {
1173 if (self.type === "range")
1180 this.loadData = function() {
1182 if (this.type === "recent") {
1183 from = moment().subtract(1, 'months').subtract(1, 'day').valueOf();
1184 to = new Date().valueOf();
1186 var fromDate = $('#sortie_stat_from').datetimepicker("getValue");
1187 var toDate = $('#sortie_stat_to').datetimepicker("getValue");
1188 if (fromDate === null || toDate === null) {
1192 from = fromDate.valueOf();
1193 to = toDate.valueOf();
1196 url: "./海戦・ドロップ報告書.json?from=" + from + "&to=" + to,
1197 success: function(data) { self.show(data.data); },
1198 dataType: "json", cache: false
1202 this.initResult = function() {
1205 if (this.type === "recent") {
1211 r.day.begin = moment(now).hour(5).minute(0).second(0);
1212 if (now.hour() < 5) {
1213 r.day.begin.subtract(1, 'days');
1215 r.week.begin = moment(now).day(1).hour(5).minute(0).second(0);
1216 if (now.day() === 0 || now.day() === 1 && now.hour() < 5) {
1217 r.week.begin.subtract(1, 'weeks');
1219 if (moment(now).endOf('month').date() === now.date() &&
1220 now.hour() >= 22) { // 月末22時以降
1221 r.month.begin = moment(now).hour(22).minute(0).second(0);
1224 moment(now).date(1).subtract(1, 'days').
1225 hour(22).minute(0).second(0);
1228 r = { all: { stat: {} } };
1229 r.all.begin = moment(0);
1234 this.gatherData = function(data) {
1235 var initStat = function() {
1236 return { start: "-", S: 0, A: 0, B: 0, C: 0, D: 0, R: 0 };
1238 var r = this.initResult();
1239 for (var i = 0; i < data.length; i++) {
1241 var date = moment(row[0]);
1243 var isBoss = row[3].indexOf("ボス") !== -1;
1244 var isStart = row[3].indexOf("出撃") !== -1;
1246 for (var j = 22; j < row.length; j++) {
1247 if (/^輸送/.test(row[j]) && /^0\x2f/.test(row[j + 1]))
1250 var item = /アイテム/.test(row[9]) ? /[^+]+$/.exec(row[10])[0] : null;
1254 for (var term in r) {
1255 if (!r.hasOwnProperty(term))
1258 if (to.begin.isAfter(date))
1260 for (var b = 0; b < 4; b++) {
1261 var name = b < 2 ? "合計" : map;
1262 if (b === 1 || b === 3) {
1265 name = name + " - ボス";
1267 var mo = to.stat[name];
1269 mo = to.stat[name] = initStat();
1271 to.stat["合計 - ボス"] = initStat();
1273 if ((b === 0 || b === 2) && isStart) {
1274 if (mo.start === "-")
1278 if (/^基地航空隊/.test(row[11]))
1293 this.isItemColumn = function(col) {
1294 return !/^(?:map|start|[SABCDR])$/.test(col);
1297 this.sortItemOrder = function(items) {
1298 ["お米", "梅干", "海苔", "お茶"].reverse().forEach(function(item) {
1299 var idx = items.indexOf(item);
1301 items.splice(idx, 1);
1302 items.unshift(item);
1307 this.setupColumns = function(r) {
1308 for (var term in r) {
1309 var columns = [{ data: "map", title: "マップ" },
1310 { data: "start", title: "出撃" },
1311 { data: "S", title: "S" },
1312 { data: "A", title: "A" },
1313 { data: "B", title: "B" },
1314 { data: "C", title: "C" },
1315 { data: "D", title: "D" },
1316 { data: "R", title: "輸送船" }];
1317 if (term === "month")
1320 for (var col in r[term].stat["合計"]) {
1321 if (this.isItemColumn(col))
1324 this.sortItemOrder(items);
1325 items.forEach(function(item) {
1326 columns.push({data: item, title: item});
1328 r[term].columns = columns;
1332 this.fillupItemRecords = function(r) {
1333 for (var term in r) {
1334 for (var col in r[term].stat["合計"]) {
1335 if (!this.isItemColumn(col))
1337 for (var map in r[term].stat) {
1340 if (!r[term].stat[map][col]){
1341 r[term].stat[map][col] = 0;
1348 this.reorderRows = function(r) {
1349 for (var term in r) {
1350 if (!r.hasOwnProperty(term))
1354 for (var map in r[term].stat) {
1355 if (!r[term].stat.hasOwnProperty(map))
1359 var e = r[term].stat[map];
1363 var boss = map + " - ボス";
1364 e = r[term].stat[boss];
1371 r[term].table = table;
1375 this.show = function(data) {
1377 $('#loading').show();
1381 var r = this.gatherData(data);
1382 this.setupColumns(r);
1383 this.fillupItemRecords(r);
1384 this.reorderRows(r);
1385 for (var term in r) {
1386 var table = $("#sortie_stat_" + term);
1387 if ($.fn.dataTable.isDataTable(table))
1388 table.DataTable().destroy();
1389 table.html("<thead><tr>" +
1390 r[term].columns.reduce(function(acc, cur) {
1391 return acc + "<th>" + cur.title + "</th>";
1392 }, "") + "</tr></thead>");
1397 columns: r[term].columns,
1401 $('#loading').hide();