@inb/oeb_visualizations 0.0.6-beta → 0.0.7

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