Completed
Push — master ( a10da3...25c806 )
by Chris
01:12
created

jsondash.handlers.handleGraph   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
nc 1
nop 2

1 Function

Rating   Name   Duplication   Size   Complexity  
A 0 21 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;
60
        var w = config.width - jsondash.config.WIDGET_MARGIN_X;
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
        svg.attr('width', w).attr('height', h);
68
        svg_group.attr('transform', jsondash.util.translateStr(20, 20));
69
        render(svg_group, g);
70
        bbox = svg.node().getBBox();
71
        svg.attr('width', bbox.width + 30).attr('height', bbox.height + 30);
72
        jsondash.unload(container);
73
    });
74
};
75
76
jsondash.handlers.handleC3 = function(container, config) {
77
    'use strict';
78
    var init_config = {
79
        bindto: '[data-guid="' + config.guid + '"] .chart-container',
80
        legend: {
81
            show: true
82
        },
83
        size: {
84
            height: config.height - jsondash.config.WIDGET_MARGIN_Y,
85
            width: config.width - jsondash.config.WIDGET_MARGIN_X
86
        },
87
        data: {
88
            type: config.type,
89
            url: config.dataSource,
90
            mimeType: 'json'
91
        },
92
        onrendered: function(){
93
            jsondash.unload(container);
94
        }
95
    };
96
    if(jsondash.util.isOverride(config)) {
97
        // Just use the raw payload for this widgets' options.
98
        jsondash.getJSON(config.dataSource, function(error, data){
99
            if(error) { throw new Error("Could not load url: " + config.dataSource); }
100
            // Keep existing options if not specified.
101
            config = $.extend(init_config, data);
102
            c3.generate(init_config);
103
        });
104
        return;
105
    }
106
    if(config.type === 'timeseries') {
107
        init_config.axis = {
108
            x: {type: 'timeseries'}
109
        };
110
        // Map the corresponding data key and list of dates
111
        // to the `x` property.
112
        init_config.data.x = 'dates';
113
    }
114
    c3.generate(init_config);
115
};
116
117
jsondash.handlers.handleD3 = function(container, config) {
118
    'use strict';
119
    // Clean up all D3 charts in one step.
120
    container.selectAll('svg').remove();
121
    // Handle specific types.
122
    if(config.type === 'radial-dendrogram') { return jsondash.handlers.handleRadialDendrogram(container, config); }
123
    if(config.type === 'dendrogram') { return jsondash.handlers.handleDendrogram(container, config); }
124
    if(config.type === 'voronoi') { return jsondash.handlers.handleVoronoi(container, config); }
125
    if(config.type === 'treemap') { return jsondash.handlers.handleTreemap(container, config); }
126
    if(config.type === 'circlepack') { return jsondash.handlers.handleCirclePack(container, config); }
127
    throw new Error('Unknown type: ' + config.type);
128
};
129
130
jsondash.handlers.handleCirclePack = function(container, config) {
131
    'use strict';
132
    // Adapted from https://bl.ocks.org/mbostock/4063530
133
    var margin = jsondash.config.WIDGET_MARGIN_Y;
134
    var diameter = d3.max([config.width, config.height]) - margin;
135
    var format = d3.format(',d');
136
137
    var pack = d3.layout.pack()
138
        .size([diameter, diameter])
139
        .value(function(d) { return d.size; });
140
141
    var svg = container
142
        .append('svg')
143
        .attr('width', diameter)
144
        .attr('height', diameter)
145
        .append('g');
146
147
    jsondash.getJSON(config.dataSource, function(error, data) {
148
        if(error) { throw new Error("Could not load url: " + config.dataSource); }
149
150
        var node = svg.datum(data).selectAll('.node')
151
        .data(pack.nodes)
152
        .enter().append('g')
153
        .attr('class', function(d) { return d.children ? 'node' : 'leaf node'; })
154
        .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; });
155
156
        node.append('title')
157
        .text(function(d) { return d.name + (d.children ? '' : ': ' + format(d.size)); });
158
159
        node.append('circle')
160
        .attr('r', function(d) { return d.r; });
161
162
        node.filter(function(d) { return !d.children; }).append('text')
163
        .attr('dy', '.3em')
164
        .style('text-anchor', 'middle')
165
        .text(function(d) { return d.name.substring(0, d.r / 3); });
166
        jsondash.unload(container);
167
    });
