@inb/oeb_visualizations 0.0.6-beta → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1,786 @@
|
|
|
1
|
-
|
|
1
|
+
import Plotly from 'plotly.js-dist';
|
|
2
|
+
|
|
3
|
+
function randstr(prefix) {
|
|
4
|
+
return Math.random().toString(36).replace('0.', prefix || '');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
//
|
|
8
|
+
var script$1 = {
|
|
9
|
+
name: 'accessibilityPlot',
|
|
10
|
+
data: () => ({
|
|
11
|
+
divId: randstr('acc_plot')
|
|
12
|
+
}),
|
|
13
|
+
props: {
|
|
14
|
+
dtick: {
|
|
15
|
+
/*
|
|
16
|
+
dtick is the interval between ticks on the x axis in ms.
|
|
17
|
+
*/
|
|
18
|
+
type: Number,
|
|
19
|
+
required: false,
|
|
20
|
+
default: 86400000.0
|
|
21
|
+
},
|
|
22
|
+
xaxisTitle: {
|
|
23
|
+
/*
|
|
24
|
+
xaxisTitle is the title of the x axis.
|
|
25
|
+
*/
|
|
26
|
+
type: String,
|
|
27
|
+
required: false,
|
|
28
|
+
default: 'Date'
|
|
29
|
+
},
|
|
30
|
+
yaxisTitle: {
|
|
31
|
+
/*
|
|
32
|
+
yaxisTitle is the title of the y axis.
|
|
33
|
+
*/
|
|
34
|
+
type: String,
|
|
35
|
+
required: false,
|
|
36
|
+
default: 'Access time (ms)'
|
|
37
|
+
},
|
|
38
|
+
colorOnline: {
|
|
39
|
+
/*
|
|
40
|
+
colorOnline is the color of the online line.
|
|
41
|
+
format: "<R>, <G>, <B>"
|
|
42
|
+
*/
|
|
43
|
+
type: String,
|
|
44
|
+
required: false,
|
|
45
|
+
default: '111, 176, 129'
|
|
46
|
+
},
|
|
47
|
+
colorOffline: {
|
|
48
|
+
/*
|
|
49
|
+
colorOffline is the color of the offline bars.
|
|
50
|
+
format: "<R>, <G>, <B>"
|
|
51
|
+
*/
|
|
52
|
+
type: String,
|
|
53
|
+
required: false,
|
|
54
|
+
default: '255, 153, 145'
|
|
55
|
+
},
|
|
56
|
+
colorNA: {
|
|
57
|
+
/*
|
|
58
|
+
colorNA is the color of the NA bars.
|
|
59
|
+
format: "<R>, <G>, <B>"
|
|
60
|
+
*/
|
|
61
|
+
type: String,
|
|
62
|
+
required: false,
|
|
63
|
+
default: '204,204,204'
|
|
64
|
+
},
|
|
65
|
+
height: {
|
|
66
|
+
/*
|
|
67
|
+
height is the height of the plot in px.
|
|
68
|
+
*/
|
|
69
|
+
type: Number,
|
|
70
|
+
required: false,
|
|
71
|
+
default: 350
|
|
72
|
+
},
|
|
73
|
+
dataItems: {
|
|
74
|
+
/*
|
|
75
|
+
dataItems is an array of objects with keys "access_time", "date" and "code".
|
|
76
|
+
- access_time is the time it took to access the server in ms.
|
|
77
|
+
- date is the date of the access.
|
|
78
|
+
- code is the HTTP code returned by the server.
|
|
79
|
+
Each object represents an access to the server.
|
|
80
|
+
*/
|
|
81
|
+
type: Array,
|
|
82
|
+
required: true,
|
|
83
|
+
validator: function (value) {
|
|
84
|
+
/*
|
|
85
|
+
This function validates the dataItems prop. It throws a console error if the prop is not valid.
|
|
86
|
+
TODO: apply vue-types to define the prop.
|
|
87
|
+
https://github.com/dwightjack/vue-types
|
|
88
|
+
*/
|
|
89
|
+
for (let i = 0; i < value.length; i++) {
|
|
90
|
+
// The value must be an array of objects with keys "access_time", "date" and "code"
|
|
91
|
+
if (!value[i].hasOwnProperty('access_time')) {
|
|
92
|
+
console.error(`[oeb-visualizations warn] access_time key is missing in dataItems prop item (at position ${i})`);
|
|
93
|
+
} else if (!value[i].hasOwnProperty('date')) {
|
|
94
|
+
console.error(`[oeb-visualizations warn] date key is missing in dataItems prop item (at position ${i})`);
|
|
95
|
+
} else if (!value[i].hasOwnProperty('code')) {
|
|
96
|
+
console.error(`[oeb-visualizations warn] code key is missing in dataItems prop item (at position ${i})`);
|
|
97
|
+
}
|
|
98
|
+
// Access time must be null or a number
|
|
99
|
+
if (value[i].access_time !== null && typeof value[i].access_time !== 'number') {
|
|
100
|
+
console.error(`[oeb-visualizations warn] access_time must be null or a number in dataItems prop item (at position ${i})`);
|
|
101
|
+
}
|
|
102
|
+
// Code must be null or a number
|
|
103
|
+
if (value[i].code !== null && typeof value[i].code !== 'number') {
|
|
104
|
+
console.error(`[oeb-visualizations warn] code must be null or a number in dataItems prop item (at position ${i})`);
|
|
105
|
+
}
|
|
106
|
+
// Date must be a string containing a date
|
|
107
|
+
if (typeof value[i].date !== 'string' || isNaN(Date.parse(value[i].date))) {
|
|
108
|
+
console.error(`[oeb-visualizations warn] date must be a string containing a date in dataItems prop item (at position ${i})`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// And date cannot be null
|
|
112
|
+
if (value[i].date === null) {
|
|
113
|
+
console.error(`[oeb-visualizations warn] date cannot be null in dataItems prop item (at position ${i})`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
mounted() {
|
|
121
|
+
var traces = this.buildOnlineTraces(this.dataItems); // generate line traces
|
|
122
|
+
|
|
123
|
+
traces = traces.concat(this.buildOfflineNATraces(this.dataItems)); // generate bar traces
|
|
124
|
+
|
|
125
|
+
const layout = {
|
|
126
|
+
bargap: 0.0,
|
|
127
|
+
barmode: 'stack',
|
|
128
|
+
showlegend: true,
|
|
129
|
+
autosize: true,
|
|
130
|
+
height: this.height,
|
|
131
|
+
margin: {
|
|
132
|
+
l: 50,
|
|
133
|
+
r: 50,
|
|
134
|
+
b: 70,
|
|
135
|
+
t: 70,
|
|
136
|
+
pad: 4
|
|
137
|
+
},
|
|
138
|
+
xaxis: {
|
|
139
|
+
type: 'date',
|
|
140
|
+
title: this.xaxisTitle,
|
|
141
|
+
font: {
|
|
142
|
+
size: 10
|
|
143
|
+
},
|
|
144
|
+
tickfont: {
|
|
145
|
+
size: 10
|
|
146
|
+
},
|
|
147
|
+
tickmode: 'linear',
|
|
148
|
+
tick0: this.dataItems[0].date,
|
|
149
|
+
dtick: this.dtick,
|
|
150
|
+
tickangle: 45,
|
|
151
|
+
tickformat: "%d %b"
|
|
152
|
+
},
|
|
153
|
+
yaxis: {
|
|
154
|
+
title: this.yaxisTitle,
|
|
155
|
+
titlefont: {
|
|
156
|
+
size: 10
|
|
157
|
+
},
|
|
158
|
+
tickfont: {
|
|
159
|
+
size: 10
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
template: 'plotly_white',
|
|
163
|
+
legend: {
|
|
164
|
+
orientation: 'h',
|
|
165
|
+
yanchor: 'bottom',
|
|
166
|
+
y: 1.02,
|
|
167
|
+
xanchor: 'right',
|
|
168
|
+
x: 1,
|
|
169
|
+
font: {
|
|
170
|
+
size: 8
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
hoverlabel: {
|
|
174
|
+
bgcolor: "#FFF"
|
|
175
|
+
},
|
|
176
|
+
hovermode: 'closest'
|
|
177
|
+
};
|
|
178
|
+
Plotly.newPlot(this.divId, traces, layout);
|
|
179
|
+
},
|
|
180
|
+
methods: {
|
|
181
|
+
generateColor(values, transparency) {
|
|
182
|
+
/*
|
|
183
|
+
Parameters:
|
|
184
|
+
- values: array of values. Values must be in ["up", "down", "NA"]
|
|
185
|
+
- transparency: float between 0 and 1
|
|
186
|
+
Returns an array of colors in the form "rgba(255, 153, 145,0.8)"
|
|
187
|
+
*/
|
|
188
|
+
let colors = [];
|
|
189
|
+
for (let i = 0; i < 30; i++) {
|
|
190
|
+
switch (values[i]) {
|
|
191
|
+
case "up":
|
|
192
|
+
colors.push(`rgba(${this.colorOnline},0)`);
|
|
193
|
+
break;
|
|
194
|
+
case "down":
|
|
195
|
+
colors.push(`rgba(${this.colorOffline},${transparency})`);
|
|
196
|
+
break;
|
|
197
|
+
case "NA":
|
|
198
|
+
colors.push(`rgba(${this.colorNA},${transparency})`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return colors;
|
|
202
|
+
},
|
|
203
|
+
extractSubarraysBetweenNullValues(data) {
|
|
204
|
+
/*
|
|
205
|
+
This function extracts subarrays of access_time between null values.
|
|
206
|
+
This is, consecutive access_time values that are not null and separated by null values.
|
|
207
|
+
It also creates the subarrays of dates for those access_time values.
|
|
208
|
+
It also computes the average access time of the whole data.
|
|
209
|
+
Parameters:
|
|
210
|
+
- data: array of objects with keys "access_time" and "date". access_time can be null
|
|
211
|
+
Returns an object with keys:
|
|
212
|
+
- access_time: array of arrays of access_time values
|
|
213
|
+
- date: array of arrays of dates
|
|
214
|
+
- average_access_time: average access time of the whole data
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
var subarrays = {
|
|
218
|
+
access_time: [],
|
|
219
|
+
date: [],
|
|
220
|
+
average_access_time: 0
|
|
221
|
+
};
|
|
222
|
+
var subarrayTime = [];
|
|
223
|
+
var subarrayDate = [];
|
|
224
|
+
var sum = 0;
|
|
225
|
+
var nonNullValues = 0;
|
|
226
|
+
for (let i = 0; i < data.length; i++) {
|
|
227
|
+
if (data[i].access_time !== null) {
|
|
228
|
+
// keep adding to subarray
|
|
229
|
+
subarrayTime.push(data[i].access_time);
|
|
230
|
+
subarrayDate.push(data[i].date);
|
|
231
|
+
sum += data[i].access_time;
|
|
232
|
+
nonNullValues += 1;
|
|
233
|
+
} else {
|
|
234
|
+
// Push subarray to subarrays and reset subarray
|
|
235
|
+
if (subarrayTime.length > 0) {
|
|
236
|
+
subarrays.access_time.push(subarrayTime);
|
|
237
|
+
subarrays.date.push(subarrayDate);
|
|
238
|
+
subarrayTime = [];
|
|
239
|
+
subarrayDate = [];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// push last subarray
|
|
244
|
+
subarrays.access_time.push(subarrayTime);
|
|
245
|
+
subarrays.date.push(subarrayDate);
|
|
246
|
+
|
|
247
|
+
// compute average access time
|
|
248
|
+
subarrays.average_access_time = sum / nonNullValues;
|
|
249
|
+
return subarrays;
|
|
250
|
+
},
|
|
251
|
+
buildAccessTimeTraces(subarrays) {
|
|
252
|
+
/*
|
|
253
|
+
This function builds the line traces of access time between null values.
|
|
254
|
+
|
|
255
|
+
Arguments:
|
|
256
|
+
- subarrays: object with keys "access_time" and "date".
|
|
257
|
+
- access_time is an array of arrays of access_time values.
|
|
258
|
+
- date is an array of arrays of dates.
|
|
259
|
+
Returns an array of line traces
|
|
260
|
+
*/
|
|
261
|
+
|
|
262
|
+
var traces = []; // stores resulting traces
|
|
263
|
+
|
|
264
|
+
// iterate over subarrays and build line traces
|
|
265
|
+
for (let i = 0; i < subarrays.access_time.length; i++) {
|
|
266
|
+
const subarray = subarrays.access_time[i];
|
|
267
|
+
const subarrayDate = subarrays.date[i];
|
|
268
|
+
const showlegend = i === 0 ? true : false;
|
|
269
|
+
const trace = {
|
|
270
|
+
x: subarrayDate,
|
|
271
|
+
y: subarray,
|
|
272
|
+
name: 'Online',
|
|
273
|
+
legendgroup: 'up',
|
|
274
|
+
showlegend: showlegend,
|
|
275
|
+
mode: 'markers+lines',
|
|
276
|
+
type: 'scatter',
|
|
277
|
+
fill: 'tozeroy',
|
|
278
|
+
fillcolor: `rgba(${this.colorOnline},.2)`,
|
|
279
|
+
connectgaps: false,
|
|
280
|
+
line: {
|
|
281
|
+
color: `rgba(${this.colorOnline},.8)`,
|
|
282
|
+
width: 1.5
|
|
283
|
+
},
|
|
284
|
+
marker: {
|
|
285
|
+
size: 5
|
|
286
|
+
},
|
|
287
|
+
hovertemplate: '<b>Online</b><br>%{x|%d %b %Y}<br>%{y} ms <extra></extra>',
|
|
288
|
+
hoveron: 'points+fills'
|
|
289
|
+
};
|
|
290
|
+
traces.push(trace);
|
|
291
|
+
}
|
|
292
|
+
return traces;
|
|
293
|
+
},
|
|
294
|
+
buildAvgAccessTimeTrace(avgAccessTime) {
|
|
295
|
+
/*
|
|
296
|
+
This function builds the line trace of the average access time of the whole data.
|
|
297
|
+
The trace is an horizontal line at y=average access time.
|
|
298
|
+
Arguments:
|
|
299
|
+
- avgAccessTime: average access time of the whole data
|
|
300
|
+
Returns a line trace
|
|
301
|
+
*/
|
|
302
|
+
|
|
303
|
+
// set start and end dates so that the line spands the whole plot
|
|
304
|
+
const firstDate = new Date(this.dataItems[0].date);
|
|
305
|
+
firstDate.setDate(firstDate.getDate() - 1); // one day before the first date in data
|
|
306
|
+
|
|
307
|
+
const lastDate = new Date(this.dataItems[this.dataItems.length - 1].date);
|
|
308
|
+
lastDate.setDate(lastDate.getDate() + 1); // one day after the last date in data
|
|
309
|
+
|
|
310
|
+
const trace = {
|
|
311
|
+
x: [firstDate, lastDate],
|
|
312
|
+
y: [avgAccessTime, avgAccessTime],
|
|
313
|
+
name: 'Average access time',
|
|
314
|
+
showlegend: true,
|
|
315
|
+
mode: 'lines',
|
|
316
|
+
type: 'scatter',
|
|
317
|
+
line: {
|
|
318
|
+
color: `rgba(${this.colorOnline},.6)`,
|
|
319
|
+
width: 1.5,
|
|
320
|
+
dash: '4px'
|
|
321
|
+
},
|
|
322
|
+
marker: {
|
|
323
|
+
size: 5
|
|
324
|
+
},
|
|
325
|
+
// avg access time with 2 decimals
|
|
326
|
+
hovertemplate: '<b>Average access time</b><br>%{y:.2f} ms <extra></extra>'
|
|
327
|
+
};
|
|
328
|
+
return trace;
|
|
329
|
+
},
|
|
330
|
+
buildOnlineTraces(data) {
|
|
331
|
+
/*
|
|
332
|
+
This function builds one trace per subarray of access_time between null values.
|
|
333
|
+
It also builds the trace of the average access time of the whole data.
|
|
334
|
+
[i] Why not just one trace for access_time?
|
|
335
|
+
[i] Because if we do that, the area under null values is filled too.
|
|
336
|
+
Arguments:
|
|
337
|
+
- data: array of objects with keys "access_time" and "date". access_time can be null
|
|
338
|
+
Returns an array of traces
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
const subarrays = this.extractSubarraysBetweenNullValues(data);
|
|
342
|
+
var traces = this.buildAccessTimeTraces(subarrays);
|
|
343
|
+
traces.push(this.buildAvgAccessTimeTrace(subarrays.average_access_time));
|
|
344
|
+
return traces;
|
|
345
|
+
},
|
|
346
|
+
extractOfflineNADates(data) {
|
|
347
|
+
/*
|
|
348
|
+
This function extracts dates with access_time null.
|
|
349
|
+
Depending on the code, the server is offline or NA on those dates.
|
|
350
|
+
Arguments:
|
|
351
|
+
- data: array of objects with keys "access_time", "date" and "code". access_time and code can be null
|
|
352
|
+
|
|
353
|
+
Returns an object with keys:
|
|
354
|
+
- NA: array of dates with access_time null and code null
|
|
355
|
+
- down: array of dates with access_time null and code in errorCodes
|
|
356
|
+
*/
|
|
357
|
+
const errorCodes = [404, 500, 502, 503, 504];
|
|
358
|
+
const resultNA = [];
|
|
359
|
+
const resultOffline = [];
|
|
360
|
+
for (let i = 0; i < data.length; i++) {
|
|
361
|
+
// if the access_time is null and the code is null, it means that the monitoring was down
|
|
362
|
+
if (data[i].access_time === null && data[i].code === null) {
|
|
363
|
+
resultNA.push(data[i].date);
|
|
364
|
+
// if the access_time is null and the code is in errorCodes, it means that the server is offline
|
|
365
|
+
} else if (data[i].access_time === null && errorCodes.includes(data[i].code)) {
|
|
366
|
+
resultOffline.push(data[i].date);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
NA: resultNA,
|
|
371
|
+
down: resultOffline
|
|
372
|
+
};
|
|
373
|
+
},
|
|
374
|
+
barTrace(data, name, showlegend, hoverinfo, hovertemplate) {
|
|
375
|
+
/*
|
|
376
|
+
This function builds a bar trace.
|
|
377
|
+
Arguments:
|
|
378
|
+
- data: object with keys "dates" and "access_times"
|
|
379
|
+
- dates: array of dates
|
|
380
|
+
- access_times: array of access_times
|
|
381
|
+
- colors: array of colors in rgba format (eg:"rgba(255, 153, 145,0.8)")
|
|
382
|
+
- name: name of the trace
|
|
383
|
+
*/
|
|
384
|
+
const trace = {
|
|
385
|
+
x: data.dates,
|
|
386
|
+
y: data.access_times,
|
|
387
|
+
marker: {
|
|
388
|
+
color: data.colors
|
|
389
|
+
},
|
|
390
|
+
name: name,
|
|
391
|
+
type: "bar",
|
|
392
|
+
legendgroup: name,
|
|
393
|
+
showlegend: showlegend,
|
|
394
|
+
hoverinfo: hoverinfo,
|
|
395
|
+
hovertemplate: hovertemplate
|
|
396
|
+
};
|
|
397
|
+
return trace;
|
|
398
|
+
},
|
|
399
|
+
buildBarTraces(dates, color, label) {
|
|
400
|
+
/*
|
|
401
|
+
This function builds the series used to display the offline/NA bars.
|
|
402
|
+
For each date, two columns are created: one very short and one tall.
|
|
403
|
+
These columns cover the whole plot height.
|
|
404
|
+
The tall column is colored with colorStrong and the short one with colorLight.
|
|
405
|
+
Arguments:
|
|
406
|
+
- dates: array of dates
|
|
407
|
+
- color: color of the bars
|
|
408
|
+
Returns array with offline and NA traces
|
|
409
|
+
*/
|
|
410
|
+
|
|
411
|
+
const colorStrong = `rgba(${color},.8)`;
|
|
412
|
+
const colorLight = `rgba(${color},.2)`;
|
|
413
|
+
const dataShort = {
|
|
414
|
+
dates: dates,
|
|
415
|
+
access_times: Array(dates.length).fill(2),
|
|
416
|
+
colors: Array(dates.length).fill(colorStrong)
|
|
417
|
+
};
|
|
418
|
+
const dataTall = {
|
|
419
|
+
dates: dates,
|
|
420
|
+
access_times: Array(dates.length).fill(120),
|
|
421
|
+
colors: Array(dates.length).fill(colorLight)
|
|
422
|
+
};
|
|
423
|
+
const hovertemplate = `<b>${label}</b><br>%{x|%d %b %Y}<br>%{y} ms <extra></extra>`;
|
|
424
|
+
const traceShort = this.barTrace(dataShort, label, false, 'skip', '');
|
|
425
|
+
const traceTall = this.barTrace(dataTall, label, true, 'all', hovertemplate);
|
|
426
|
+
return [traceShort, traceTall];
|
|
427
|
+
},
|
|
428
|
+
buildOfflineNATraces(data) {
|
|
429
|
+
/*
|
|
430
|
+
This function builds the bar traces of offline and NA values.
|
|
431
|
+
Arguments:
|
|
432
|
+
- data: array of objects with keys "access_time", "date" and "code". access_time and code can be null
|
|
433
|
+
|
|
434
|
+
Returns an array of bar traces
|
|
435
|
+
*/
|
|
436
|
+
|
|
437
|
+
var traces = [];
|
|
438
|
+
const arrays = this.extractOfflineNADates(data);
|
|
439
|
+
|
|
440
|
+
// Down arrays
|
|
441
|
+
traces = traces.concat(this.buildBarTraces(arrays.down, this.colorOffline, 'Offline'));
|
|
442
|
+
|
|
443
|
+
// NA arrays
|
|
444
|
+
traces = traces.concat(this.buildBarTraces(arrays.NA, this.colorNA, 'No information available'));
|
|
445
|
+
return traces;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
|
|
451
|
+
/* server only */
|
|
452
|
+
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
|
|
453
|
+
if (typeof shadowMode !== 'boolean') {
|
|
454
|
+
createInjectorSSR = createInjector;
|
|
455
|
+
createInjector = shadowMode;
|
|
456
|
+
shadowMode = false;
|
|
457
|
+
} // Vue.extend constructor export interop.
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
var options = typeof script === 'function' ? script.options : script; // render functions
|
|
461
|
+
|
|
462
|
+
if (template && template.render) {
|
|
463
|
+
options.render = template.render;
|
|
464
|
+
options.staticRenderFns = template.staticRenderFns;
|
|
465
|
+
options._compiled = true; // functional template
|
|
466
|
+
|
|
467
|
+
if (isFunctionalTemplate) {
|
|
468
|
+
options.functional = true;
|
|
469
|
+
}
|
|
470
|
+
} // scopedId
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
if (scopeId) {
|
|
474
|
+
options._scopeId = scopeId;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
var hook;
|
|
478
|
+
|
|
479
|
+
if (moduleIdentifier) {
|
|
480
|
+
// server build
|
|
481
|
+
hook = function hook(context) {
|
|
482
|
+
// 2.3 injection
|
|
483
|
+
context = context || // cached call
|
|
484
|
+
this.$vnode && this.$vnode.ssrContext || // stateful
|
|
485
|
+
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
|
|
486
|
+
// 2.2 with runInNewContext: true
|
|
487
|
+
|
|
488
|
+
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
|
|
489
|
+
context = __VUE_SSR_CONTEXT__;
|
|
490
|
+
} // inject component styles
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
if (style) {
|
|
494
|
+
style.call(this, createInjectorSSR(context));
|
|
495
|
+
} // register component module identifier for async chunk inference
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
if (context && context._registeredComponents) {
|
|
499
|
+
context._registeredComponents.add(moduleIdentifier);
|
|
500
|
+
}
|
|
501
|
+
}; // used by ssr in case component is cached and beforeCreate
|
|
502
|
+
// never gets called
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
options._ssrRegister = hook;
|
|
506
|
+
} else if (style) {
|
|
507
|
+
hook = shadowMode ? function () {
|
|
508
|
+
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot));
|
|
509
|
+
} : function (context) {
|
|
510
|
+
style.call(this, createInjector(context));
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (hook) {
|
|
515
|
+
if (options.functional) {
|
|
516
|
+
// register for functional component in vue file
|
|
517
|
+
var originalRender = options.render;
|
|
518
|
+
|
|
519
|
+
options.render = function renderWithStyleInjection(h, context) {
|
|
520
|
+
hook.call(context);
|
|
521
|
+
return originalRender(h, context);
|
|
522
|
+
};
|
|
523
|
+
} else {
|
|
524
|
+
// inject component registration as beforeCreate hook
|
|
525
|
+
var existing = options.beforeCreate;
|
|
526
|
+
options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return script;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
var normalizeComponent_1 = normalizeComponent;
|
|
534
|
+
|
|
535
|
+
/* script */
|
|
536
|
+
const __vue_script__$1 = script$1;
|
|
537
|
+
|
|
538
|
+
/* template */
|
|
539
|
+
var __vue_render__$1 = function () {
|
|
540
|
+
var _vm = this;
|
|
541
|
+
var _h = _vm.$createElement;
|
|
542
|
+
var _c = _vm._self._c || _h;
|
|
543
|
+
return _c('div', {
|
|
544
|
+
attrs: {
|
|
545
|
+
"id": _vm.divId
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
};
|
|
549
|
+
var __vue_staticRenderFns__$1 = [];
|
|
550
|
+
|
|
551
|
+
/* style */
|
|
552
|
+
const __vue_inject_styles__$1 = undefined;
|
|
553
|
+
/* scoped */
|
|
554
|
+
const __vue_scope_id__$1 = undefined;
|
|
555
|
+
/* module identifier */
|
|
556
|
+
const __vue_module_identifier__$1 = undefined;
|
|
557
|
+
/* functional template */
|
|
558
|
+
const __vue_is_functional_template__$1 = false;
|
|
559
|
+
/* style inject */
|
|
560
|
+
|
|
561
|
+
/* style inject SSR */
|
|
562
|
+
|
|
563
|
+
var accessibilityPlot = normalizeComponent_1({
|
|
564
|
+
render: __vue_render__$1,
|
|
565
|
+
staticRenderFns: __vue_staticRenderFns__$1
|
|
566
|
+
}, __vue_inject_styles__$1, __vue_script__$1, __vue_scope_id__$1, __vue_is_functional_template__$1, __vue_module_identifier__$1, undefined, undefined);
|
|
567
|
+
|
|
568
|
+
//
|
|
569
|
+
var script = {
|
|
570
|
+
name: 'citationsPlot',
|
|
571
|
+
data: () => ({
|
|
572
|
+
divId: randstr('cit_plot')
|
|
573
|
+
}),
|
|
574
|
+
props: {
|
|
575
|
+
dataTraces: {
|
|
576
|
+
/*
|
|
577
|
+
dataTraces is and array of data to be plotted. Each element of the array is an object with the following structure:
|
|
578
|
+
TODO: apply vue-types to define the prop
|
|
579
|
+
https://github.com/dwightjack/vue-types
|
|
580
|
+
{
|
|
581
|
+
data: array, // required
|
|
582
|
+
id: string, // required
|
|
583
|
+
label: string, // optional
|
|
584
|
+
title: string, // optional
|
|
585
|
+
year: number, // optional
|
|
586
|
+
url: string // optional
|
|
587
|
+
}
|
|
588
|
+
*/
|
|
589
|
+
type: Array,
|
|
590
|
+
required: true
|
|
591
|
+
},
|
|
592
|
+
stack: {
|
|
593
|
+
type: Boolean,
|
|
594
|
+
required: false,
|
|
595
|
+
default: false
|
|
596
|
+
},
|
|
597
|
+
colors: {
|
|
598
|
+
type: Array,
|
|
599
|
+
required: false,
|
|
600
|
+
default: () => ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
|
|
601
|
+
},
|
|
602
|
+
height: {
|
|
603
|
+
type: Number,
|
|
604
|
+
required: false,
|
|
605
|
+
default: 400
|
|
606
|
+
},
|
|
607
|
+
showlegend: {
|
|
608
|
+
type: Boolean,
|
|
609
|
+
required: false,
|
|
610
|
+
default: true
|
|
611
|
+
},
|
|
612
|
+
title: {
|
|
613
|
+
type: String,
|
|
614
|
+
required: false,
|
|
615
|
+
default: ''
|
|
616
|
+
},
|
|
617
|
+
xaxisTitle: {
|
|
618
|
+
type: String,
|
|
619
|
+
required: false,
|
|
620
|
+
default: 'Year'
|
|
621
|
+
},
|
|
622
|
+
yaxisTitle: {
|
|
623
|
+
type: String,
|
|
624
|
+
required: false,
|
|
625
|
+
default: 'Number of citations'
|
|
626
|
+
},
|
|
627
|
+
darkMode: {
|
|
628
|
+
type: Boolean,
|
|
629
|
+
required: false,
|
|
630
|
+
default: false
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
mounted() {
|
|
634
|
+
const traces = this.buildTraces();
|
|
635
|
+
const layout = {
|
|
636
|
+
showlegend: this.showlegend,
|
|
637
|
+
autosize: true,
|
|
638
|
+
height: this.height,
|
|
639
|
+
margin: {
|
|
640
|
+
l: 50,
|
|
641
|
+
r: 50,
|
|
642
|
+
b: 70,
|
|
643
|
+
t: 50,
|
|
644
|
+
pad: 4
|
|
645
|
+
},
|
|
646
|
+
xaxis: {
|
|
647
|
+
title: this.xaxisTitle,
|
|
648
|
+
font: {
|
|
649
|
+
size: 10
|
|
650
|
+
},
|
|
651
|
+
tickfont: {
|
|
652
|
+
size: 10
|
|
653
|
+
},
|
|
654
|
+
tickmode: 'linear',
|
|
655
|
+
color: this.darkMode ? 'white' : 'black'
|
|
656
|
+
},
|
|
657
|
+
yaxis: {
|
|
658
|
+
title: this.yaxisTitle,
|
|
659
|
+
titlefont: {
|
|
660
|
+
size: 10
|
|
661
|
+
},
|
|
662
|
+
tickfont: {
|
|
663
|
+
size: 10
|
|
664
|
+
},
|
|
665
|
+
font: {
|
|
666
|
+
size: 10
|
|
667
|
+
},
|
|
668
|
+
color: this.darkMode ? 'white' : 'black'
|
|
669
|
+
},
|
|
670
|
+
legend: {
|
|
671
|
+
orientation: 'h',
|
|
672
|
+
yanchor: 'bottom',
|
|
673
|
+
y: 1.02,
|
|
674
|
+
xanchor: 'right',
|
|
675
|
+
x: 1,
|
|
676
|
+
font: {
|
|
677
|
+
size: 8,
|
|
678
|
+
color: this.darkMode ? 'white' : 'black'
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
hoverlabel: {
|
|
682
|
+
color: this.darkMode ? 'white' : 'black'
|
|
683
|
+
},
|
|
684
|
+
hovermode: this.stack ? 'x unified' : 'closest',
|
|
685
|
+
hoverdistance: 70,
|
|
686
|
+
plot_bgcolor: this.darkMode ? "rgb(38, 50, 56)" : "white",
|
|
687
|
+
paper_bgcolor: this.darkMode ? "rgb(38, 50, 56)" : "white"
|
|
688
|
+
};
|
|
689
|
+
Plotly.newPlot(this.divId, traces, layout);
|
|
690
|
+
},
|
|
691
|
+
methods: {
|
|
692
|
+
buildTraces() {
|
|
693
|
+
const traces = [];
|
|
694
|
+
// build traces for object in dataTraces
|
|
695
|
+
for (let i = 0; i < this.dataTraces.length; i++) {
|
|
696
|
+
const trace = {
|
|
697
|
+
x: this.dataTraces[i].data.map(d => d.year),
|
|
698
|
+
y: this.dataTraces[i].data.map(d => d.citations),
|
|
699
|
+
mode: 'lines+markers',
|
|
700
|
+
name: this.dataTraces[i].label,
|
|
701
|
+
hovertemplate: this.hoverTemplate(),
|
|
702
|
+
marker: {
|
|
703
|
+
size: 5
|
|
704
|
+
},
|
|
705
|
+
line: {
|
|
706
|
+
color: this.colors[i],
|
|
707
|
+
width: 1.8
|
|
708
|
+
},
|
|
709
|
+
stackgroup: this.stack ? 'one' : null
|
|
710
|
+
};
|
|
711
|
+
traces.push(trace);
|
|
712
|
+
}
|
|
713
|
+
return traces;
|
|
714
|
+
},
|
|
715
|
+
hoverTemplate() {
|
|
716
|
+
if (this.stack) {
|
|
717
|
+
return "%{y} citations <extra></extra>";
|
|
718
|
+
} else {
|
|
719
|
+
return "%{y} citations in %{x} <extra></extra>";
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
/* script */
|
|
726
|
+
const __vue_script__ = script;
|
|
727
|
+
|
|
728
|
+
/* template */
|
|
729
|
+
var __vue_render__ = function () {
|
|
730
|
+
var _vm = this;
|
|
731
|
+
var _h = _vm.$createElement;
|
|
732
|
+
var _c = _vm._self._c || _h;
|
|
733
|
+
return _c('div', {
|
|
734
|
+
attrs: {
|
|
735
|
+
"id": _vm.divId
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
};
|
|
739
|
+
var __vue_staticRenderFns__ = [];
|
|
740
|
+
|
|
741
|
+
/* style */
|
|
742
|
+
const __vue_inject_styles__ = undefined;
|
|
743
|
+
/* scoped */
|
|
744
|
+
const __vue_scope_id__ = undefined;
|
|
745
|
+
/* module identifier */
|
|
746
|
+
const __vue_module_identifier__ = undefined;
|
|
747
|
+
/* functional template */
|
|
748
|
+
const __vue_is_functional_template__ = false;
|
|
749
|
+
/* style inject */
|
|
750
|
+
|
|
751
|
+
/* style inject SSR */
|
|
752
|
+
|
|
753
|
+
var citationsPlot = normalizeComponent_1({
|
|
754
|
+
render: __vue_render__,
|
|
755
|
+
staticRenderFns: __vue_staticRenderFns__
|
|
756
|
+
}, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, undefined, undefined);
|
|
757
|
+
|
|
758
|
+
var components = /*#__PURE__*/Object.freeze({
|
|
759
|
+
__proto__: null,
|
|
760
|
+
accessibilityPlot: accessibilityPlot,
|
|
761
|
+
citationsPlot: citationsPlot
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
// install function executed by Vue.use()
|
|
765
|
+
const install = function installVueOEBViz(Vue) {
|
|
766
|
+
if (install.installed) return;
|
|
767
|
+
install.installed = true;
|
|
768
|
+
Object.entries(components).forEach(([componentName, component]) => {
|
|
769
|
+
Vue.component(componentName, component);
|
|
770
|
+
});
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
// Create module definition for Vue.use()
|
|
774
|
+
const plugin = {
|
|
775
|
+
install
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
/*
|
|
779
|
+
if (typeof Vue !== 'undefined') {
|
|
780
|
+
for (const name in components) {
|
|
781
|
+
Vue.component(name, components[name])
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
*/
|
|
785
|
+
|
|
786
|
+
export { accessibilityPlot, citationsPlot, plugin as default };
|