Completed
Push — master ( e2de76...dced21 )
by Chris
01:24
created

jsondash.handlers.handleDendrogram   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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