168
169
    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...
170
};
171
172
jsondash.handlers.handleTreemap = function(container, config) {
173
    'use strict';
174
    // Adapted from http://bl.ocks.org/mbostock/4063582
175
    var margin = {
0 ignored issues
show
Unused Code introduced by
The variable margin seems to be never used. Consider removing it.
Loading history...
176
        top: jsondash.config.WIDGET_MARGIN_Y / 2,
177
        bottom: jsondash.config.WIDGET_MARGIN_Y / 2,
178
        left: jsondash.config.WIDGET_MARGIN_X / 2,
179
        right: jsondash.config.WIDGET_MARGIN_X / 2
180
    };
181
    var width = config.width - jsondash.config.WIDGET_MARGIN_X;
182
    var height = config.height - jsondash.config.WIDGET_MARGIN_Y;
183
    var color = d3.scale.category20c();
184
    var treemap = d3.layout.treemap()
185
        .size([width, height])
186
        .sticky(true)
187
        .value(function(d) { return d.size; });
188
    // Cleanup
189
    container.selectAll('.treemap').remove();
190
    var div = container
191
        .append('div')
192
        .classed({treemap: true, 'chart-centered': true})
193
        .style('position', 'relative')
194
        .style('width', width + 'px')
195
        .style('height', height + 'px');
196
197
    jsondash.getJSON(config.dataSource, function(error, root) {
198
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
199
        var node = div.datum(root).selectAll('.node')
200
            .data(treemap.nodes)
201
            .enter().append('div')
202
            .attr('class', 'node')
203
            .call(position)
204
            .style('border', '1px solid white')
205
            .style('font', '10px sans-serif')
206
            .style('line-height', '12px')
207
            .style('overflow', 'hidden')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
208
            .style('position', 'absolute')
209
            .style('text-indent', '2px')
210
            .style('background', function(d) {
211
                return d.children ? color(d.name) : null;
212
            })
213
            .text(function(d) {
214
                return d.children ? null : d.name;
215
            });
216
        d3.selectAll('input').on('change', function change() {
217
            var value = this.value === 'count'
218
            ? function() { return 1; }
219
            : function(d) { return d.size;};
220
            node
221
            .data(treemap.value(value).nodes)
222
            .transition()
223
            .duration(1500)
224
            .call(position);
225
        });
226
        jsondash.unload(container);
227
    });
228
229
    function position() {
230
        this.style('left', function(d) { return d.x + 'px'; })
231
            .style('top', function(d) { return d.y + 'px'; })
232
            .style('width', function(d) { return Math.max(0, d.dx - 1) + 'px'; })
233
            .style('height', function(d) { return Math.max(0, d.dy - 1) + 'px'; });
234
    }
235
};
236
237
jsondash.handlers.handleRadialDendrogram = function(container, config) {
238
    'use strict';
239
    container.selectAll('svg').remove();
240
    // Code taken (and refactored for use here) from:
241
    // https://bl.ocks.org/mbostock/4339607
242
    var padding = 50;
243
    var radius = (config.width > config.height ? config.width : config.height) - padding;
244
    var cluster = d3.layout.cluster()
245
        .size([360, radius / 2 - 150]); // reduce size relative to `radius`
246
    var diagonal = d3.svg.diagonal.radial()
247
        .projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
248
    var svg = container.append('svg')
249
        .attr('width', radius)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
250
        .attr('height', radius);
251
    var g = svg.append('g');
252
    g.attr('transform', 'translate(' + radius / 2 + ',' + radius / 2 + ')');
253
254
    jsondash.getJSON(config.dataSource, function(error, root) {
255
        if (error) { throw error; }
256
        var nodes = cluster.nodes(root);
257
        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...
258
            .data(cluster.links(nodes))
259
            .enter().append('path')
260
            .attr('class', 'link')
261
            .attr('d', diagonal);
262
        var node = g.selectAll('g.node')
263
            .data(nodes)
264
            .enter().append('g')
265
            .attr('class', 'node')
266
            .attr('transform', function(d) { return 'rotate(' + (d.x - 90) + ')translate(' + d.y + ')'; });
267
        node.append('circle')
268
            .attr('r', 4.5);
269
        node.append('text')
270
            .attr('dy', '.31em')
271
            .attr('text-anchor', function(d) { return d.x < 180 ? 'start' : 'end'; })
272
            .attr('transform', function(d) { return d.x < 180 ? 'translate(8)' : 'rotate(180)translate(-8)'; })
273
            .text(function(d) { return d.name; });
274
        jsondash.unload(container);
275
    });
276
    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...
277
};
278
279
jsondash.handlers.handleDendrogram = function(container, config) {
280
    'use strict';
281
    container.selectAll('svg').remove();
282
    // A general padding for the svg inside of the widget.
283
    // The cluster dendrogram will also need to have padding itself, so
284
    // the bounds are not clipped in the svg.
285
    var svg_pad = 20;
286
    var width = config.width - svg_pad;
287
    var height = config.height - svg_pad;
288
    var PADDING = width / 4;
289
    var cluster = d3.layout.cluster()
290
        .size([height * 0.85, width - PADDING]);
291
    var diagonal = d3.svg.diagonal()
292
        .projection(function(d) { return [d.y, d.x]; });
293
    var svg = container
294
        .append('svg')
295
        .attr('width', width)
296
        .attr('height', height);
297
    var g = svg.append('g')
298
        .attr('transform', 'translate(40, 0)');
299
300
    jsondash.getJSON(config.dataSource, function(error, root) {
301
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
302
303
        var nodes = cluster.nodes(root);
304
        var links = cluster.links(nodes);
305
        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...
306
        .data(links)
307
        .enter().append('path')
308
        .attr('class', 'link')
309
        .attr('d', diagonal);
310
311
        var node = g.selectAll('.node')
312
        .data(nodes)
313
        .enter().append('g')
314
        .attr('class', 'node')
315
        .attr('transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; });
316
317
        node.append('circle').attr('r', 4.5);
318
        node.append('text')
319
        .attr('dx', function(d) { return d.children ? -8 : 8; })
320
        .attr('dy', 3)
321
        .style('text-anchor', function(d) { return d.children ? 'end' : 'start'; })
322
        .text(function(d) { return d.name; });
323
324
        jsondash.unload(container);
325
    });
326
};
327
328
jsondash.handlers.handleVoronoi = function(container, config) {
329
    'use strict';
330
    jsondash.getJSON(config.dataSource, function(error, data){
331
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
332
        var width = config.width - jsondash.config.WIDGET_MARGIN_X;
333
        var height = config.height - jsondash.config.WIDGET_MARGIN_Y;
334
        var vertices = data;
335
        var voronoi = d3.geom.voronoi().clipExtent([[0, 0], [width, height]]);
336
        // Cleanup
337
        var svg = container
338
        .append('svg')
339
        .attr('width', width)
340
        .attr('height', height);
341
        var path = svg.append('g').selectAll('path');
342
        svg.selectAll('circle')
343
        .data(vertices.slice(1))
344
        .enter().append('circle')
345
        .attr('transform', function(d) { return 'translate(' + d + ')'; })
346
        .attr('r', 1.5);
347
        redraw();
348
349
        function redraw() {
350
            path = path.data(voronoi(vertices), jsondash.util.polygon);
351
            path.exit().remove();
352
            path.enter().append('path')
353
            .attr('class', function(d, i) { return 'q' + (i % 9) + '-9'; })
354
            .attr('d', jsondash.util.polygon);
355
            path.order();
356
        }
357
        jsondash.unload(container);
358
    });
359
};
360
361
jsondash.handlers.handleSparkline = function(container, config) {
362
    'use strict';
363
    // Clean up old canvas elements
364
    container.selectAll('.sparkline-container').remove();
365
    var sparkline_type = config.type.split('-')[1];
366
    var spark = container
367
        .append('div')
368
        .classed({
369
            'sparkline-container': true,
370
            'text-center': true
371
        });
372
    spark = $(spark[0]);
373
    jsondash.getJSON(config.dataSource, function(data){
374
        var opts = {
375
            type: sparkline_type,
376
            width: config.width - jsondash.config.WIDGET_MARGIN_X,
377
            height: config.height - jsondash.config.WIDGET_MARGIN_Y
378
        };
379
        spark.sparkline(data, opts);
380
        jsondash.unload(container);
381
    });
382
};
383
384
jsondash.handlers.handleDataTable = function(container, config) {
385
    'use strict';
386
    // Clean up old tables if they exist, during reloading.
387
    container.selectAll('.dataTables_wrapper').remove();
388
    jsondash.getJSON(config.dataSource, function(error, res) {
389
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
390
        var keys = d3.keys(res[0]).map(function(d){
391
            return {data: d, title: d};
392
        });
393
        container
394
            .append('table')
395
            .classed({
396
                table: true,
397
                'table-striped': true,
398
                'table-bordered': true,
399
                'table-condensed': true
400
            });
401
        var opts = config.override ? res : {data: res, columns: keys};
402
        $(container.select('table')[0]).dataTable(opts).css({width: 'auto'});
403
        jsondash.unload(container);
404
    });
405
};
406
407
jsondash.handlers.handleSingleNum = function(container, config) {
408
    'use strict';
409
    container.selectAll('.singlenum').remove();
410
    jsondash.getJSON(config.dataSource, function(data){
411
        var num = container.append('div')
412
            .classed({singlenum: true})
413
            .text(data);
414
        data = String(data);
415
        // Add red or green, depending on if the number appears to be pos/neg.
416
        num.classed({
417
            'text-danger': data.startsWith('-'),
418
            'text-success': !data.startsWith('-')
419
        });
420
        // Get title height to offset box.
421
        var title_h = container
422
            .select('.widget-title')
423
            .node()
424
            .getBoundingClientRect()
425
            .height;
426
        var inner_box_height = config.height - title_h; // factor in rough height of title.
427
        container.style({
428
            'line-height': inner_box_height + 'px',
429
            height: inner_box_height + 'px'
430
        });
431
        var digits = String(data).length;
432
        var size = jsondash.util.getDigitSize()(digits);
433
        num.style('font-size', size + 'px');
434
        jsondash.unload(container);
435
    });
436
};
437
438
jsondash.handlers.handleTimeline = function(container, config) {
439
    'use strict';
440
    jsondash.getJSON(config.dataSource, function(data){
441
        container.append('div').attr('id', 'widget-' + config.guid);
442
        var timeline = new TL.Timeline('widget-' + config.guid, data);
0 ignored issues
show
Unused Code introduced by
The variable timeline seems to be never used. Consider removing it.
Loading history...
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...
443
        jsondash.unload(container);
444
    });
445
};
446
447
jsondash.handlers.handleIframe = function(container, config) {
448
    'use strict';
449
    container.selectAll('iframe').remove();
450
    var iframe = container.append('iframe');
451
    iframe.attr({
452
        border: 0,
453
        src: config.dataSource,
454
        height: '100%',
455
        width: '100%'
456
    });
457
    jsondash.unload(container);
458
};
459
460
jsondash.handlers.handleCustom = function(container, config) {
461
    'use strict';
462
    container.selectAll('.custom-container').remove();
463
    $.get(config.dataSource, function(html){
464
        container.append('div').classed({'custom-container': true}).html(html);
465
        jsondash.unload(container);
466
    });
467
};
468
469
jsondash.handlers.handleVenn = function(container, config) {
470
    'use strict';
471
    container.selectAll('.venn').remove();
472
    jsondash.getJSON(config.dataSource, function(error, data){
473
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
474
        var chart = venn.VennDiagram();
475
        var cont = container
476
            .append('div')
477
            .classed({venn: true});
478
        cont.datum(data).call(chart);
479
        cont.select('svg')
480
            .attr('width', config.width - jsondash.config.WIDGET_MARGIN_X)
481
            .attr('height', config.height - jsondash.config.WIDGET_MARGIN_Y);
482
        jsondash.unload(container);
483
    });
484
};
485
486
jsondash.handlers.handlePlotly = function(container, config) {
487
    'use strict';
488
    var id = 'plotly-' + config.guid;
489
    container.selectAll('.plotly-container').remove();
490
    container.append('div')
491
        .classed({'plotly-container': true})
492
        .attr('id', id);
493
    jsondash.getJSON(config.dataSource, function(error, data){
494
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
495
        if(config.override) {
496
            if(data.layout && data.layout.margin) {
497
                // Remove margins, they mess up the
498
                // layout and are already accounted for.
499
                delete data.layout['margin'];
500
            }
501
            Plotly.plot(id, data.data, data.layout || {}, data.options || {});
502
        } else {
503
            Plotly.plot(id, data);
504
        }
505
        d3.select('#' + id).select('.svg-container').style({'margin': '0 auto'})
506
        jsondash.unload(container);
507
    });
508
};
509