Completed
Push — master ( c61f7a...bf9541 )
by Chris
01:26
created

app.js ➔ getFormConfig   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
/** global: d3 */
2
/**
3
 * Bootstrapping functions, event handling, etc... for application.
4
 */
5
6
var jsondash = function() {
7
    var my = {
8
        chart_wall: null,
9
        widgets: {}
10
    };
11
    var dashboard_data    = null;
12
    var $API_ROUTE_URL    = '[name="dataSource"]';
13
    var $API_PREVIEW      = '#api-output';
14
    var $API_PREVIEW_BTN  = '#api-output-preview';
15
    var $API_PREVIEW_CONT = '.api-preview-container';
16
    var $MODULE_FORM      = '#module-form';
17
    var $VIEW_BUILDER     = '#view-builder';
0 ignored issues
show
Unused Code introduced by
The variable $VIEW_BUILDER seems to be never used. Consider removing it.
Loading history...
18
    var $ADD_MODULE       = '#add-module';
19
    var $MAIN_CONTAINER   = '#container';
20
    var $EDIT_MODAL       = '#chart-options';
21
    var $DELETE_BTN       = '#delete-widget';
22
    var $DELETE_DASHBOARD = '.delete-dashboard';
23
    var $SAVE_MODULE      = '#save-module';
24
    var $EDIT_CONTAINER   = '#edit-view-container';
25
    var $MAIN_FORM        = '#save-view-form';
26
    var $JSON_DATA        = '#raw-config';
27
28
    function addWidget(container, config) {
29
        if(document.querySelector('[data-guid="' + config.guid + '"]')) return d3.select('[data-guid="' + config.guid + '"]');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
30
        return d3.select(container).select('div')
31
            .append('div')
32
            .classed({item: true, widget: true})
33
            .attr('data-guid', config.guid)
34
            .attr('data-refresh', config.refresh)
35
            .attr('data-refresh-interval', config.refreshInterval)
36
            .style('width', config.width + 'px')
37
            .style('height', config.height + 'px')
38
            .html(d3.select('#chart-template').html())
39
            .select('.widget-title .widget-title-text').text(config.name);
40
    }
41
42
    function getFormConfig() {
43
        return jsondash.util.serializeToJSON($($MODULE_FORM).serializeArray());
44
    }
45
46
    function togglePreviewOutput(is_on) {
47
        if(is_on) {
48
            $($API_PREVIEW_CONT).show();
49
            return;
50
        }
51
        $($API_PREVIEW_CONT).hide();
52
    }
53
54
    function previewAPIRoute(e) {
55
        e.preventDefault();
56
        // Shows the response of the API field as a json payload, inline.
57
        $.ajax({
58
            type: 'get',
59
            url: $($API_ROUTE_URL).val().trim(),
60
            success: function(data) {
61
                $($API_PREVIEW).html(prettyCode(data));
62
            },
63
            error: function(data, status, error) {
64
                $($API_PREVIEW).html(error);
65
            }
66
        });
67
    }
68
69
    function refreshableType(type) {
70
        if(type === 'youtube') {return false;}
71
        return true;
72
    }
73
74
    function saveModule(e){
0 ignored issues
show
Unused Code introduced by
The parameter e 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...
75
        var config   = getFormConfig();
76
        var newfield = $('<input class="form-control" type="text">');
77
        var id       = jsondash.util.guid();
78
        // Add a unique guid for referencing later.
79
        config['guid'] = id;
80
        // Add family for lookups
81
        config['family'] = $($MODULE_FORM).find('[name="type"]').find('option:selected').data().family;
82
        if(!config.refresh || !refreshableType(config.type)) {config['refresh'] = false;}
83
        if(!config.override) {config['override'] = false;}
84
        newfield.attr('name', 'module_' + id);
85
        newfield.val(JSON.stringify(config));
86
        $('.modules').append(newfield);
87
        // Save immediately.
88
        $($MAIN_FORM).submit();
89
    }
90
91
    function isModalButton(e) {
92
        return e.relatedTarget.id === $ADD_MODULE.replace('#', '');
93
    }
94
95
    function updateEditForm(e) {
96
        var module_form = $($MODULE_FORM);
97
        // If the modal caller was the add modal button, skip populating the field.
98
        $($API_PREVIEW).text('...');
99
        if(isModalButton(e)) {
100
            module_form.find('input').each(function(_, input){
101
                $(input).val('');
102
            });
103
            $($DELETE_BTN).hide();
104
            return;
105
        }
106
        $($DELETE_BTN).show();
107
        // Updates the fields in the edit form to the active widgets values.
108
        var item = $(e.relatedTarget).closest('.item.widget');
109
        var guid = item.data().guid;
110
        var module = getModule(item);
111
        // Update the modal window fields with this one's value.
112
        $.each(module, function(field, val){
113
            if(field === 'override' || field === 'refresh') {
114
                module_form.find('[name="' + field + '"]').prop('checked', val);
115
            } else {
116
                module_form.find('[name="' + field + '"]').val(val);
117
            }
118
        });
119
        // Update with current guid for referencing the module.
120
        module_form.attr('data-guid', guid);
121
        populateOrderField(module);
122
    }
123
124
    function populateOrderField(module) {
125
        var module_form = $($MODULE_FORM);
126
        var widgets = $('.item.widget');
127
        // Add the number of items to order field.
128
        var order_field = module_form.find('[name="order"]');
129
        var max_options = widgets.length > 0 ? widgets.length + 1 : 2;
130
        order_field.find('option').remove();
131
        // Add empty option.
132
        order_field.append('<option value=""></option>');
133
        d3.map(d3.range(1, max_options), function(i){
134
            var option = $('<option></option>');
135
            option.val(i).text(i);
136
            order_field.append(option);
137
        });
138
        order_field.val(module && module.order ? module.order : '');
139
    }
140
141
    function updateModule(e){
0 ignored issues
show
Unused Code introduced by
The parameter e 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...
142
        var module_form = $($MODULE_FORM);
143
        // Updates the module input fields with new data by rewriting them all.
144
        var guid = module_form.attr('data-guid');
145
        var active = getModuleByGUID(guid);
146
        // Update the modules values to the current input values.
147
        module_form.find('input').each(function(_, input){
148
            var name = $(input).attr('name');
149
            if(name) {
150
                if(name === 'override' || name === 'refresh') {
151
                    // Convert checkbox to json friendly format.
152
                    active[name] = $(input).is(':checked');
153
                } else {
154
                    active[name] = $(input).val();
155
                }
156
            }
157
        });
158
        // Update bar chart type
159
        active['type'] = module_form.find('[name="type"]').val();
160
        // Update order
161
        active['order'] = parseInt(module_form.find('[name="order"]').val(), 10);
162
        // Clear out module input values
163
        $('.modules').empty();
164
        $.each(dashboard_data.modules, function(i, module){
165
            var val = JSON.stringify(module, module);
166
            var input = $('<input type="text" name="module_' + i + '" class="form-control">');
167
            input.val(val);
168
            $('.modules').append(input);
169
        });
170
        updateWidget(active);
171
        $($EDIT_CONTAINER).collapse();
172
        // Refit the grid
173
        fitGrid();
174
    }
175
176
    function updateWidget(config) {
177
        // Trigger update form into view since data is dirty
178
        // Update visual size to existing widget.
179
        var widget = my.widgets[config.guid].el;
180
        loader(widget);
181
        widget.style({
182
            height: config.height + 'px',
183
            width: config.width + 'px'
184
        });
185
        widget.select('.widget-title .widget-title-text').text(config.name);
186
        loadWidgetData(widget, config);
187
    }
188
189
    function refreshWidget(e) {
190
        e.preventDefault();
191
        var config = getModule($(this).closest('.widget'));
192
        var widget = addWidget($MAIN_CONTAINER, config);
193
        loadWidgetData(widget, config);
194
        fitGrid();
195
    }
196
197
    function addChartContainers(container, data) {
198
        for(var name in data.modules){
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
199
            // Closure to maintain each chart data value in loop
200
            (function(config){
201
                var config = data.modules[name];
0 ignored issues
show
introduced by
The variable name is changed by the for-each loop on line 198. Only the value of the last iteration will be visible in this function if it is called outside of the loop.
Loading history...
202
                // Add div wrappers for js grid layout library,
203
                // and add title, icons, and buttons
204
                var widget = addWidget(container, config);
205
                my.widgets[config.guid] = {el: widget, config: config};
206
            })(data.modules[name]);
207
        }
208
        fitGrid();
209
        for(var guid in my.widgets){
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
210
            var widg = my.widgets[guid];
211
            loadWidgetData(widg.el, widg.config);
212
        }
213
    }
214
215
    function getModuleByGUID(guid) {
216
        return my.widgets[guid].config;
217
    }
218
219
    function deleteModule(e) {
220
        e.preventDefault();
221
        if(!confirm('Are you sure?')) {return;}
222
        var guid = $($MODULE_FORM).attr('data-guid');
223
        // Remove form input and visual widget
224
        $('.modules').find('#' + guid).remove();
225
        $('.item.widget[data-guid="' + guid + '"]').remove();
226
        $($EDIT_MODAL).modal('hide');
227
        // Redraw wall to replace visual 'hole'
228
        fitGrid();
229
        // Trigger update form into view since data is dirty
230
        $($EDIT_CONTAINER).collapse('show');
231
    }
232
233
    function isPreviewableType(type) {
234
        if(type === 'iframe') {return false;}
235
        if(type === 'youtube') {return false;}
236
        if(type === 'custom') {return false;}
237
        return true;
238
    }
239
240
    function chartsTypeChanged(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e 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...
241
        var active_conf = getFormConfig();
242
        var previewable = isPreviewableType(active_conf.type);
243
        togglePreviewOutput(previewable);
244
    }
245
246
    function addDomEvents() {
247
        // Chart type change
248
        $($MODULE_FORM).find('[name="type"]').on('change.charts.type', chartsTypeChanged);
249
        // TODO: debounce/throttle
250
        $($API_ROUTE_URL).on('change.charts', previewAPIRoute);
251
        $($API_PREVIEW_BTN).on('click.charts', previewAPIRoute);
252
        // Save module popup form
253
        $($SAVE_MODULE).on('click.charts.module', saveModule);
254
        // Edit existing modules
255
        $($EDIT_MODAL).on('show.bs.modal', updateEditForm);
256
        $('#update-module').on('click.charts.module', updateModule);
257
        // Allow swapping of edit/update events
258
        // for the add module button and form modal
259
        $($ADD_MODULE).on('click.charts', function(){
260
            $('#update-module')
261
            .attr('id', $SAVE_MODULE.replace('#', ''))
262
            .text('Save module')
263
            .off('click.charts.module')
264
            .on('click.charts', saveModule);
265
        });
266
        // Allow swapping of edit/update events
267
        // for the edit button and form modal
268
        $('.widget-edit').on('click.charts', function(){
269
            $($SAVE_MODULE)
270
            .attr('id', 'update-module')
271
            .text('Update module')
272
            .off('click.charts.module')
273
            .on('click.charts', updateModule);
274
        });
275
        // Add delete button for existing widgets.
276
        $($DELETE_BTN).on('click.charts', deleteModule);
277
        // Add delete confirm for dashboards.
278
        $($DELETE_DASHBOARD).on('submit.charts', function(e){
279
            if(!confirm('Are you sure?')) e.preventDefault();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
280
        });
281
    }
282
283
    function initGrid(container) {
0 ignored issues
show
Unused Code introduced by
The parameter container 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...
284
        fitGrid({
285
            columnWidth: 5,
286
            itemSelector: '.item',
287
            transitionDuration: 0,
288
            fitWidth: true
289
        }, true);
290
        $('.item.widget').removeClass('hidden');
291
    }
292
293
    function fitGrid(opts, init) {
294
        var valid_options = $.isPlainObject(opts);
0 ignored issues
show
Unused Code introduced by
The variable valid_options seems to be never used. Consider removing it.
Loading history...
295
        var options = $.extend({}, opts, {});
296
        if(init) {
297
            my.chart_wall = $('#container').packery(options);
298
            items = my.chart_wall.find('.item').draggable({
0 ignored issues
show
Bug introduced by
The variable items seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.items.
Loading history...
299
                scroll: true,
300
                handle: '.dragger',
301
                stop: function(){
302
                    $($EDIT_CONTAINER).collapse('show');
303
                    updateModuleOrder();
304
                    my.chart_wall.packery(options);
305
                }
306
            });
307
            my.chart_wall.packery('bindUIDraggableEvents', items);
308
        } else {
309
            my.chart_wall.packery(options);
310
        }
311
    }
312
313
    function updateModuleOrder() {
314
        var items = my.chart_wall.packery('getItemElements');
315
        // Update module order
316
        $.each(items, function(i, el){
0 ignored issues
show
Unused Code introduced by
The parameter el 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...
317
            var module = getModule($(this));
318
            var config = $.extend(module, {order: i});
319
            updateModuleInput(config);
320
        });
321
    }
322
323
    function getModule(el) {
324
        // Return module by element
325
        var data = el.data();
326
        var guid = data.guid;
327
        return getModuleByGUID(guid);
328
    }
329
330
    function loader(container) {
331
        container.select('.loader-overlay').classed({hidden: false});
332
        container.select('.widget-loader').classed({hidden: false});
333
    }
334
335
    function unload(container) {
336
        container.select('.loader-overlay').classed({hidden: true});
337
        container.select('.widget-loader').classed({hidden: true});
338
    }
339
340
    function handleInputs(widget, config) {
341
        var inputs_selector = '[data-guid="' + config.guid + '"] .chart-inputs';
342
        // Load event handlers for these newly created forms.
343
        $(inputs_selector).find('form').on('submit', function(e){
344
            e.stopImmediatePropagation();
345
            e.preventDefault();
346
            // Just create a new url for this, but use existing config.
347
            // The global object config will not be altered.
348
            // The first {} here is important, as it enforces a deep copy,
349
            // not a mutation of the original object.
350
            var url = config.dataSource;
351
            // Ensure we don't lose params already save on this endpoint url.
352
            var existing_params = url.split('?')[1];
353
            var params = getValidParamString($(this).serializeArray());
354
            var _config = $.extend({}, config, {
355
                dataSource: url.replace(/\?.+/, '') + '?' + existing_params + '&' + params
356
            });
357
            // Otherwise reload like normal.
358
            loadWidgetData(widget, _config);
359
            // Hide the form again
360
            $(inputs_selector).removeClass('in');
361
        });
362
    }
363
364
    function getValidParamString(arr) {
365
        // Jquery $.serialize and $.serializeArray will
366
        // return empty query parameters, which is undesirable and can
367
        // be error prone for RESTFUL endpoints.
368
        // e.g. `foo=bar&bar=` becomes `foo=bar`
369
        var param_str = '';
370
        arr = arr.filter(function(param, i){return param.value !== '';});
0 ignored issues
show
Unused Code introduced by
The parameter i 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...
371
        $.each(arr, function(i, param){
372
            param_str += (param.name + '=' + param.value);
373
            if(i < arr.length - 1 && arr.length > 1) param_str += '&';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
374
        });
375
        return param_str;
376
    }
377
378
    function loadWidgetData(widget, config) {
379
        loader(widget);
380
381
        try {
382
            // Handle any custom inputs the user specified for this module.
383
            // They map to standard form inputs and correspond to query
384
            // arguments for this dataSource.
385
            if(config.inputs) {handleInputs(widget, config);}
386
387
            if(config.type === 'datatable') {
388
                jsondash.handlers.handleDataTable(widget, config);
389
            }
390
            else if(jsondash.util.isSparkline(config.type)) {
391
                jsondash.handlers.handleSparkline(widget, config);
392
            }
393
            else if(config.type === 'iframe') {
394
                jsondash.handlers.handleIframe(widget, config);
395
            }
396
            else if(config.type === 'timeline') {
397
                jsondash.handlers.handleTimeline(widget, config);
398
            }
399
            else if(config.type === 'venn') {
400
                jsondash.handlers.handleVenn(widget, config);
401
            }
402
            else if(config.type === 'number') {
403
                jsondash.handlers.handleSingleNum(widget, config);
404
            }
405
            else if(config.type === 'youtube') {
406
                jsondash.handlers.handleYoutube(widget, config);
407
            }
408
            else if(config.type === 'graph'){
409
                jsondash.handlers.handleGraph(widget, config);
410
            }
411
            else if(config.type === 'custom') {
412
                jsondash.handlers.handleCustom(widget, config);
413
            }
414
            else if(config.type === 'wordcloud') {
415
                jsondash.handlers.handleWordCloud(widget, config);
416
            }
417
            else if(config.type === 'plotly-any') {
418
                jsondash.handlers.handlePlotly(widget, config);
419
            }
420
            else if(jsondash.util.isD3Subtype(config)) {
421
                jsondash.handlers.handleD3(widget, config);
422
            } else {
423
                jsondash.handlers.handleC3(widget, config);
424
            }
425
        } catch(e) {
426
            if(console && console.error) console.error(e);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
427
            unload(widget);
428
        }
429
        addResizeEvent(widget, config);
430
    }
431
432
    function addResizeEvent(widget, config) {
433
        // Add resize event
434
        $(widget[0]).resizable({
435
            helper: 'resizable-helper',
436
            minWidth: 200,
437
            minHeight: 200,
438
            stop: function(event, ui) {
439
                // Update the configs dimensions.
440
                config = $.extend(config, {width: ui.size.width, height: ui.size.height});
441
                updateModuleInput(config);
442
                loadWidgetData(widget, config);
443
                fitGrid();
444
                // Open save panel
445
                $($EDIT_CONTAINER).collapse('show');
446
            }
447
        });
448
    }
449
450
    function updateModuleInput(config) {
451
        $('input[id="' + config.guid + '"]').val(JSON.stringify(config));
452
    }
453
454
    function prettyCode(code) {
455
        if(typeof code === "object") return JSON.stringify(code, null, 4);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
456
        return JSON.stringify(JSON.parse(code), null, 4);
457
    }
458
459
    function addRefreshers(modules) {
460
        $.each(modules, function(_, module){
461
            if(module.refresh && module.refreshInterval) {
462
                var container = d3.select('[data-guid="' + module.guid + '"]');
463
                setInterval(function(){
464
                    loadWidgetData(container, module);
465
                }, parseInt(module.refreshInterval, 10));
466
            }
467
        });
468
    }
469
470
    function prettifyJSONPreview() {
471
        // Reformat the code inside of the raw json field,
472
        // to pretty print for the user.
473
        $($JSON_DATA).text(prettyCode($($JSON_DATA).text()));
474
    }
475
476
    function loadDashboard(data) {
477
        // Load the grid before rendering the ajax, since the DOM
478
        // is rendered server side.
479
        initGrid($MAIN_CONTAINER);
480
        // Add actual ajax data.
481
        addChartContainers($MAIN_CONTAINER, data);
482
        dashboard_data = data;
483
484
        // Add event handlers for widget UI
485
        $('.widget-refresh').on('click.charts', refreshWidget);
486
487
        // Setup refresh intervals for all widgets that specify it.
488
        addRefreshers(data.modules);
489
490
        // Format json config display
491
        $('#json-output').on('show.bs.modal', function(e){
0 ignored issues
show
Unused Code introduced by
The parameter e 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...
492
            var code = $(this).find('code').text();
493
            $(this).find('code').text(prettyCode(code));
494
        });
495
496
        // Add event for downloading json config raw.
497
        // Will provide decent support but still not major: http://caniuse.com/#search=download
498
        $('[href="#download-json"]').on('click', function(e){
0 ignored issues
show
Unused Code introduced by
The parameter e 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...
499
            var datestr = new Date().toString().replace(/ /gi, '-');
500
            var json_data = $($JSON_DATA).val();
501
            var data = encodeURIComponent(JSON.stringify(json_data, null, 4));
502
            data = "data:text/json;charset=utf-8," + data;
503
            $(this).attr('href', data);
504
            $(this).attr('download', 'charts-config-raw-' + datestr + '.json');
505
        });
506
507
        prettifyJSONPreview();
508
509
        // Setup responsive handlers
510
        var jres = jRespond([
511
        {
512
            label: 'handheld',
513
            enter: 0,
514
            exit: 767
515
        }
516
        ]);
517
        jres.addFunc({
518
            breakpoint: 'handheld',
519
            enter: function() {
520
                $('.widget').css({
521
                    'max-width': '100%',
522
                    'width': '100%',
523
                    'position': 'static'
524
                });
525
            }
526
        });
527
        populateOrderField();
528
        fitGrid();
529
    }
530
    my.config = {
531
        WIDGET_MARGIN_X: 20,
532
        WIDGET_MARGIN_Y: 60
533
    };
534
    my.loadDashboard = loadDashboard;
535
    my.handlers = {};
536
    my.util = {};
537
    my.loader = loader;
538
    my.unload = unload;
539
    my.addDomEvents = addDomEvents;
540
    return my;
541
}();
542