Completed
Push — master ( 47fa31...5e32c6 )
by Chris
01:17
created

app.js ➔ prettifyJSONPreview   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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