Skip to content

Commit e9ce87e

Browse files
authored
Merge pull request #233 from vector-im/bwindels/log-viewer
Log viewer
2 parents 7e05e4e + 69feb40 commit e9ce87e

File tree

4 files changed

+502
-0
lines changed

4 files changed

+502
-0
lines changed

scripts/logviewer/file.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Copyright 2020 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
export function openFile(mimeType = null) {
18+
const input = document.createElement("input");
19+
input.setAttribute("type", "file");
20+
input.className = "hidden";
21+
if (mimeType) {
22+
input.setAttribute("accept", mimeType);
23+
}
24+
const promise = new Promise((resolve, reject) => {
25+
const checkFile = () => {
26+
input.removeEventListener("change", checkFile, true);
27+
const file = input.files[0];
28+
document.body.removeChild(input);
29+
if (file) {
30+
resolve(file);
31+
} else {
32+
reject(new Error("no file picked"));
33+
}
34+
}
35+
input.addEventListener("change", checkFile, true);
36+
});
37+
// IE11 needs the input to be attached to the document
38+
document.body.appendChild(input);
39+
input.click();
40+
return promise;
41+
}
42+
43+
export function readFileAsText(file) {
44+
const reader = new FileReader();
45+
const promise = new Promise((resolve, reject) => {
46+
reader.addEventListener("load", evt => resolve(evt.target.result));
47+
reader.addEventListener("error", evt => reject(evt.target.error));
48+
});
49+
reader.readAsText(file);
50+
return promise;
51+
}

scripts/logviewer/html.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
Copyright 2020 Bruno Windels <[email protected]>
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// DOM helper functions
18+
19+
export function isChildren(children) {
20+
// children should be an not-object (that's the attributes), or a domnode, or an array
21+
return typeof children !== "object" || !!children.nodeType || Array.isArray(children);
22+
}
23+
24+
export function classNames(obj, value) {
25+
return Object.entries(obj).reduce((cn, [name, enabled]) => {
26+
if (typeof enabled === "function") {
27+
enabled = enabled(value);
28+
}
29+
if (enabled) {
30+
return cn + (cn.length ? " " : "") + name;
31+
} else {
32+
return cn;
33+
}
34+
}, "");
35+
}
36+
37+
export function setAttribute(el, name, value) {
38+
if (name === "className") {
39+
name = "class";
40+
}
41+
if (value === false) {
42+
el.removeAttribute(name);
43+
} else {
44+
if (value === true) {
45+
value = name;
46+
}
47+
el.setAttribute(name, value);
48+
}
49+
}
50+
51+
export function el(elementName, attributes, children) {
52+
return elNS(HTML_NS, elementName, attributes, children);
53+
}
54+
55+
export function elNS(ns, elementName, attributes, children) {
56+
if (attributes && isChildren(attributes)) {
57+
children = attributes;
58+
attributes = null;
59+
}
60+
61+
const e = document.createElementNS(ns, elementName);
62+
63+
if (attributes) {
64+
for (let [name, value] of Object.entries(attributes)) {
65+
if (name === "className" && typeof value === "object" && value !== null) {
66+
value = classNames(value);
67+
}
68+
setAttribute(e, name, value);
69+
}
70+
}
71+
72+
if (children) {
73+
if (!Array.isArray(children)) {
74+
children = [children];
75+
}
76+
for (let c of children) {
77+
if (!c.nodeType) {
78+
c = text(c);
79+
}
80+
e.appendChild(c);
81+
}
82+
}
83+
return e;
84+
}
85+
86+
export function text(str) {
87+
return document.createTextNode(str);
88+
}
89+
90+
export const HTML_NS = "http://www.w3.org/1999/xhtml";
91+
export const SVG_NS = "http://www.w3.org/2000/svg";
92+
93+
export const TAG_NAMES = {
94+
[HTML_NS]: [
95+
"br", "a", "ol", "ul", "li", "div", "h1", "h2", "h3", "h4", "h5", "h6",
96+
"p", "strong", "em", "span", "img", "section", "main", "article", "aside",
97+
"pre", "button", "time", "input", "textarea", "label", "form", "progress", "output"],
98+
[SVG_NS]: ["svg", "circle"]
99+
};
100+
101+
export const tag = {};
102+
103+
104+
for (const [ns, tags] of Object.entries(TAG_NAMES)) {
105+
for (const tagName of tags) {
106+
tag[tagName] = function(attributes, children) {
107+
return elNS(ns, tagName, attributes, children);
108+
}
109+
}
110+
}

