@inb/oeb_visualizations 0.0.5 → 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
- 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_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_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);export{accessibilityPlot,citationsPlot};
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 };