Completed
Push — master ( e2de76...dced21 )
by Chris
01:24
created

app.js ➔ addResizeEvent   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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