Completed
Push — master ( f3ed3a...684a71 )
by Chris
01:16
created

app.js ➔ ... ➔ DELETE_DASHBOARD.submit.charts   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
dl 0
loc 3
rs 10
nc 2
nop 1
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.selector.replace('#', '');
93
    }
94
95
    function updateEditForm(e) {
96
        // If the modal caller was the add modal button, skip populating the field.
97
        API_PREVIEW.text('...');
98
        if(isModalButton(e)) {
99
            MODULE_FORM.find('input').each(function(_, input){
100
                $(input).val('');
101
            });
102
            DELETE_BTN.hide();
103
            return;
104
        }
105
        DELETE_BTN.show();
106
        // Updates the fields in the edit form to the active widgets values.
107
        var item = $(e.relatedTarget).closest('.item.widget');
108
        var guid = item.data().guid;
109
        var module = getModule(item);
110
        // Update the modal window fields with this one's value.
111
        $.each(module, function(field, val){
112
            if(field === 'override' || field === 'refresh') {
113
                MODULE_FORM.find('[name="' + field + '"]').prop('checked', val);
114
            } else {
115
                MODULE_FORM.find('[name="' + field + '"]').val(val);
116
            }
117
        });
118
        // Update with current guid for referencing the module.
119
        MODULE_FORM.attr('data-guid', guid);
120
        populateOrderField(module);
121
122
        // Trigger event for select dropdown to ensure any UI is consistent.
123
        // This is done AFTER the fields have been pre-populated.
124
        MODULE_FORM.find('[name="type"]').change();
125
    }
126
127
    function populateOrderField(module) {
128
        var widgets = $('.item.widget');
129
        // Add the number of items to order field.
130
        var order_field = MODULE_FORM.find('[name="order"]');
131
        var max_options = widgets.length > 0 ? widgets.length + 1 : 2;
132
        order_field.find('option').remove();
133
        // Add empty option.
134
        order_field.append('<option value=""></option>');
135
        d3.map(d3.range(1, max_options), function(i){
136
            var option = $('<option></option>');
137
            option.val(i).text(i);
138
            order_field.append(option);
139
        });
140
        order_field.val(module && module.order ? module.order : '');
141
    }
142
143
    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...
144
        // Updates the module input fields with new data by rewriting them all.
145
        var guid = MODULE_FORM.attr('data-guid');
146
        var active = getModuleByGUID(guid);
147
        // Update the modules values to the current input values.
148
        MODULE_FORM.find('input').each(function(_, input){
149
            var name = $(input).attr('name');
150
            if(name) {
151
                if(name === 'override' || name === 'refresh') {
152
                    // Convert checkbox to json friendly format.
153
                    active[name] = $(input).is(':checked');
154
                } else {
155
                    active[name] = $(input).val();
156
                }
157
            }
158
        });
159
        // Update bar chart type
160
        active['type'] = MODULE_FORM.find('[name="type"]').val();
161
        // Update order
162
        active['order'] = parseInt(MODULE_FORM.find('[name="order"]').val(), 10);
163
        // Clear out module input values
164
        $('.modules').empty();
165
        $.each(dashboard_data.modules, function(i, module){
166
            var val = JSON.stringify(module, module);
167
            var input = $('<input type="text" name="module_' + i + '" class="form-control">');
168
            input.val(val);
169
            $('.modules').append(input);
170
        });
171
        updateWidget(active);
172
        EDIT_CONTAINER.collapse();
173
        // Refit the grid
174
        fitGrid();
175
    }
176
177
    function updateWidget(config) {
178
        // Trigger update form into view since data is dirty
179
        // Update visual size to existing widget.
180
        var widget = my.widgets[config.guid].el;
181
        loader(widget);
182
        widget.style({
183
            height: config.height + 'px',
184
            width: config.width + 'px'
185
        });
186
        widget.select('.widget-title .widget-title-text').text(config.name);
187
        loadWidgetData(widget, config);
188
    }
189
190
    function refreshWidget(e) {
191
        e.preventDefault();
192
        var config = getModule($(this).closest('.widget'));
193
        var widget = addWidget(MAIN_CONTAINER, config);
194
        loadWidgetData(widget, config);
195
        fitGrid();
196
    }
197
198
    function addChartContainers(container, data) {
199
        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...
200
            // Closure to maintain each chart data value in loop
201
            (function(config){
202
                var config = data.modules[name];
0 ignored issues
show
introduced by
The variable name is changed by the for-each loop on line 199. Only the value of the last iteration will be visible in this function if it is called outside of the loop.
Loading history...
203
                // Add div wrappers for js grid layout library,
204
                // and add title, icons, and buttons
205
                var widget = addWidget(container, config);
206
                my.widgets[config.guid] = {el: widget, config: config};
207
            })(data.modules[name]);
208
        }
209
        fitGrid();
210
        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...
211
            var widg = my.widgets[guid];
212
            loadWidgetData(widg.el, widg.config);
213
        }
214
    }
215
216
    function getModuleByGUID(guid) {
217
        return my.widgets[guid].config;
218
    }
