channel-plot.html 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <title>TS3 channel usage</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="flot/excanvas.min.js"></script><![endif]-->
  8. <script language="javascript" type="text/javascript" src="flot/jquery.min.js"></script>
  9. <script language="javascript" type="text/javascript" src="flot/jquery.flot.min.js"></script>
  10. <script language="javascript" type="text/javascript" src="flot/jquery.flot.stack.min.js"></script>
  11. <script language="javascript" type="text/javascript" src="flot/jquery.flot.time.min.js"></script>
  12. <script language="javascript" type="text/javascript" src="flot/jquery.flot.navigate.min.js"></script>
  13. <script language="javascript" type="text/javascript" src="flot/jquery.flot.resize.min.js"></script>
  14. </head>
  15. <body>
  16. <div id="graphs"></div>
  17. <script>
  18. var startDate, endDate;
  19. var eventTimeRange = [0, 0];
  20. function updateDate(newDate) {
  21. newDate.setHours(6, 0, 0, 0);
  22. if (newDate.getTime() > eventTimeRange[1])
  23. newDate = new Date(eventTimeRange[1]);
  24. if (newDate.getTime() < eventTimeRange[0])
  25. newDate = new Date(eventTimeRange[0]);
  26. newDate.setHours(6, 0, 0, 0);
  27. startDate = newDate;
  28. endDate = addDays(startDate, 1);
  29. updatePlots();
  30. }
  31. function update(init = false) {
  32. fetch("ajax.php").then(res => res.json()).then(function (json) {
  33. let urlParams = new URLSearchParams(window.location.search);
  34. const channelId = parseInt(urlParams.get('channelId'));
  35. const div = document.getElementById("graphs");
  36. div.style.width = urlParams.get('width') || "600px";
  37. div.style.height = urlParams.get('height') || "300px";
  38. const serverName = json.channels ? json.channels[0].serverName : "Server";
  39. const channel = json.channels.filter(ch => ch.channelId == channelId)[0];
  40. if(!channel) {
  41. console.log("channel", channelId, "not found");
  42. return;
  43. }
  44. let channelTree = channel;
  45. eventTimeRange[0] = json.events.reduce((min, e) => min && (min < e[1]) ? min : e[1]) * 1000 - 12 * 3600000;
  46. eventTimeRange[1] = json.events.reduce((max, e) => max && (max > e[1]) ? max : e[1]) * 1000 + 12 * 3600000;
  47. if (init)
  48. updateDate(new Date());
  49. makeTree(channelTree, json.channels, json.events);
  50. if (init) {
  51. plotEvents(channel, div, true);
  52. }
  53. console.log(channelTree);
  54. });
  55. }
  56. update(true);
  57. setInterval(update, 30 * 1000);
  58. function makeTree(tree, channels, events) {
  59. tree.events = events.filter(e => e[0] == tree.id);
  60. if (tree.events.length > 0) {
  61. tree.events.push([tree.events[tree.events.length - 1][0], (new Date()).getTime() / 1000, tree.events[tree.events.length - 1][2]]);
  62. }
  63. tree.children = channels.filter((ch) => ch.parentId == tree.channelId).sort((a, b) => a.position - b.position);
  64. tree.clientCount = tree.events.reduce((sum, e) => sum + e[2], 0) / (tree.events.length > 0 ? tree.events.length : 1);
  65. const recentActivity = tree.events.filter(e => e[1] > (new Date()).getTime() / 1000 - 3600 * 24);
  66. tree.activeCount = recentActivity.length > 2 ? recentActivity.reduce((acc, val) => val[2] > acc ? val[2] : acc, 0) : 0;
  67. for (const child of tree.children) {
  68. makeTree(child, channels.filter(ch => ch.parentId != tree.channelId), events.filter(e => e[0] != tree.id));
  69. tree.clientCount += child.clientCount;
  70. tree.activeCount += child.activeCount;
  71. }
  72. }
  73. function formatHeadings(str, html = true) {
  74. const res = /(?:\[(.)spacer[^\]]*\])?(.*)/.exec(str);
  75. if (!html)
  76. return res[2];
  77. switch (res[1]) {
  78. case '*':
  79. return `<div class='channel'>${res[2].repeat(50)}</div>`;
  80. case 'l':
  81. return `<div class='channel' align='left'>${res[2]}</div>`;
  82. case 'c':
  83. return `<div class='channel' align='center'>${res[2]}</div>`;
  84. case 'r':
  85. return `<div class='channel' align='right'>${res[2]}</div>`;
  86. default:
  87. return `<div class='channel'>${res[2]}</div>`;
  88. }
  89. }
  90. var plots = {};
  91. var viewMin = 0;
  92. var viewMax = 0;
  93. function plotEvents(tree, div, visible = true) {
  94. let placeholder = document.getElementById(`graph_${tree.channelId}`);
  95. if (!placeholder) {
  96. placeholder = document.createElement("div");
  97. placeholder.classList.add("demo-placeholder");
  98. placeholder.id = `graph_${tree.channelId}`;
  99. placeholder.style.height = div.style.height;
  100. placeholder.style.width = div.style.width;
  101. div.appendChild(placeholder);
  102. }
  103. let series = [];
  104. function iterChilds(tree, prefix = "") {
  105. const data = tree.events.map(e => [e[1] * 1000, e[2]]);
  106. const name = prefix + formatHeadings(tree.name, false);
  107. const maxClients = data.reduce((acc, val) => val[1] > acc ? val[1] : acc, 0);
  108. if (data.length > 0 && maxClients > 0) {
  109. series.push({
  110. label: name,
  111. data: data,
  112. lines: {
  113. show: true,
  114. fill: true,
  115. steps: true
  116. }
  117. });
  118. }
  119. for (const channel of tree.children) {
  120. iterChilds(channel, formatHeadings(tree.name, false) + ' / ');
  121. }
  122. }
  123. iterChilds(tree);
  124. if (!plots[tree.channelId]) {
  125. plots[tree.channelId] = $.plot(placeholder, series, {
  126. xaxis: {
  127. mode: "time",
  128. timeBase: "milliseconds",
  129. timezone: "browser",
  130. min: viewMin ? viewMin : startDate.getTime(),
  131. max: viewMax ? viewMax : endDate.getTime(),
  132. zoomRange: [60000, null],
  133. panRange: eventTimeRange
  134. },
  135. yaxis: {
  136. zoomRange: false,
  137. panRange: false,
  138. min: 0,
  139. tickDecimals: 0
  140. },
  141. zoom: {
  142. interactive: true
  143. },
  144. pan: {
  145. interactive: true
  146. },
  147. legend: {
  148. position: 'nw'
  149. }
  150. });
  151. plots[tree.channelId].hooks.drawOverlay.push(function (plot, cvs) {
  152. if (!plot) { return; }
  153. var cvsWidth = plot.width() / 2;
  154. var text = tree.name.replace(/\[\/?[^\]]+\]/g, "");
  155. cvs.font = "bold 16px Arial";
  156. cvs.fillStyle = "#666666";
  157. cvs.textAlign = 'center';
  158. cvs.fillText(text, cvsWidth, 30);
  159. return cvs;
  160. });
  161. $(placeholder).bind("plotpan plotzoom", function (event, plot) {
  162. var axes = plot.getAxes();
  163. viewMin = axes.xaxis.min;
  164. viewMax = axes.xaxis.max;
  165. for (const id in plots) {
  166. plots[id].getOptions().xaxes[0].min = axes.xaxis.min;
  167. plots[id].getOptions().xaxes[0].max = axes.xaxis.max;
  168. plots[id].setupGrid();
  169. plots[id].draw();
  170. }
  171. });
  172. } else {
  173. plots[tree.channelId].getOptions().xaxis.panRange = eventTimeRange;
  174. plots[tree.channelId].setData(series);
  175. plots[tree.channelId].setupGrid();
  176. plots[tree.channelId].draw();
  177. }
  178. }
  179. function updatePlots() {
  180. viewMin = startDate.getTime();
  181. viewMax = endDate.getTime();
  182. for (const id in plots) {
  183. let opt = plots[id].getXAxes()[0].options;
  184. opt.min = viewMin;
  185. opt.max = viewMax;
  186. if (!document.getElementById(`cb_${id}`).checked)
  187. continue;
  188. plots[id].setupGrid();
  189. plots[id].draw();
  190. }
  191. }
  192. function addDays(date, days) {
  193. var result = new Date(date);
  194. result.setDate(result.getDate() + days);
  195. return result;
  196. }
  197. </script>
  198. </body>
  199. </html>