scripts/logviewer/index.html

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<style type="text/css">
6+
html, body {
7+
height: 100%;
8+
}
9+
10+
body {
11+
font-family: sans-serif;
12+
font-size: 1rem;
13+
margin: 0;
14+
display: grid;
15+
grid-template-areas: "nav nav" "items details";
16+
grid-template-columns: 1fr 400px;
17+
grid-template-rows: auto 1fr;
18+
min-height: 0;
19+
}
20+
21+
main {
22+
grid-area: items;
23+
min-width: 0;
24+
min-height: 0;
25+
overflow-y: auto;
26+
padding: 8px;
27+
}
28+
29+
main section h2 {
30+
margin: 2px 14px;
31+
font-size: 1rem;
32+
}
33+
34+
aside {
35+
grid-area: details;
36+
padding: 8px;
37+
}
38+
39+
aside h3 {
40+
word-wrap: anywhere;
41+
}
42+
43+
aside p {
44+
margin: 2px 0;
45+
}
46+
47+
aside .values li span {
48+
word-wrap: ;
49+
word-wrap: anywhere;
50+
}
51+
52+
aside .values {
53+
list-style: none;
54+
padding: 0;
55+
border: 1px solid lightgray;
56+
}
57+
58+
aside .values span.key {
59+
width: 30%;
60+
display: block;
61+
}
62+
63+
aside .values span.value {
64+
width: 70%;
65+
display: block;
66+
padding-left: 10px;
67+
}
68+
69+
aside .values li {
70+
display: flex;
71+
}
72+
73+
aside .values li:not(:first-child) {
74+
border-top: 1px solid lightgray;
75+
}
76+
77+
nav {
78+
grid-area: nav;
79+
}
80+
81+
.timeline li:not(.expanded) > ol {
82+
display: none;
83+
}
84+
85+
.timeline li > div {
86+
display: flex;
87+
}
88+
89+
.timeline .toggleExpanded {
90+
border: none;
91+
background: none;
92+
width: 24px;
93+
height: 24px;
94+
margin-right: 4px;
95+
cursor: pointer;
96+
}
97+
98+
.timeline .toggleExpanded:before {
99+
content: "▶";
100+
}
101+
102+
.timeline li.expanded > div > .toggleExpanded:before {
103+
content: "▼";
104+
}
105+
106+
.timeline ol {
107+
list-style: none;
108+
padding: 0 0 0 20px;
109+
margin: 0;
110+
}
111+
112+
.timeline div.item {
113+
--hue: 100deg;
114+
--brightness: 80%;
115+
background-color: hsl(var(--hue), 60%, var(--brightness));
116+
border: 1px solid hsl(var(--hue), 60%, calc(var(--brightness) - 40%));
117+
border-radius: 4px;
118+
padding: 2px;
119+
display: flex;
120+
margin: 1px;
121+
flex: 1;
122+
min-width: 0;
123+
cursor: pointer;
124+
}
125+
126+
127+
.timeline div.item:not(.has-children) {
128+
margin-left: calc(24px + 4px + 1px);
129+
}
130+
131+
.timeline div.item .caption {
132+
white-space: nowrap;
133+
text-overflow: ellipsis;
134+
overflow: hidden;
135+
flex: 1;
136+
}
137+
138+
.timeline div.item.level-3 {
139+
--brightness: 90%;
140+
}
141+
142+
.timeline div.item.level-6 {
143+
--hue: 0deg !important;
144+
}
145+
146+
.timeline div.item.type-network {
147+
--hue: 30deg;
148+
}
149+
150+
.timeline div.item.selected {
151+
background-color: Highlight;
152+
border-color: Highlight;
153+
color: HighlightText;
154+
}
155+
156+
.hidden {
157+
display: none;
158+
}
159+
</style>
160+
</head>
161+
<body>
162+
<nav><button id="openFile">Open log file</button></nav>
163+
<main></main>
164+
<aside></aside>
165+
<script type="module" src="main.js"></script>
166+
</body>
167+
</html>

0 commit comments

Comments
 (0)