219
220
    function deleteModule(e) {
221
        e.preventDefault();
222
        if(!confirm('Are you sure?')) {return;}
223
        var guid = MODULE_FORM.attr('data-guid');
224
        // Remove form input and visual widget
225
        $('.modules').find('#' + guid).remove();
226
        $('.item.widget[data-guid="' + guid + '"]').remove();
227
        EDIT_MODAL.modal('hide');
228
        // Redraw wall to replace visual 'hole'
229
        fitGrid();
230
        // Trigger update form into view since data is dirty
231
        EDIT_CONTAINER.collapse('show');
232
    }
233
234
    function isPreviewableType(type) {
235
        if(type === 'iframe') {return false;}
236
        if(type === 'youtube') {return false;}
237
        if(type === 'custom') {return false;}
238
        return true;
239
    }
240
241
    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...
242
        var active_conf = getFormConfig();
243
        var previewable = isPreviewableType(active_conf.type);
244
        togglePreviewOutput(previewable);
245
    }
246
247
    function addDomEvents() {
248
        // Chart type change
249
        MODULE_FORM.find('[name="type"]').on('change.charts.type', chartsTypeChanged);
250
        // TODO: debounce/throttle
251
        API_ROUTE_URL.on('change.charts', previewAPIRoute);
252
        API_PREVIEW_BTN.on('click.charts', previewAPIRoute);
253
        // Save module popup form
254
        SAVE_MODULE.on('click.charts.module', saveModule);
255
        // Edit existing modules
256
        EDIT_MODAL.on('show.bs.modal', updateEditForm);
257
        $('#update-module').on('click.charts.module', updateModule);
258
        // Allow swapping of edit/update events
259
        // for the add module button and form modal
260
        ADD_MODULE.on('click.charts', function(){
261
            $('#update-module')
262
            .attr('id', SAVE_MODULE.selector.replace('#', ''))
263
            .text('Save module')
264
            .off('click.charts.module')
265
            .on('click.charts', saveModule);
266
        });
267
        // Allow swapping of edit/update events
268
        // for the edit button and form modal
269
        $('.widget-edit').on('click.charts', function(){
270
            SAVE_MODULE
271
            .attr('id', 'update-module')
272
            .text('Update module')
273
            .off('click.charts.module')
274
            .on('click.charts', updateModule);
275
        });
276
        // Add delete button for existing widgets.
277
        DELETE_BTN.on('click.charts', deleteModule);
278
        // Add delete confirm for dashboards.
279
        DELETE_DASHBOARD.on('submit.charts', function(e){
280
            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...
281
        });
282
    }
283
284
    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...
285
        fitGrid({
286
            columnWidth: 5,
287
            itemSelector: '.item',
288
            transitionDuration: 0,
289
            fitWidth: true
290
        }, true);
291
        $('.item.widget').removeClass('hidden');
292
    }
293
294
    function fitGrid(opts, init) {
295
        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...
296
        var options = $.extend({}, opts, {});
297
        if(init) {
298
            my.chart_wall = $('#container').packery(options);
299
            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...
300
                scroll: true,
301
                handle: '.dragger',
302
                stop: function(){
303
                    EDIT_CONTAINER.collapse('show');
304
                    updateModuleOrder();
305
                    my.chart_wall.packery(options);
306
                }
307
            });
308
            my.chart_wall.packery('bindUIDraggableEvents', items);
309
        } else {
310
            my.chart_wall.packery(options);
311
        }
312
    }
313
314
    function updateModuleOrder() {
315
        var items = my.chart_wall.packery('getItemElements');
316
        // Update module order
317
        $.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...
318
            var module = getModule($(this));
319
            var config = $.extend(module, {order: i});
320
            updateModuleInput(config);
321
        });
322
    }
323
324
    function getModule(el) {
325
        // Return module by element
326
        var data = el.data();
327
        var guid = data.guid;
328
        return getModuleByGUID(guid);
329
    }
330
331
    function loader(container) {
332
        container.select('.loader-overlay').classed({hidden: false});
333
        container.select('.widget-loader').classed({hidden: false});
334
    }
335
336
    function unload(container) {
337
        container.select('.loader-overlay').classed({hidden: true});
338
        container.select('.widget-loader').classed({hidden: true});
339
    }
340
341
    function handleInputs(widget, config) {
342
        var inputs_selector = '[data-guid="' + config.guid + '"] .chart-inputs';
343
        // Load event handlers for these newly created forms.
344
        $(inputs_selector).find('form').on('submit', function(e){
345
            e.stopImmediatePropagation();
346
            e.preventDefault();
347
            // Just create a new url for this, but use existing config.
348
            // The global object config will not be altered.
349
            // The first {} here is important, as it enforces a deep copy,
350
            // not a mutation of the original object.
351
            var url = config.dataSource;
352
            // Ensure we don't lose params already save on this endpoint url.
353
            var existing_params = url.split('?')[1];
354
            var params = getValidParamString($(this).serializeArray());
355
            var _config = $.extend({}, config, {
356
                dataSource: url.replace(/\?.+/, '') + '?' + existing_params + '&' + params
357
            });
358
            // Otherwise reload like normal.
359
            loadWidgetData(widget, _config);
360
            // Hide the form again
361
            $(inputs_selector).removeClass('in');
362
        });
363
    }
