###### tags: `GitHub Page` `canvasjs` `JavaScript` `HTML`
# LineTagAnalyze
Using LINE's tags to generate cumulative graphs over time.
# 網頁成果展示
> [網頁應用連結](https://yutingkung.github.io/LineTagAnalyze/)
> [原始碼連結](https://github.com/YuTingKung/LineTagAnalyze)

# 邏輯流程圖
```flow
st=>start: 開始
e=>end: 結束
upload=>operation: 點按選擇檔案,選擇從LINE匯出的TXT檔案
analyze=>operation: 從TXT讀取各訊息的發送者名稱,再用發送者名稱來解析標記
graph=>operation: canvasJS讀取整理好的資料並產生頁面
st->upload->analyze->graph->e
```
# 使用技術
## 程式語言: JavaScript
原生JavaScript,無使用框架。
## 部署平台: GitHub Page
> [參考連結](https://pages.github.com/)
在GitHub創立Repository,再將該Repository設定為GitHub Page,平台就會認定Repository內的index.html為初始檔案。
## 圖表模組: canvasJS
> [折線圖連結](https://canvasjs.com/javascript-charts/multi-series-line-chart/)
此專案選擇外部引用canvasJS。canvasJS支援多個框架和多種圖形,並且可直接於線上編輯,將測試資料帶入頁面,即可事先查看所產生的圖形是否符合需求。
# 程式補充說明
## 1. 若使用者更改名稱,則會造成中間欄位名稱和右方標記不同。(下圖為從LINE匯出的格式參考)

## 2. 部分標記後方有空白,部分則無,增加Regex的難度。
## 3. 預期最佳解決方案,可再下方列出所有解析出的標記,並讓使用者選擇是否顯示或合併。
# 初版完整index.html程式碼
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="file" name="file" id="file">
<div id="chartContainer" style="height: 370px; width: 100%;"></div>
<div id="output"></div>
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script>
const $output = document.getElementById('output')
document.getElementById('file').onchange = function() {
var file = this.files[0];
var reader = new FileReader();
var data = [];
reader.onload = function(progressEvent) {
const text = this.result;
// $output.innerText = text // This line will show all text on screen, but the screen will get stocked if txt is too large.
let lines = text.split('\n').slice(3);
let table = [];
let date = '';
let uniqueNameFromTitle = [];
for (var line = 0; line < lines.length; line++) {
let timeRegExp = new RegExp("^([0-9]{2})([:]{1})([0-9]{2})");
let isTime = timeRegExp.test(lines[line].substring(0, 10).split(' ')[0]);
if (isTime) {
let title = lines[line].split('\t')[1];
uniqueNameFromTitle.push(title);
};
}
uniqueNameFromTitle = uniqueNameFromTitle.filter((v, i, a) => a.indexOf(v) === i).filter(e => e != null && e.length > 0);
let matchNameRegExp = uniqueNameFromTitle.map(e => `@${e}`).join('|');
for (var line = 0; line < lines.length; line++) {
let dateRegExp = new RegExp("^([0-9]{4})([/]{1})([0-9]{1,2})([/]{1})([0-9]{1,2})");
let isDate = dateRegExp.test(lines[line].substring(0, 10).split(' ')[0]);
if (isDate) {
date = lines[line].slice(0, -4);
} else {
//let tagMatch = lines[line].match(/@.*?(?=[ ]|$)/g); // Match tag by @ and space
let tagMatch = lines[line].match(new RegExp(matchNameRegExp, "g"));
if (tagMatch) {
tagMatch.forEach((tag) => {
let lastCount = table.filter(e => e.name == tag)?.length ?? 0;
table.push({date: date, name: tag, count: lastCount + 1 });
});
}
}
}
// var uniqueNameFromTable = table.map(e => e.name).filter((v, i, a) => a.indexOf(v) === i);
uniqueNameFromTitle.forEach((uniqueName) => {
let item = {
type:"line",
axisYType: "secondary",
name: uniqueName?.replace('@', '') ?? '',
showInLegend: true,
markerSize: 0,
dataPoints: table.filter(e => e.name?.replace('@', '') == uniqueName)?.map(e => ({ x: new Date(e.date), y: Number(e.count)})) ?? []
}
data.push(item);
});
data = data.sort((a, b) => b.dataPoints.length - a.dataPoints.length).slice(0, 12);
var chart = new CanvasJS.Chart("chartContainer", {
title: {
text: "每日標記數累加圖"
},
toolTip: {
shared: true
},
legend: {
cursor: "pointer",
verticalAlign: "top",
horizontalAlign: "center",
dockInsidePlotArea: true,
itemclick: toogleDataSeries
},
data: data
});
chart.render();
function toogleDataSeries(e){
if (typeof(e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
e.dataSeries.visible = false;
} else{
e.dataSeries.visible = true;
}
chart.render();
}
};
reader.readAsText(file);
};
</script>
</body>
</html>
```