Completed
Push — master ( f39d31...7d65e7 )
by Chris
01:08
created

jsondash.handlers.handleGraph   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
c 1
b 0
f 1
nc 1
nop 2
dl 0
loc 23
rs 9.0856

1 Function

Rating   Name   Duplication   Size   Complexity  
A 0 20 2
1
/** global: jsondash */
2
/** global: c3 */
3
/** global: d3 */
4
/** global: venn */
5
/** global: Plotly */
6
7
jsondash.getJSON = function(url, callback) {
8
    return d3.json(url, callback);
9
};
10
11
/**
12
 * Handlers for various widget types. The method signatures are always the same,
13
 * but each handler can handle them differently.
14
 */
15
16
jsondash.handlers.handleYoutube = function(container, config) {
17
    // Clean up all previous.
18
    'use strict';
19
    container.selectAll('iframe').remove();
20
21
    function getAttr(prop, props) {
22
        // Return the propery from a list of properties for the iframe.
23
        // e.g. getAttr('width', ["width="900""]) --> "900"
24
        return props.filter(function(k, v){
0 ignored issues
show
Unused Code introduced by
The parameter v is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
25
            return k.startsWith(prop);
26
        })[0];
27
    }
28
29
    var url = config.dataSource;
0 ignored issues
show
Unused Code introduced by
The assignment to variable url seems to be never used. Consider removing it.
Loading history...
30
    var parts = config.dataSource.split(' ');
31
    var height = parseInt(getAttr('height', parts).split('=')[1].replace(/"/gi, ''), 10);
32
    var width = parseInt(getAttr('width', parts).split('=')[1].replace(/"/gi, ''), 10);
33
    var url = getAttr('src', parts).replace('src=', '').replace(/"/gi, '');
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable url already seems to be declared on line 29. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
34
35
    // In the case of YouTube, we have to override the config dimensions
36
    // as this will be wonky when the aspect ratio is calculated. We will
37
    // defer to YouTube calculations instead.
38
    container.append('iframe')
39
        .attr('width', width)
40
        .attr('height', height)
41
        .attr('src', url)
42
        .attr('allowfullscreen', true)
43
        .attr('frameborder', 0);
44
    jsondash.unload(container);
45
};
46
47
/**
48
 * [handleGraph creates graphs using the dot format
49
 * spec with d3 and dagre-d3]
50
 */
51
jsondash.handlers.handleGraph = function(container, config) {
52
    'use strict';
53
    jsondash.getJSON(config.dataSource, function(error, data){
54
        if(error) {
55
            jsondash.unload(container);
56
            throw new Error("Could not load url: " + config.dataSource);
57
        }
58
        container.selectAll('.chart-graph').remove();
59
        var h = config.height - jsondash.config.WIDGET_MARGIN_Y;
0 ignored issues
show
Unused Code introduced by
The variable h seems to be never used. Consider removing it.
Loading history...
60
        var w = config.width - jsondash.config.WIDGET_MARGIN_X;
0 ignored issues
show
Unused Code introduced by
The variable w seems to be never used. Consider removing it.
Loading history...
61
        var svg = container.append('svg').classed({'chart-graph': true});
62
        var svg_group = svg.append('g');
63
        var g = graphlibDot.read(data.graph);
0 ignored issues
show
Bug introduced by
The variable graphlibDot seems to be never declared. If this is a global, consider adding a /** global: graphlibDot */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
64
        var bbox = null;
0 ignored issues
show
Unused Code introduced by
The assignment to bbox seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
65
        // Create the renderer
66
        var render = new dagreD3.render();
0 ignored issues
show
Bug introduced by
The variable dagreD3 seems to be never declared. If this is a global, consider adding a /** global: dagreD3 */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
67
        render(svg_group, g);
68
        bbox = svg.node().getBBox();
69
        svg.attr('width', bbox.width)
70
            .attr('height', bbox.height);
71
        jsondash.unload(container);
72
    });
73
};
74
75
jsondash.handlers.handleC3 = function(container, config) {
76
    'use strict';
77
    var init_config = {
78
        bindto: '[data-guid="' + config.guid + '"] .chart-container',
79
        legend: {
80
            show: true
81
        },
82
        size: {
83
            height: config.height - jsondash.config.WIDGET_MARGIN_Y,
84
            width: config.width - jsondash.config.WIDGET_MARGIN_X
85
        },
86
        data: {
87
            type: config.type,
88
            url: config.dataSource,
89
            mimeType: 'json'
90
        },
91
        onrendered: function(){
92
            jsondash.unload(container);
93
        }
94
    };
95
    if(jsondash.util.isOverride(config)) {
96
        // Just use the raw payload for this widgets' options.
97
        jsondash.getJSON(config.dataSource, function(error, data){
98
            if(error) { throw new Error("Could not load url: " + config.dataSource); }
99
            // Keep existing options if not specified.
100
            config = $.extend(init_config, data);
101
            c3.generate(init_config);
102
        });
103
        return;
104
    }
105
    if(config.type === 'timeseries') {
106
        init_config.axis = {
107
            x: {type: 'timeseries'}
108
        };
109
        // Map the corresponding data key and list of dates
110
        // to the `x` property.
111
        init_config.data.x = 'dates';
112
    }
113
    c3.generate(init_config);
114
};
115
116
jsondash.handlers.handleD3 = function(container, config) {
117
    'use strict';
118
    // Clean up all D3 charts in one step.
119
    container.selectAll('svg').remove();
120
    // Handle specific types.
121
    if(config.type === 'radial-dendrogram') { return jsondash.handlers.handleRadialDendrogram(container, config); }
122
    if(config.type === 'dendrogram') { return jsondash.handlers.handleDendrogram(container, config); }
123
    if(config.type === 'voronoi') { return jsondash.handlers.handleVoronoi(container, config); }
124
    if(config.type === 'treemap') { return jsondash.handlers.handleTreemap(container, config); }
125
    if(config.type === 'circlepack') { return jsondash.handlers.handleCirclePack(container, config); }
126
    throw new Error('Unknown type: ' + config.type);
127
};
128
129
jsondash.handlers.handleCirclePack = function(container, config) {
130
    'use strict';
131
    // Adapted from https://bl.ocks.org/mbostock/4063530
132
    var margin = jsondash.config.WIDGET_MARGIN_Y;
133
    var diameter = d3.max([config.width, config.height]) - margin;
134
    var format = d3.format(',d');
135
136
    var pack = d3.layout.pack()
137
        .size([diameter, diameter])
138
        .value(function(d) { return d.size; });
139
140
    var svg = container
141
        .append('svg')
142
        .attr('width', diameter)
143
        .attr('height', diameter)
144
        .append('g');
145
146
    jsondash.getJSON(config.dataSource, function(error, data) {
147
        if(error) { throw new Error("Could not load url: " + config.dataSource); }
148
149
        var node = svg.datum(data).selectAll('.node')
150
        .data(pack.nodes)
151
        .enter().append('g')
152
        .attr('class', function(d) { return d.children ? 'node' : 'leaf node'; })
153
        .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; });
154
155
        node.append('title')
156
        .text(function(d) { return d.name + (d.children ? '' : ': ' + format(d.size)); });
157
158
        node.append('circle')
159
        .attr('r', function(d) { return d.r; });
160
161
        node.filter(function(d) { return !d.children; }).append('text')
162
        .attr('dy', '.3em')
163
        .style('text-anchor', 'middle')
164
        .text(function(d) { return d.name.substring(0, d.r / 3); });
165
        jsondash.unload(container);
166
    });
167
168
    d3.select(self.frameElement).style("height", diameter + "px");
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
169
};
170
171
jsondash.handlers.handleTreemap = function(container, config) {
172
    'use strict';
173
    // Adapted from http://bl.ocks.org/mbostock/4063582
174
    var margin = {
0 ignored issues
show
Unused Code introduced by
The variable margin seems to be never used. Consider removing it.
Loading history...
175
        top: jsondash.config.WIDGET_MARGIN_Y / 2,
176
        bottom: jsondash.config.WIDGET_MARGIN_Y / 2,
177
        left: jsondash.config.WIDGET_MARGIN_X / 2,
178
        right: jsondash.config.WIDGET_MARGIN_X / 2
179
    };
180
    var width = config.width - jsondash.config.WIDGET_MARGIN_X;
181
    var height = config.height - jsondash.config.WIDGET_MARGIN_Y;
182
    var color = d3.scale.category20c();
183
    var treemap = d3.layout.treemap()
184
        .size([width, height])
185
        .sticky(true)
186
        .value(function(d) { return d.size; });
187
    // Cleanup
188
    container.selectAll('.treemap').remove();
189
    var div = container
190
        .append('div')
191
        .classed({treemap: true, 'chart-centered': true})
192
        .style('position', 'relative')
193
        .style('width', width + 'px')
194
        .style('height', height + 'px');
195
196
    jsondash.getJSON(config.dataSource, function(error, root) {
197
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
198
        var node = div.datum(root).selectAll('.node')
199
            .data(treemap.nodes)
200
            .enter().append('div')
201
            .attr('class', 'node')
202
            .call(position)
203
            .style('border', '1px solid white')
204
            .style('font', '10px sans-serif')
205
            .style('line-height', '12px')
206
            .style('overflow', 'hidden')
207
            .style('position', 'absolute')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
208
            .style('text-indent', '2px')
209
            .style('background', function(d) {
210
                return d.children ? color(d.name) : null;
211
            })
212
            .text(function(d) {
213
                return d.children ? null : d.name;
214
            });
215
        d3.selectAll('input').on('change', function change() {
216
            var value = this.value === 'count'
217
            ? function() { return 1; }
218
            : function(d) { return d.size;};
219
            node
220
            .data(treemap.value(value).nodes)
221
            .transition()
222
            .duration(1500)
223
            .call(position);
224
        });
225
        jsondash.unload(container);
226
    });
227
228
    function position() {
229
        this.style('left', function(d) { return d.x + 'px'; })
230
            .style('top', function(d) { return d.y + 'px'; })
231
            .style('width', function(d) { return Math.max(0, d.dx - 1) + 'px'; })
232
            .style('height', function(d) { return Math.max(0, d.dy - 1) + 'px'; });
233
    }
234
};
235
236
jsondash.handlers.handleRadialDendrogram = function(container, config) {
237
    'use strict';
238
    container.selectAll('svg').remove();
239
    // Code taken (and refactored for use here) from:
240
    // https://bl.ocks.org/mbostock/4339607
241
    var padding = 50;
242
    var radius = (config.width > config.height ? config.width : config.height) - padding;
243
    var cluster = d3.layout.cluster()
244
        .size([360, radius / 2 - 150]); // reduce size relative to `radius`
245
    var diagonal = d3.svg.diagonal.radial()
246
        .projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
247
    var svg = container.append('svg')
248
        .attr('width', radius)
249
        .attr('height', radius);
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
250
    var g = svg.append('g');
251
    g.attr('transform', 'translate(' + radius / 2 + ',' + radius / 2 + ')');
252
253
    jsondash.getJSON(config.dataSource, function(error, root) {
254
        if (error) { throw error; }
255
        var nodes = cluster.nodes(root);
256
        var link = g.selectAll('path.link')
0 ignored issues
show
Unused Code introduced by
The variable link seems to be never used. Consider removing it.
Loading history...
257
            .data(cluster.links(nodes))
258
            .enter().append('path')
259
            .attr('class', 'link')
260
            .attr('d', diagonal);
261
        var node = g.selectAll('g.node')
262
            .data(nodes)
263
            .enter().append('g')
264
            .attr('class', 'node')
265
            .attr('transform', function(d) { return 'rotate(' + (d.x - 90) + ')translate(' + d.y + ')'; });
266
        node.append('circle')
267
            .attr('r', 4.5);
268
        node.append('text')
269
            .attr('dy', '.31em')
270
            .attr('text-anchor', function(d) { return d.x < 180 ? 'start' : 'end'; })
271
            .attr('transform', function(d) { return d.x < 180 ? 'translate(8)' : 'rotate(180)translate(-8)'; })
272
            .text(function(d) { return d.name; });
273
        jsondash.unload(container);
274
    });
275
    d3.select(self.frameElement).style('height', radius * 2 + 'px');
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
276
};
277
278
jsondash.handlers.handleDendrogram = function(container, config) {
279
    'use strict';
280
    container.selectAll('svg').remove();
281
    // A general padding for the svg inside of the widget.
282
    // The cluster dendrogram will also need to have padding itself, so
283
    // the bounds are not clipped in the svg.
284
    var svg_pad = 20;
285
    var width = config.width - svg_pad;
286
    var height = config.height - svg_pad;
287
    var PADDING = width / 4;
288
    var cluster = d3.layout.cluster()
289
        .size([height * 0.85, width - PADDING]);
290
    var diagonal = d3.svg.diagonal()
291
        .projection(function(d) { return [d.y, d.x]; });
292
    var svg = container
293
        .append('svg')
294
        .attr('width', width)
295
        .attr('height', height);
296
    var g = svg.append('g')
297
        .attr('transform', 'translate(40, 0)');
298
299
    jsondash.getJSON(config.dataSource, function(error, root) {
300
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
301
302
        var nodes = cluster.nodes(root);
303
        var links = cluster.links(nodes);
304
        var link = g.selectAll('.link')
0 ignored issues
show
Unused Code introduced by
The variable link seems to be never used. Consider removing it.
Loading history...
305
        .data(links)
306
        .enter().append('path')
307
        .attr('class', 'link')
308
        .attr('d', diagonal);
309
310
        var node = g.selectAll('.node')
311
        .data(nodes)
312
        .enter().append('g')
313
        .attr('class', 'node')
314
        .attr('transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; });
315
316
        node.append('circle').attr('r', 4.5);
317
        node.append('text')
318
        .attr('dx', function(d) { return d.children ? -8 : 8; })
319
        .attr('dy', 3)
320
        .style('text-anchor', function(d) { return d.children ? 'end' : 'start'; })
321
        .text(function(d) { return d.name; });
322
323
        jsondash.unload(container);
324
    });
325
};
326
327
jsondash.handlers.handleVoronoi = function(container, config) {
328
    'use strict';
329
    jsondash.getJSON(config.dataSource, function(error, data){
330
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
331
        var width = config.width - jsondash.config.WIDGET_MARGIN_X;
332
        var height = config.height - jsondash.config.WIDGET_MARGIN_Y;
333
        var vertices = data;
334
        var voronoi = d3.geom.voronoi().clipExtent([[0, 0], [width, height]]);
335
        // Cleanup
336
        var svg = container
337
        .append('svg')
338
        .attr('width', width)
339
        .attr('height', height);
340
        var path = svg.append('g').selectAll('path');
341
        svg.selectAll('circle')
342
        .data(vertices.slice(1))
343
        .enter().append('circle')
344
        .attr('transform', function(d) { return 'translate(' + d + ')'; })
345
        .attr('r', 1.5);
346
        redraw();
347
348
        function redraw() {
349
            path = path.data(voronoi(vertices), jsondash.util.polygon);
350
            path.exit().remove();
351
            path.enter().append('path')
352
            .attr('class', function(d, i) { return 'q' + (i % 9) + '-9'; })
353
            .attr('d', jsondash.util.polygon);
354
            path.order();
355
        }
356
        jsondash.unload(container);
357
    });
358
};
359
360
jsondash.handlers.handleSparkline = function(container, config) {
361
    'use strict';
362
    // Clean up old canvas elements
363
    container.selectAll('.sparkline-container').remove();
364
    var sparkline_type = config.type.split('-')[1];
365
    var spark = container
366
        .append('div')
367
        .classed({
368
            'sparkline-container': true,
369
            'text-center': true
370
        });
371
    spark = $(spark[0]);
372
    jsondash.getJSON(config.dataSource, function(data){
373
        var opts = {
374
            type: sparkline_type,
375
            width: config.width - jsondash.config.WIDGET_MARGIN_X,
376
            height: config.height - jsondash.config.WIDGET_MARGIN_Y
377
        };
378
        spark.sparkline(data, opts);
379
        jsondash.unload(container);
380
    });
381
};
382
383
jsondash.handlers.handleDataTable = function(container, config) {
384
    'use strict';
385
    // Clean up old tables if they exist, during reloading.
386
    container.selectAll('.dataTables_wrapper').remove();
387
    jsondash.getJSON(config.dataSource, function(error, res) {
388
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
389
        var keys = d3.keys(res[0]).map(function(d){
390
            return {data: d, title: d};
391
        });
392
        container
393
            .append('table')
394
            .classed({
395
                table: true,
396
                'table-striped': true,
397
                'table-bordered': true,
398
                'table-condensed': true
399
            });
400
        var opts = config.override ? res : {data: res, columns: keys};
401
        $(container.select('table')[0]).dataTable(opts).css({width: 'auto'});
402
        jsondash.unload(container);
403
    });
404
};
405
406
jsondash.handlers.handleSingleNum = function(container, config) {
407
    'use strict';
408
    container.selectAll('.singlenum').remove();
409
    jsondash.getJSON(config.dataSource, function(data){
410
        var num = container.append('div')
411
            .classed({singlenum: true})
412
            .text(data);
413
        data = String(data);
414
        // Add red or green, depending on if the number appears to be pos/neg.
415
        num.classed({
416
            'text-danger': data.startsWith('-'),
417
            'text-success': !data.startsWith('-')
418
        });
419
        // Get title height to offset box.
420
        var title_h = container
421
            .select('.widget-title')
422
            .node()
423
            .getBoundingClientRect()
424
            .height;
425
        var inner_box_height = config.height - title_h; // factor in rough height of title.
426
        container.style({
427
            'line-height': inner_box_height + 'px',
428
            height: inner_box_height + 'px'
429
        });
430
        var digits = String(data).length;
431
        var size = jsondash.util.getDigitSize()(digits);
432
        num.style('font-size', size + 'px');
433
        jsondash.unload(container);
434
    });
435
};
436
437
jsondash.handlers.handleTimeline = function(container, config) {
438
    'use strict';
439
    jsondash.getJSON(config.dataSource, function(data){
440
        container.append('div').attr('id', 'widget-' + config.guid);
441
        var timeline = new TL.Timeline('widget-' + config.guid, data);
0 ignored issues
show
Bug introduced by
The variable TL seems to be never declared. If this is a global, consider adding a /** global: TL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Unused Code introduced by
The variable timeline seems to be never used. Consider removing it.
Loading history...
442
        jsondash.unload(container);
443
    });
444
};
445
446
jsondash.handlers.handleIframe = function(container, config) {
447
    'use strict';
448
    container.selectAll('iframe').remove();
449
    var iframe = container.append('iframe');
450
    iframe.attr({
451
        border: 0,
452
        src: config.dataSource,
453
        height: '100%',
454
        width: '100%'
455
    });
456
    jsondash.unload(container);
457
};
458
459
jsondash.handlers.handleCustom = function(container, config) {
460
    'use strict';
461
    container.selectAll('.custom-container').remove();
462
    $.get(config.dataSource, function(html){
463
        container.append('div').classed({'custom-container': true}).html(html);
464
        jsondash.unload(container);
465
    });
466
};
467
468
jsondash.handlers.handleVenn = function(container, config) {
469
    'use strict';
470
    container.selectAll('.venn').remove();
471
    jsondash.getJSON(config.dataSource, function(error, data){
472
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
473
        var chart = venn.VennDiagram();
474
        var cont = container
475
            .append('div')
476
            .classed({venn: true});
477
        cont.datum(data).call(chart);
478
        cont.select('svg')
479
            .attr('width', config.width - jsondash.config.WIDGET_MARGIN_X)
480
            .attr('height', config.height - jsondash.config.WIDGET_MARGIN_Y);
481
        jsondash.unload(container);
482
    });
483
};
484
485
jsondash.handlers.handlePlotly = function(container, config) {
486
    'use strict';
487
    var id = 'plotly-' + config.guid;
488
    container.selectAll('.plotly-container').remove();
489
    container.append('div')
490
        .classed({'plotly-container': true})
491
        .attr('id', id);
492
    jsondash.getJSON(config.dataSource, function(error, data){
493
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
494
        if(config.override) {
495
            if(data.layout && data.layout.margin) {
496
                // Remove margins, they mess up the
497
                // layout and are already accounted for.
498
                delete data.layout['margin'];
499
            }
500
            Plotly.plot(id, data.data, data.layout || {}, data.options || {});
501
        } else {
502
            Plotly.plot(id, data);
503
        }
504
        d3.select('#' + id).select('.svg-container').style({'margin': '0 auto'})
505
        jsondash.unload(container);
506
    });
507
};
508