364
365
    function getValidParamString(arr) {
366
        // Jquery $.serialize and $.serializeArray will
367
        // return empty query parameters, which is undesirable and can
368
        // be error prone for RESTFUL endpoints.
369
        // e.g. `foo=bar&bar=` becomes `foo=bar`
370
        var param_str = '';
371
        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...
372
        $.each(arr, function(i, param){
373
            param_str += (param.name + '=' + param.value);
374
            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...
375
        });
376
        return param_str;
377
    }
378
379
    function loadWidgetData(widget, config) {
380
        loader(widget);
381
382
        try {
383
            // Handle any custom inputs the user specified for this module.
384
            // They map to standard form inputs and correspond to query
385
            // arguments for this dataSource.
386
            if(config.inputs) {handleInputs(widget, config);}
387
388
            if(config.type === 'datatable') {
389
                jsondash.handlers.handleDataTable(widget, config);
390
            }
391
            else if(jsondash.util.isSparkline(config.type)) {
392
                jsondash.handlers.handleSparkline(widget, config);
393
            }
394
            else if(config.type === 'iframe') {
395
                jsondash.handlers.handleIframe(widget, config);
396
            }
397
            else if(config.type === 'timeline') {
398
                jsondash.handlers.handleTimeline(widget, config);
399
            }
400
            else if(config.type === 'venn') {
401
                jsondash.handlers.handleVenn(widget, config);
402
            }
403
            else if(config.type === 'number') {
404
                jsondash.handlers.handleSingleNum(widget, config);
405
            }
406
            else if(config.type === 'youtube') {
407
                jsondash.handlers.handleYoutube(widget, config);
408
            }
409
            else if(config.type === 'graph'){
410
                jsondash.handlers.handleGraph(widget, config);
411
            }
412
            else if(config.type === 'custom') {
413
                jsondash.handlers.handleCustom(widget, config);
414
            }
415
            else if(config.type === 'wordcloud') {
416
                jsondash.handlers.handleWordCloud(widget, config);
417
            }
418
            else if(config.type === 'plotly-any') {
419
                jsondash.handlers.handlePlotly(widget, config);
420
            }
421
            else if(jsondash.util.isD3Subtype(config)) {
422
                jsondash.handlers.handleD3(widget, config);
423
            } else {
424
                jsondash.handlers.handleC3(widget, config);
425
            }
426
        } catch(e) {
427
            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...
428
            unload(widget);
429
        }
430
        addResizeEvent(widget, config);
431
    }
432
433
    function addResizeEvent(widget, config) {
434
        // Add resize event
435
        $(widget[0]).resizable({
436
            helper: 'resizable-helper',
437
            minWidth: 200,
438
            minHeight: 200,
439
            stop: function(event, ui) {
440
                // Update the configs dimensions.
441
                config = $.extend(config, {width: ui.size.width, height: ui.size.height});
442
                updateModuleInput(config);
443
                loadWidgetData(widget, config);
444
                fitGrid();
445
                // Open save panel
446
                EDIT_CONTAINER.collapse('show');
447
            }
448
        });
449
    }
450
451
    function updateModuleInput(config) {
452
        $('input[id="' + config.guid + '"]').val(JSON.stringify(config));
453
    }
454
455
    function prettyCode(code) {
456
        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...
457
        return JSON.stringify(JSON.parse(code), null, 4);
458
    }
459
460
    function addRefreshers(modules) {
461
        $.each(modules, function(_, module){
462
            if(module.refresh && module.refreshInterval) {
463
                var container = d3.select('[data-guid="' + module.guid + '"]');
464
                setInterval(function(){
465
                    loadWidgetData(container, module);
466
                }, parseInt(module.refreshInterval, 10));
467
            }
468
        });
469
    }
470
471
    function prettifyJSONPreview() {
472
        // Reformat the code inside of the raw json field,
473
        // to pretty print for the user.
474
        JSON_DATA.text(prettyCode(JSON_DATA.text()));
475
    }
476
477
    function loadDashboard(data) {
478
        // Load the grid before rendering the ajax, since the DOM
479
        // is rendered server side.
480
        initGrid(MAIN_CONTAINER);
481
        // Add actual ajax data.
482
        addChartContainers(MAIN_CONTAINER, data);
483
        dashboard_data = data;
484
485
        // Add event handlers for widget UI
486
        $('.widget-refresh').on('click.charts', refreshWidget);
487
488
        // Setup refresh intervals for all widgets that specify it.
489
        addRefreshers(data.modules);
490
491
        // Format json config display
492
        $('#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...
493
            var code = $(this).find('code').text();
494
            $(this).find('code').text(prettyCode(code));
495
        });
496
497
        // Add event for downloading json config raw.
498
        // Will provide decent support but still not major: http://caniuse.com/#search=download
499
        $('[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...
500
            var datestr = new Date().toString().replace(/ /gi, '-');
501
            var data = encodeURIComponent(JSON.stringify(JSON_DATA.val(), 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