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