Completed
Push — master ( cd6046...9bd4ef )
by Chris
01:15
created

jsondash.handlers.handleRadialDendrogram   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) {
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) {
250
    'use strict';
251
    container.selectAll('svg').remove();
252
    var PADDING = 100;
253
    var width = config.width - jsondash.config.WIDGET_MARGIN_X;
254
    var height = config.height - jsondash.config.WIDGET_MARGIN_Y;
255
    var cluster = d3.layout.cluster()
256
        .size([height, width - PADDING]);
257
    var diagonal = d3.svg.diagonal()
258
        .projection(function(d) { return [d.y, d.x]; });
259
    var svg = container
260
        .append('svg')
261
        .attr('width', width)
262
        .attr('height', height)
263
        .append('g')
264
        .attr('transform', 'translate(40,0)');
265
266
    jsondash.getJSON(config.dataSource, function(error, root) {
267
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
268
269
        var nodes = cluster.nodes(root),
270
        links = cluster.links(nodes);
271
272
        var link = svg.selectAll('.link')
0 ignored issues
show
Unused Code introduced by
The variable link seems to be never used. Consider removing it.
Loading history...
273
        .data(links)
274
        .enter().append('path')
275
        .attr('class', 'link')
276
        .attr('d', diagonal);
277
278
        var node = svg.selectAll('.node')
279
        .data(nodes)
280
        .enter().append('g')
281
        .attr('class', 'node')
282
        .attr('transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; });
283
284
        node.append('circle').attr('r', 4.5);
285
        node.append('text')
286
        .attr('dx', function(d) { return d.children ? -8 : 8; })
287
        .attr('dy', 3)
288
        .style('text-anchor', function(d) { return d.children ? 'end' : 'start'; })
289
        .text(function(d) { return d.name; });
290
291
        jsondash.unload(container);
292
    });
293
};
294
295
jsondash.handlers.handleVoronoi = function(container, config) {
296
    'use strict';
297
    jsondash.getJSON(config.dataSource, function(error, data){
298
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
299
        var width = config.width - jsondash.config.WIDGET_MARGIN_X;
300
        var height = config.height - jsondash.config.WIDGET_MARGIN_Y;
301
        var vertices = data;
302
        var voronoi = d3.geom.voronoi().clipExtent([[0, 0], [width, height]]);
303
        // Cleanup
304
        var svg = container
305
        .append('svg')
306
        .attr('width', width)
307
        .attr('height', height);
308
        var path = svg.append('g').selectAll('path');
309
        svg.selectAll('circle')
310
        .data(vertices.slice(1))
311
        .enter().append('circle')
312
        .attr('transform', function(d) { return 'translate(' + d + ')'; })
313
        .attr('r', 1.5);
314
        redraw();
315
316
        function redraw() {
317
            path = path.data(voronoi(vertices), jsondash.util.polygon);
318
            path.exit().remove();
319
            path.enter().append('path')
320
            .attr('class', function(d, i) { return 'q' + (i % 9) + '-9'; })
321
            .attr('d', jsondash.util.polygon);
322
            path.order();
323
        }
324
        jsondash.unload(container);
325
    });
326
};
327
328
jsondash.handlers.handleSparkline = function(container, config) {
329
    'use strict';
330
    // Clean up old canvas elements
331
    container.selectAll('.sparkline-container').remove();
332
    var sparkline_type = config.type.split('-')[1];
333
    var spark = container
334
        .append('div')
335
        .classed({
336
            'sparkline-container': true,
337
            'text-center': true
338
        });
339
    spark = $(spark[0]);
340
    jsondash.getJSON(config.dataSource, function(data){
341
        var opts = {
342
            type: sparkline_type,
343
            width: config.width - jsondash.config.WIDGET_MARGIN_X,
344
            height: config.height - jsondash.config.WIDGET_MARGIN_Y
345
        };
346
        spark.sparkline(data, opts);
347
        jsondash.unload(container);
348
    });
349
};
350
351
jsondash.handlers.handleDataTable = function(container, config) {
352
    'use strict';
353
    // Clean up old tables if they exist, during reloading.
354
    container.selectAll('.dataTables_wrapper').remove();
355
    jsondash.getJSON(config.dataSource, function(error, res) {
356
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
357
        var keys = d3.keys(res[0]).map(function(d){
358
            return {data: d, title: d};
359
        });
360
        container
361
            .append('table')
362
            .classed({
363
                table: true,
364
                'table-striped': true,
365
                'table-bordered': true,
366
                'table-condensed': true
367
            });
368
        var opts = config.override ? res : {data: res, columns: keys};
369
        $(container.select('table')[0]).dataTable(opts).css({width: 'auto'});
370
        jsondash.unload(container);
371
    });
372
};
373
374
jsondash.handlers.handleSingleNum = function(container, config) {
375
    'use strict';
376
    container.selectAll('.singlenum').remove();
377
    jsondash.getJSON(config.dataSource, function(data){
378
        var num = container.append('div')
379
            .classed({singlenum: true})
380
            .text(data);
381
        data = String(data);
382
        // Add red or green, depending on if the number appears to be pos/neg.
383
        num.classed({
384
            'text-danger': data.startsWith('-'),
385
            'text-success': !data.startsWith('-')
386
        });
387
        // Get title height to offset box.
388
        var title_h = container
389
            .select('.widget-title')
390
            .node()
391
            .getBoundingClientRect()
392
            .height;
393
        var inner_box_height = config.height - title_h; // factor in rough height of title.
394
        container.style({
395
            'line-height': inner_box_height + 'px',
396
            height: inner_box_height + 'px'
397
        });
398
        var digits = String(data).length;
399
        var size = jsondash.util.getDigitSize()(digits);
400
        num.style('font-size', size + 'px');
401
        jsondash.unload(container);
402
    });
403
};
404
405
jsondash.handlers.handleTimeline = function(container, config) {
406
    'use strict';
407
    jsondash.getJSON(config.dataSource, function(data){
408
        container.append('div').attr('id', 'widget-' + config.guid);
409
        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...
410
        jsondash.unload(container);
411
    });
412
};
413
414
jsondash.handlers.handleIframe = function(container, config) {
415
    'use strict';
416
    container.selectAll('iframe').remove();
417
    var iframe = container.append('iframe');
418
    iframe.attr({
419
        border: 0,
420
        src: config.dataSource,
421
        height: '100%',
422
        width: '100%'
423
    });
424
    jsondash.unload(container);
425
};
426
427
jsondash.handlers.handleCustom = function(container, config) {
428
    'use strict';
429
    container.selectAll('.custom-container').remove();
430
    $.get(config.dataSource, function(html){
431
        container.append('div').classed({'custom-container': true}).html(html);
432
        jsondash.unload(container);
433
    });
434
};
435
436
jsondash.handlers.handleVenn = function(container, config) {
437
    'use strict';
438
    container.selectAll('.venn').remove();
439
    jsondash.getJSON(config.dataSource, function(error, data){
440
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
441
        var chart = venn.VennDiagram();
442
        var cont = container
443
            .append('div')
444
            .classed({venn: true});
445
        cont.datum(data).call(chart);
446
        cont.select('svg')
447
            .attr('width', config.width - jsondash.config.WIDGET_MARGIN_X)
448
            .attr('height', config.height - jsondash.config.WIDGET_MARGIN_Y);
449
        jsondash.unload(container);
450
    });
451
};
452
453
jsondash.handlers.handlePlotly = function(container, config) {
454
    'use strict';
455
    var id = 'plotly-' + config.guid;
456
    container.selectAll('.plotly-container').remove();
457
    container.append('div')
458
        .classed({'plotly-container': true})
459
        .attr('id', id);
460
    jsondash.getJSON(config.dataSource, function(error, data){
461
        if(error) { throw new Error('Could not load url: ' + config.dataSource); }
462
        if(config.override) {
463
            if(data.layout && data.layout.margin) {
464
                // Remove margins, they mess up the
465
                // layout and are already accounted for.
466
                delete data.layout['margin'];
467
            }
468
            Plotly.plot(id, data.data, data.layout || {}, data.options || {});
469
        } else {
470
            Plotly.plot(id, data);
471
        }
472
        d3.select('#' + id).select('.svg-container').style({'margin': '0 auto'})
473
        jsondash.unload(container);
474
    });
475
};
476