UNPKG

6.91 kBJavaScriptView Raw
1import rasterizeHTML from 'rasterizehtml';
2
3function shadeColor(color, percent) {
4 let f = parseInt(color.slice(1),16)
5 , t = percent < 0 ? 0 : 255
6 , p = percent < 0 ? percent * -1 : percent
7 ;
8 let R = f >> 16
9 , G = f >> 8 & 0x00FF
10 , B = f & 0x0000FF
11 ;
12 return '#' + (
13 0x1000000 + (Math.round((t-R) * p) + R) * 0x10000 +
14 (Math.round((t - G) * p) + G) * 0x100 +
15 (Math.round((t - B) * p) + B)
16 ).toString(16).slice(1);
17}
18
19function collectElements(article, selectors) {
20 let q = '';
21 [ // default
22 ['heading', 'h1,h2,h3,h4,h5,h6']
23 , ['paragraph', 'p']
24 //, ['sentence', ''] FIXME: sentence must be nested in p
25 , ['material', 'ul,ol,pre,table,blockquote']
26 ].forEach(function(d) {
27 let key = d[0];
28 q += ',' + (selectors[key] || d[1]);
29 });
30 return article.querySelectorAll(q.slice(1));
31}
32
33function fetchResultData(
34 endpointURL, csrfToken, resolveCallback, rejectCallback) {
35 let credentials = {csrfToken};
36 if (!credentials.csrfToken) {
37 credentials.csrfToken = '';
38 }
39 let getJSON = (url) => {
40 let emptyData = {'p': []};
41 return new Promise((resolve, reject) => {
42 let xhr = new XMLHttpRequest();
43 xhr.open('GET', url, true);
44 xhr.setRequestHeader('Accept', 'application/json');
45 xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
46 if (credentials.csrfToken !== '') {
47 xhr.setRequestHeader('X-CSRF-Token', credentials.csrfToken);
48 }
49 xhr.responseType = 'text';
50 xhr.onerror = () => {
51 let status = xhr.status;
52 reject(emptyData);
53 };
54 xhr.onload = () => {
55 let status = xhr.status
56 , response = xhr.response
57 ;
58 if (status === 200) {
59 response = response.replace(/^\]\)\}while\(1\);<\/x>/, '');
60 resolve(JSON.parse(response));
61 } else {
62 console.error('[ERROR] GET status: ', status);
63 reject(emptyData);
64 }
65 };
66 xhr.send();
67 });
68 };
69 return getJSON(endpointURL).then((data) => {
70 return resolveCallback(data);
71 }, (data) => {
72 return rejectCallback(data);
73 });
74}
75
76function buildHTML(data, elements) {
77 let html = '<html><head>';
78 html += `
79<style>
80 body {
81 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
82 font-size: 1.66em;
83 }
84</style>
85`;
86 // additional styles
87 // html += Array.from([]).map((s) => {
88 // return s.outerHTML;
89 // }).join('');
90 html += '</head><body>';
91
92 let colors0 = [ // heatmap 11
93 '#2d96db'
94 , '#4aa8a6'
95 , '#64b977'
96 , '#99c95d'
97 , '#c5d062'
98 , '#f6d866'
99 , '#fab252'
100 , '#fd8e3e'
101 , '#fe6f43'
102 , '#fd515b'
103 , '#fb1b2a'
104 ];
105
106 let colors1 = [ // pastel 12
107 '#9e0142'
108 , '#d0384d'
109 , '#ee6445'
110 , '#fa9c58'
111 , '#fdcd7b'
112 , '#fef0a7'
113 , '#f3faad'
114 , '#d0ec9c'
115 , '#98d5a4'
116 , '#5cb7a9'
117 , '#3582ba'
118 , '#5e4fa2'
119 ]
120
121 let colors = colors0 // heatmap (for now)
122 , pIndex = 0
123 ;
124 html += Array.from(elements).map((e) => {
125 let n = document.importNode(e, true);
126 if (n.nodeName !== 'IMG') {
127 if (n.nodeName === 'P' && 'p' in data) {
128 // BETA only paragraph support
129 let v = data['p'][String(pIndex)];
130 if (v !== undefined) {
131 let color = '#ffffff';
132 try {
133 let i = Math.round(parseFloat(v) * 10);
134 color = shadeColor(colors[i], 0.55);
135 } catch(_e) {
136 console.error(e);
137 }
138 n.style.background = color;
139 n.style.backgroundColor = 'rgba(' + color + ', 0.9)';
140 }
141 pIndex += 1;
142 }
143 }
144 return n.outerHTML;
145 }).join('');
146 html += '</body></html>';
147 return html;
148}
149
150function makeCanvas(width, height) {
151 let container = document.getElementById('scrolliris_canvas_container')
152 , canvas = document.createElement('canvas')
153 ;
154 canvas.setAttribute('id', 'scrolliris_canvas');
155 canvas.setAttribute('width', width* 0.5);
156 canvas.setAttribute('height', height * 0.5);
157 container.appendChild(canvas);
158 return canvas;
159}
160
161function drawCanvas(canvas, html, width, height, margin) {
162 let dragging = false
163 , lastY
164 , marginTop = 0
165 , event = {}
166 ;
167
168 canvas.addEventListener('mousedown', (e) => {
169 let evt = e || event;
170 canvas.style.cursor = 'grabbing';
171 dragging = true;
172 lastY = evt.clientY;
173 e.preventDefault();
174 }, false);
175
176 canvas.addEventListener('mouseup', (e) => {
177 canvas.style.cursor = 'grab';
178 dragging = false;
179 }, false);
180
181 rasterizeHTML.drawHTML(html, canvas, {
182 zoom: 0.5
183 , width: width
184 , height: height
185 });
186
187 window.addEventListener('mousemove', (e) => {
188 let evt = e || event;
189 if (dragging) {
190 let delta = evt.clientY - lastY;
191 lastY = evt.clientY;
192 marginTop += delta;
193 if (marginTop > 0) {
194 marginTop = 0;
195 } else if (marginTop < margin) {
196 marginTop = margin;
197 }
198 canvas.style.marginTop = marginTop + 'px';
199 }
200 e.preventDefault();
201 }, false);
202
203 window.addEventListener('mouseout', (e) => {
204 canvas.style.cursor = 'grab';
205 dragging = false;
206 }, false);
207}
208
209((doc, ctx) => {
210 let config = {}
211 , settings = {}
212 , options = {}
213 ;
214
215 if (ctx.hasOwnProperty('config') && typeof ctx.config === 'object') {
216 config = ctx['config'];
217 // pass
218 }
219 if (ctx.hasOwnProperty('settings') && typeof ctx.options === 'object') {
220 settings = ctx['settings'];
221 if (!settings.endpointURL) {
222 console.error('endpointURL is required');
223 return false;
224 }
225 }
226 if (ctx.hasOwnProperty('options') && typeof ctx.options === 'object') {
227 options = ctx['options'];
228 }
229
230 let selectors = (options.selectors || {});
231 let article = doc.querySelector(selectors.article || 'body article');
232
233 // collect elements
234 let elements = collectElements(article, selectors)
235 ;
236
237 // TODO:
238 // Remove magic number(s)
239 let elm = doc.documentElement;
240 let docWidth = Math.max(
241 doc.body.scrollWidth, article.scrollWidth, elm.scrollWidth) / 0.5;
242 let docHeight = Math.max(
243 doc.body.scrollHeight, article.scrollHeight, elm.scrollHeight) / 0.5;
244
245 let draw = (data) => {
246 let html = buildHTML(data, elements)
247 , canvas = makeCanvas(docWidth, docHeight)
248 ;
249 // draw minimap
250 let canvasHeight = 325 // container main area
251 , headerHeight = 22
252 , footerHeiht = 22
253 , frameMargin = 9 // {left|bottom} 9px
254 , scale = 0.5
255 ;
256 let margin = -1 * ((docHeight * scale) / canvasHeight * 100) +
257 (headerHeight + footerHeiht + frameMargin)
258 ;
259 drawCanvas(canvas, html, docWidth, docHeight, margin);
260 };
261
262 fetchResultData(settings.endpointURL, settings.csrfToken, (data) => {
263 // resolve
264 draw(data);
265 }, (data) => { // reject
266 // FIXME
267 draw(data);
268 });
269})(
270 window.parent.document,
271 (window.ScrollirisReadabilityReflector || {}).Context
272);