Files: f97d1056daa9154ab13a44d173700ec4f3e94d1a / index.html
4823 bytesRaw
1 | |
2 | <html> |
3 | <head> |
4 | <meta charset=utf-8> |
5 | <title>ssb-activity</title> |
6 | <script src=common.js></script> |
7 | </head> |
8 | <body> |
9 | |
10 | <div style="text-align:center"> |
11 | <h2>Daily active users</h2> |
12 | <img id=graph src=day.png alt="Graph of daily active feeds"> |
13 | </div> |
14 | |
15 | <div id="user-info" style="visibility:hidden"> |
16 | <div>User: <a id="user-link"><code id="user-id"> </code></a></div> |
17 | </div> |
18 | |
19 | <script> |
20 | var feedBase = 'https://viewer.scuttlebot.io/' |
21 | var ids, idColors, idsByColor = {} |
22 | var img = document.getElementById('graph') |
23 | var imgData, origImgData |
24 | var width, height |
25 | var userInfo = document.getElementById('user-info') |
26 | var userLink = document.getElementById('user-link') |
27 | var userIdText = document.getElementById('user-id').firstChild |
28 | |
29 | var colors = { |
30 | yellow: [255, 255, 0, 255], |
31 | red: [255, 0, 0, 255], |
32 | black: [0, 0, 0, 255], |
33 | grey: [200, 200, 200, 255], |
34 | bg: [255, 255, 255, 0], |
35 | highlight: [0, 0, 0, 255] |
36 | } |
37 | |
38 | function getJSON(url, cb) { |
39 | var req = new XMLHttpRequest() |
40 | req.open('GET', url, true) |
41 | req.onload = function () { |
42 | if (req.readyState !== 4) return |
43 | if (req.status >= 200 && req.status < 400) |
44 | cb(null, JSON.parse(req.responseText)) |
45 | else |
46 | return cb(req) |
47 | } |
48 | req.onerror = cb |
49 | req.send(null) |
50 | } |
51 | |
52 | function printError(err) { |
53 | var pre = document.createElement('pre') |
54 | document.body.appendChild(pre) |
55 | pre.appendChild(document.createTextNode(err.stack)) |
56 | } |
57 | |
58 | getJSON('ids.json', function (err, _ids) { |
59 | if (err) return printError(err) |
60 | ids = _ids |
61 | idColors = common.computeIdColors(ids) |
62 | for (var i = 0; i < ids.length; i++) { |
63 | var id = ids[i] |
64 | var color = idColors[id] |
65 | idsByColor[color] = id |
66 | } |
67 | }) |
68 | |
69 | var canvas = document.createElement('canvas') |
70 | var ctx |
71 | function onImageLoad() { |
72 | width = canvas.width = img.width |
73 | height = canvas.height = img.height |
74 | ctx = canvas.getContext('2d') |
75 | ctx.drawImage(img, 0, 0) |
76 | imgData = ctx.getImageData(0, 0, width, height) |
77 | origImgData = ctx.getImageData(0, 0, width, height) |
78 | img.parentNode.insertBefore(canvas, img) |
79 | img.parentNode.removeChild(img) |
80 | canvas.style.maxWidth = '100%' |
81 | } |
82 | if (img.complete) onImageLoad() |
83 | else img.onload = onImageLoad |
84 | |
85 | function getPoint(e) { |
86 | return [ |
87 | Math.floor(e.offsetX * width / canvas.offsetWidth), |
88 | Math.floor(e.offsetY * height / canvas.offsetHeight) |
89 | ] |
90 | } |
91 | |
92 | function colorEquals(a, b) { |
93 | return a && b |
94 | && a[3] === b[3] |
95 | && (a[3] === 0 || ( |
96 | a[0] === b[0] |
97 | && a[1] === b[1] |
98 | && a[2] === b[2] |
99 | )) |
100 | } |
101 | |
102 | function getPixel(x, y) { |
103 | var i = (y * width + x) << 2 |
104 | var data = origImgData.data |
105 | return [ |
106 | data[i], |
107 | data[i+1], |
108 | data[i+2], |
109 | data[i+3] |
110 | ] |
111 | } |
112 | |
113 | function setPixel(x, y, color) { |
114 | var i = (y * width + x) << 2 |
115 | var data = imgData.data |
116 | data[i] = color[0] |
117 | data[i+1] = color[1] |
118 | data[i+2] = color[2] |
119 | data[i+3] = 255 |
120 | } |
121 | |
122 | function replaceColor(oldColor, newColor) { |
123 | for (var x = 0; x < width; x++) { |
124 | for (var y = height-1; y > 0; y--) { |
125 | var c = getPixel(x, y) |
126 | if (colorEquals(c, colors.bg)) break |
127 | if (colorEquals(c, oldColor)) { |
128 | setPixel(x, y, newColor) |
129 | break |
130 | } |
131 | } |
132 | } |
133 | } |
134 | |
135 | function pointEquals(a, b) { |
136 | return a ? b && a[0] === b[0] && a[1] === b[1] : !b |
137 | } |
138 | |
139 | var hoverColor, hoverPoint |
140 | var clickedColor, clickedPoint |
141 | |
142 | function highlight(color, point) { |
143 | if (colorEquals(color, hoverColor)) return |
144 | if (!color) { |
145 | if (hoverColor) replaceColor(hoverColor, hoverColor) |
146 | ctx.putImageData(imgData, 0, 0) |
147 | hoverColor = null |
148 | userInfo.style.visibility = 'hidden' |
149 | return |
150 | } |
151 | if (colorEquals(color, colors.black)) return |
152 | if (colorEquals(color, colors.bg)) return |
153 | if (hoverColor) replaceColor(hoverColor, hoverColor) |
154 | replaceColor(color, colors.highlight) |
155 | hoverColor = color |
156 | ctx.putImageData(imgData, 0, 0) |
157 | var id = idsByColor[color] |
158 | if (!id) { |
159 | userInfo.style.visibility = 'hidden' |
160 | return |
161 | } |
162 | userIdText.nodeValue = id |
163 | userLink.href = feedBase + encodeURIComponent(id) |
164 | userInfo.style.visibility = 'visible' |
165 | } |
166 | |
167 | function clearHoverPoint() { |
168 | highlight(clickedColor, clickedPoint) |
169 | } |
170 | |
171 | function setHoverPoint(point) { |
172 | if (pointEquals(point, hoverPoint)) return |
173 | hoverPoint = point |
174 | var color = getPixel(point[0], point[1]) |
175 | highlight(color, point) |
176 | } |
177 | |
178 | function setClickedPoint(point) { |
179 | if (pointEquals(point, clickedPoint)) return |
180 | var color |
181 | if (point) { |
182 | color = getPixel(point[0], point[1]) |
183 | if (colorEquals(color, colors.bg)) { |
184 | color = null |
185 | point = null |
186 | } |
187 | } |
188 | clickedPoint = point |
189 | clickedColor = color |
190 | highlight(color, point) |
191 | } |
192 | |
193 | canvas.addEventListener('mousemove', function (e) { |
194 | setHoverPoint(getPoint(e)) |
195 | }, false) |
196 | |
197 | |
198 | canvas.addEventListener('mousedown', function (e) { |
199 | setClickedPoint(getPoint(e)) |
200 | }, false) |
201 | |
202 | canvas.addEventListener('mouseout', function (e) { |
203 | clearHoverPoint() |
204 | }, false) |
205 | |
206 | </script> |
207 | </body> |
208 | </html> |
209 |
Built with git-ssb-web