Completed
Push — master ( b20456...a613a9 )
by LA
01:24
created

?!?.?!?   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 33
rs 8.8571
1
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
0 ignored issues
show
Unused Code introduced by
The parameter exports 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...
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
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...
Unused Code introduced by
The parameter module 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...
2
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
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...
3
4
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
5
6
var elementResizeDetectorMaker = require('element-resize-detector');
7
8 View Code Duplication
var CoverImage = function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
9
	function CoverImage(el, cb) {
10
		_classCallCheck(this, CoverImage);
11
12
		var _this = this;
13
14
		_this.$el = el ? el : window;
15
		_this.$img = _this.getElementForSizing();
16
		_this.disableOnMobile = _this.$el.dataset['cover-image-mobile'] === 'false';
17
		_this.cb = cb || function () {
18
			//DEBUG console.log("Default callback");
19
		};
20
21
		_this.positioning = {
22
			x: 0.5,
23
			y: 0.5
24
		};
25
		_this.options = {
26
			parallax: _this.$el.dataset.coverImageParallax === 'true'
27
		};
28
29
		if (!_this.$img) {
30
			console.log('Error:', 'no image found', _this.$img);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
31
			return;
32
		}
33
34
		_this.imageWidth = _this.$img.width;
35
		_this.imageHeight = _this.$img.height;
36
37
		// If the image doesn't have harcoded width|height
38
		// attributes then load the image to calculate
39
		// the dimensions
40
		if (!_this.imageWidth || !_this.imageHeight) {
41
			console.log('No dimensions found. Generating image:', _this.$img.attr('src'));
42
			_this.img = new Image();
0 ignored issues
show
Bug introduced by
The variable Image seems to be never declared. If this is a global, consider adding a /** global: Image */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
43
			_this.img.src = _this.$img.attr('src');
44
45
			_this.imageWidth = _this.img.width;
46
			_this.imageHeight = _this.img.height;
47
		}
48
49
		if (_this.disableOnMobile && window.innerWidth < 480) {
50
			return;
51
		}
52
53
		_this.elementDimensions = {
54
			height: _this.$el.clientHeight,
55
			width: _this.$el.clientWidth
56
		};
57
58
		_this.$el.style = '\n\t\t\toverflow : hidden;\n\t\t\tposition : relative;\n\t\t';
59
60
		if (!_this.$img) {
61
			// TODO: Implement load
62
			setTimeout(function () {
63
				new CoverImage(_this.$el);
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new CoverImage(_this.$el) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
64
			}, 1000);
65
		} else {
66
			_this.resizeImage();
67
		}
68
69
		_this.$img.addEventListener('load', function () {
70
			_this.resizeImage();
71
		}, false);
72
73
		_this.$el.addEventListener('transitionend', function () {
74
			_this.resizeImage();
75
		}, false);
76
77
		var erd = elementResizeDetectorMaker({
78
			strategy: 'scroll'
79
		});
80
81
		erd.listenTo(_this.$el, function () {
82
			_this.resizeImage();
83
		});
84
85
		_this.$el.addEventListener('animationend', function () {
86
			_this.resizeImage();
87
		}, false);
88
89
		window.addEventListener('resize', function () {
90
			_this.resizeImage();
91
		}, true);
92
93
		window.addEventListener('ci.resize', function () {
94
			_this.resizeImage();
95
		}, true);
96
97
		if (_this.options.parallax) {
98
			_this.draw();
99
		}
100
	}
101
102
	/**
103
  * Parallax FX
104
  *
105
  */
106
107
108
	_createClass(CoverImage, [{
109
		key: 'draw',
110
		value: function draw() {
111
			var _this = this;
112
			var friction = 0.5;
113
			var imageOffsetX = document.body.scrollTop * friction;
114
			var imageOffsetY = document.body.scrollTop * friction;
115
			var maximumMovementY = (_this.imageDimensions.height - _this.elementDimensions.height) * _this.positioning.y;
116
			var maximumMovementX = (_this.imageDimensions.width - _this.elementDimensions.width) * _this.positioning.x;
117
118
			if (maximumMovementX > 0) {
119
				if (imageOffsetX < maximumMovementX) {
120
					// console.log('New position:', maximumMovementX - imageOffsetX);
121
					_this.$img.css({
122
						'transform': 'translateX(' + (maximumMovementX - imageOffsetX) + 'px)'
123
					});
124
				}
125
			} else {
126
				if (imageOffsetY < maximumMovementY) {
127
					_this.$img.css('transform', 'translateY(' + (maximumMovementY - imageOffsetY) + 'px)');
128
				}
129
			}
130
131
			window.requestAnimationFrame(function () {
132
				_this.draw();
133
			});
134
		}
135
136
		/**
137
   *
138
   * @return DOM Element
139
   */
140
141
	}, {
142
		key: 'getElementForSizing',
143
		value: function getElementForSizing() {
144
			var _this = this;
145
			var selector = _this.$el.dataset['coverImageEl'];
146
147
			if (selector) {
148
149
				console.log('Element selector Present', _this.$el.find(selector));
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
150
151
				return _this.$el.find(selector) ? _this.$el.find(selector) : _this.$el.find('img');
152
			}
153
154
			return _this.$el.querySelector('img');
155
		}
156
	}, {
157
		key: 'resizeImage',
158
		value: function resizeImage() {
159
			var _this = this;
160
			var elementWidth = _this.$el.clientWidth;
161
			var elementHeight = _this.$el.clientHeight;
162
			var dimensions = _this.coverDimensions(_this.imageWidth, _this.imageHeight, elementWidth, elementHeight);
163
164
			_this.imageDimensions = dimensions;
165
166
			if (isNaN(dimensions.width)) {
167
				console.log('Failed to calculate image sizes.');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
168
			}
169
170
			_this.setImageSize();
171
		}
172
	}, {
173
		key: 'setImageSize',
174
		value: function setImageSize() {
175
			var _this = this;
176
177
			_this.$img.width = _this.imageDimensions.width;
178
			_this.$img.height = _this.imageDimensions.height;
179
180
			var transform = _this.getTransform((_this.$el.clientWidth - _this.imageDimensions.width) * _this.positioning.y, (_this.$el.clientHeight - _this.imageDimensions.height) * _this.positioning.x);
181
182
			_this.$img.style = '\n\t\t\tposition: absolute;\n\t\t\twidth: ' + _this.imageDimensions.width + ';\n\t\t\theight: ' + _this.imageDimensions.height + ';\n\t\t\ttransform: ' + transform + ';\n\t\t\tmax-width: none;\n\t\t';
183
184
			_this.$img.classList.add('ci--sized');
185
			_this.cb();
186
		}
187
	}, {
188
		key: 'getTransform',
189
		value: function getTransform(x, y) {
190
			return 'translate3d(' + x + 'px,' + y + 'px,0)';
191
		}
192
	}, {
193
		key: 'coverDimensions',
194
		value: function coverDimensions(child_w, child_h, container_w, container_h) {
195
			var scale_factor = this.max(container_w / child_w, container_h / child_h);
196
197
			return {
198
				width: Math.ceil(child_w * scale_factor),
199
				height: Math.ceil(child_h * scale_factor)
200
			};
201
		}
202
	}, {
203
		key: 'containDimensions',
204
		value: function containDimensions(child_w, child_h, container_w, container_h) {
205
			var scale_factor = this.min(container_w / child_w, container_h / child_h);
206
207
			return {
208
				width: child_w * scale_factor,
209
				height: child_h * scale_factor
210
			};
211
		}
212
	}, {
213
		key: 'min',
214
		value: function min(a, b) {
215
			return a > b ? b : a;
216
		}
217
	}, {
218
		key: 'max',
219
		value: function max(a, b) {
220
			return a > b ? a : b;
221
		}
222
	}]);
223
224
	return CoverImage;
225
}();
226
227
var elems = document.body.querySelectorAll('[data-cover-image]');
228
229
elems.forEach(function (el) {
230
	new CoverImage(el);
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new CoverImage(el) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
231
});
232
//# sourceMappingURL=coverimage.js.map
233
},{"element-resize-detector":8}],2:[function(require,module,exports){
234
"use strict";
235
236
var utils = require("./utils");
237
238
module.exports = function batchProcessorMaker(options) {
239
    options             = options || {};
240
    var reporter        = options.reporter;
241
    var asyncProcess    = utils.getOption(options, "async", true);
242
    var autoProcess     = utils.getOption(options, "auto", true);
243
244
    if(autoProcess && !asyncProcess) {
245
        reporter && reporter.warn("Invalid options combination. auto=true and async=false is invalid. Setting async=true.");
246
        asyncProcess = true;
247
    }
248
249
    var batch = Batch();
250
    var asyncFrameHandler;
251
    var isProcessing = false;
252
253
    function addFunction(level, fn) {
254
        if(!isProcessing && autoProcess && asyncProcess && batch.size() === 0) {
255
            // Since this is async, it is guaranteed to be executed after that the fn is added to the batch.
256
            // This needs to be done before, since we're checking the size of the batch to be 0.
257
            processBatchAsync();
258
        }
259
260
        batch.add(level, fn);
261
    }
262
263
    function processBatch() {
264
        // Save the current batch, and create a new batch so that incoming functions are not added into the currently processing batch.
265
        // Continue processing until the top-level batch is empty (functions may be added to the new batch while processing, and so on).
266
        isProcessing = true;
267
        while (batch.size()) {
0 ignored issues
show
Bug introduced by
The variable batch is changed as part of the while loop for example by Batch() on line 269. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
268
            var processingBatch = batch;
269
            batch = Batch();
270
            processingBatch.process();
271
        }
272
        isProcessing = false;
273
    }
274
275
    function forceProcessBatch(localAsyncProcess) {
276
        if (isProcessing) {
277
            return;
278
        }
279
280
        if(localAsyncProcess === undefined) {
281
            localAsyncProcess = asyncProcess;
282
        }
283
284
        if(asyncFrameHandler) {
285
            cancelFrame(asyncFrameHandler);
286
            asyncFrameHandler = null;
287
        }
288
289
        if(localAsyncProcess) {
290
            processBatchAsync();
291
        } else {
292
            processBatch();
293
        }
294
    }
295
296
    function processBatchAsync() {
297
        asyncFrameHandler = requestFrame(processBatch);
298
    }
299
300
    function clearBatch() {
0 ignored issues
show
introduced by
The function clearBatch does not seem to be used and can be removed.
Loading history...
301
        batch           = {};
302
        batchSize       = 0;
0 ignored issues
show
Bug introduced by
The variable batchSize 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.batchSize.
Loading history...
303
        topLevel        = 0;
0 ignored issues
show
Bug introduced by
The variable topLevel 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.topLevel.
Loading history...
304
        bottomLevel     = 0;
0 ignored issues
show
Bug introduced by
The variable bottomLevel 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.bottomLevel.
Loading history...
305
    }
306
307
    function cancelFrame(listener) {
308
        // var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.clearTimeout;
309
        var cancel = clearTimeout;
0 ignored issues
show
Bug introduced by
The variable clearTimeout seems to be never declared. If this is a global, consider adding a /** global: clearTimeout */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
310
        return cancel(listener);
311
    }
312
313
    function requestFrame(callback) {
314
        // var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || function(fn) { return window.setTimeout(fn, 20); };
315
        var raf = function(fn) { return setTimeout(fn, 0); };
316
        return raf(callback);
317
    }
318
319
    return {
320
        add: addFunction,
321
        force: forceProcessBatch
322
    };
323
};
324
325
function Batch() {
326
    var batch       = {};
327
    var size        = 0;
328
    var topLevel    = 0;
329
    var bottomLevel = 0;
330
331
    function add(level, fn) {
332
        if(!fn) {
333
            fn = level;
334
            level = 0;
335
        }
336
337
        if(level > topLevel) {
338
            topLevel = level;
339
        } else if(level < bottomLevel) {
340
            bottomLevel = level;
341
        }
342
343
        if(!batch[level]) {
344
            batch[level] = [];
345
        }
346
347
        batch[level].push(fn);
348
        size++;
349
    }
350
351
    function process() {
352
        for(var level = bottomLevel; level <= topLevel; level++) {
353
            var fns = batch[level];
354
355
            for(var i = 0; i < fns.length; i++) {
356
                var fn = fns[i];
357
                fn();
358
            }
359
        }
360
    }
361
362
    function getSize() {
363
        return size;
364
    }
365
366
    return {
367
        add: add,
368
        process: process,
369
        size: getSize
370
    };
371
}
372
373
},{"./utils":3}],3:[function(require,module,exports){
374
"use strict";
375
376
var utils = module.exports = {};
377
378
utils.getOption = getOption;
379
380
function getOption(options, name, defaultValue) {
381
    var value = options[name];
382
383
    if((value === undefined || value === null) && defaultValue !== undefined) {
384
        return defaultValue;
385
    }
386
387
    return value;
388
}
389
390
},{}],4:[function(require,module,exports){
391
"use strict";
392
393
var detector = module.exports = {};
394
395
detector.isIE = function(version) {
396
    function isAnyIeVersion() {
397
        var agent = navigator.userAgent.toLowerCase();
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
398
        return agent.indexOf("msie") !== -1 || agent.indexOf("trident") !== -1 || agent.indexOf(" edge/") !== -1;
399
    }
400
401
    if(!isAnyIeVersion()) {
402
        return false;
403
    }
404
405
    if(!version) {
406
        return true;
407
    }
408
409
    //Shamelessly stolen from https://gist.github.com/padolsey/527683
410
    var ieVersion = (function(){
411
        var undef,
412
            v = 3,
413
            div = document.createElement("div"),
414
            all = div.getElementsByTagName("i");
415
416
        do {
417
            div.innerHTML = "<!--[if gt IE " + (++v) + "]><i></i><![endif]-->";
418
        }
419
        while (all[0]);
420
421
        return v > 4 ? v : undef;
0 ignored issues
show
Bug introduced by
The variable undef seems to be never initialized.
Loading history...
422
    }());
423
424
    return version === ieVersion;
425
};
426
427
detector.isLegacyOpera = function() {
428
    return !!window.opera;
429
};
430
431
},{}],5:[function(require,module,exports){
432
"use strict";
433
434
var utils = module.exports = {};
435
436
/**
437
 * Loops through the collection and calls the callback for each element. if the callback returns truthy, the loop is broken and returns the same value.
438
 * @public
439
 * @param {*} collection The collection to loop through. Needs to have a length property set and have indices set from 0 to length - 1.
440
 * @param {function} callback The callback to be called for each element. The element will be given as a parameter to the callback. If this callback returns truthy, the loop is broken and the same value is returned.
441
 * @returns {*} The value that a callback has returned (if truthy). Otherwise nothing.
442
 */
443
utils.forEach = function(collection, callback) {
444
    for(var i = 0; i < collection.length; i++) {
445
        var result = callback(collection[i]);
446
        if(result) {
447
            return result;
448
        }
449
    }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
450
};
451
452
},{}],6:[function(require,module,exports){
453
/**
454
 * Resize detection strategy that injects objects to elements in order to detect resize events.
455
 * Heavily inspired by: http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/
456
 */
457
458
"use strict";
459
460
var browserDetector = require("../browser-detector");
461
462
module.exports = function(options) {
463
    options             = options || {};
464
    var reporter        = options.reporter;
465
    var batchProcessor  = options.batchProcessor;
466
    var getState        = options.stateHandler.getState;
467
468
    if(!reporter) {
469
        throw new Error("Missing required dependency: reporter.");
470
    }
471
472
    /**
473
     * Adds a resize event listener to the element.
474
     * @public
475
     * @param {element} element The element that should have the listener added.
476
     * @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
477
     */
478
    function addListener(element, listener) {
479
        if(!getObject(element)) {
480
            throw new Error("Element is not detectable by this strategy.");
481
        }
482
483
        function listenerProxy() {
484
            listener(element);
485
        }
486
487
        if(browserDetector.isIE(8)) {
488
            //IE 8 does not support object, but supports the resize event directly on elements.
489
            getState(element).object = {
490
                proxy: listenerProxy
491
            };
492
            element.attachEvent("onresize", listenerProxy);
493
        } else {
494
            var object = getObject(element);
495
            object.contentDocument.defaultView.addEventListener("resize", listenerProxy);
496
        }
497
    }
498
499
    /**
500
     * Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
501
     * @private
502
     * @param {object} options Optional options object.
503
     * @param {element} element The element to make detectable
504
     * @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
505
     */
506
    function makeDetectable(options, element, callback) {
507
        if (!callback) {
508
            callback = element;
509
            element = options;
510
            options = null;
511
        }
512
513
        options = options || {};
514
        var debug = options.debug;
0 ignored issues
show
Unused Code introduced by
The assignment to variable debug seems to be never used. Consider removing it.
Loading history...
515
516
        function injectObject(element, callback) {
517
            var OBJECT_STYLE = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding: 0; margin: 0; opacity: 0; z-index: -1000; pointer-events: none;";
518
519
            //The target element needs to be positioned (everything except static) so the absolute positioned object will be positioned relative to the target element.
520
521
            // Position altering may be performed directly or on object load, depending on if style resolution is possible directly or not.
522
            var positionCheckPerformed = false;
523
524
            // The element may not yet be attached to the DOM, and therefore the style object may be empty in some browsers.
525
            // Since the style object is a reference, it will be updated as soon as the element is attached to the DOM.
526
            var style = window.getComputedStyle(element);
527
            var width = element.offsetWidth;
528
            var height = element.offsetHeight;
529
530
            getState(element).startSize = {
531
                width: width,
532
                height: height
533
            };
534
535
            function mutateDom() {
536
                function alterPositionStyles() {
537
                    if(style.position === "static") {
538
                        element.style.position = "relative";
539
540
                        var removeRelativeStyles = function(reporter, element, style, property) {
541
                            function getNumericalValue(value) {
542
                                return value.replace(/[^-\d\.]/g, "");
543
                            }
544
545
                            var value = style[property];
546
547
                            if(value !== "auto" && getNumericalValue(value) !== "0") {
548
                                reporter.warn("An element that is positioned static has style." + property + "=" + value + " which is ignored due to the static positioning. The element will need to be positioned relative, so the style." + property + " will be set to 0. Element: ", element);
549
                                element.style[property] = 0;
550
                            }
551
                        };
552
553
                        //Check so that there are no accidental styles that will make the element styled differently now that is is relative.
554
                        //If there are any, set them to 0 (this should be okay with the user since the style properties did nothing before [since the element was positioned static] anyway).
555
                        removeRelativeStyles(reporter, element, style, "top");
556
                        removeRelativeStyles(reporter, element, style, "right");
557
                        removeRelativeStyles(reporter, element, style, "bottom");
558
                        removeRelativeStyles(reporter, element, style, "left");
559
                    }
560
                }
561
562
                function onObjectLoad() {
563
                    // The object has been loaded, which means that the element now is guaranteed to be attached to the DOM.
564
                    if (!positionCheckPerformed) {
565
                        alterPositionStyles();
566
                    }
567
568
                    /*jshint validthis: true */
569
570
                    function getDocument(element, callback) {
571
                        //Opera 12 seem to call the object.onload before the actual document has been created.
572
                        //So if it is not present, poll it with an timeout until it is present.
573
                        //TODO: Could maybe be handled better with object.onreadystatechange or similar.
574
                        if(!element.contentDocument) {
575
                            setTimeout(function checkForObjectDocument() {
576
                                getDocument(element, callback);
577
                            }, 100);
578
579
                            return;
580
                        }
581
582
                        callback(element.contentDocument);
583
                    }
584
585
                    //Mutating the object element here seems to fire another load event.
586
                    //Mutating the inner document of the object element is fine though.
587
                    var objectElement = this;
588
589
                    //Create the style element to be added to the object.
590
                    getDocument(objectElement, function onObjectDocumentReady(objectDocument) {
0 ignored issues
show
Unused Code introduced by
The parameter objectDocument 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...
591
                        //Notify that the element is ready to be listened to.
592
                        callback(element);
593
                    });
594
                }
595
596
                // The element may be detached from the DOM, and some browsers does not support style resolving of detached elements.
597
                // The alterPositionStyles needs to be delayed until we know the element has been attached to the DOM (which we are sure of when the onObjectLoad has been fired), if style resolution is not possible.
598
                if (style.position !== "") {
599
                    alterPositionStyles(style);
0 ignored issues
show
Bug introduced by
The call to alterPositionStyles seems to have too many arguments starting with style.
Loading history...
600
                    positionCheckPerformed = true;
601
                }
602
603
                //Add an object element as a child to the target element that will be listened to for resize events.
604
                var object = document.createElement("object");
605
                object.style.cssText = OBJECT_STYLE;
606
                object.tabIndex = -1;
607
                object.type = "text/html";
608
                object.onload = onObjectLoad;
609
610
                //Safari: This must occur before adding the object to the DOM.
611
                //IE: Does not like that this happens before, even if it is also added after.
612
                if(!browserDetector.isIE()) {
613
                    object.data = "about:blank";
614
                }
615
616
                element.appendChild(object);
617
                getState(element).object = object;
618
619
                //IE: This must occur after adding the object to the DOM.
620
                if(browserDetector.isIE()) {
621
                    object.data = "about:blank";
622
                }
623
            }
624
625
            if(batchProcessor) {
626
                batchProcessor.add(mutateDom);
627
            } else {
628
                mutateDom();
629
            }
630
        }
631
632
        if(browserDetector.isIE(8)) {
633
            //IE 8 does not support objects properly. Luckily they do support the resize event.
634
            //So do not inject the object and notify that the element is already ready to be listened to.
635
            //The event handler for the resize event is attached in the utils.addListener instead.
636
            callback(element);
637
        } else {
638
            injectObject(element, callback);
639
        }
640
    }
641
642
    /**
643
     * Returns the child object of the target element.
644
     * @private
645
     * @param {element} element The target element.
646
     * @returns The object element of the target.
647
     */
648
    function getObject(element) {
649
        return getState(element).object;
650
    }
651
652
    function uninstall(element) {
653
        if(browserDetector.isIE(8)) {
654
            element.detachEvent("onresize", getState(element).object.proxy);
655
        } else {
656
            element.removeChild(getObject(element));
657
        }
658
        delete getState(element).object;
659
    }
660
661
    return {
662
        makeDetectable: makeDetectable,
663
        addListener: addListener,
664
        uninstall: uninstall
665
    };
666
};
667
668
},{"../browser-detector":4}],7:[function(require,module,exports){
669
/**
670
 * Resize detection strategy that injects divs to elements in order to detect resize events on scroll events.
671
 * Heavily inspired by: https://github.com/marcj/css-element-queries/blob/master/src/ResizeSensor.js
672
 */
673
674
"use strict";
675
676
var forEach = require("../collection-utils").forEach;
677
678
module.exports = function(options) {
679
    options             = options || {};
680
    var reporter        = options.reporter;
681
    var batchProcessor  = options.batchProcessor;
682
    var getState        = options.stateHandler.getState;
683
    var hasState        = options.stateHandler.hasState;
0 ignored issues
show
Unused Code introduced by
The assignment to variable hasState seems to be never used. Consider removing it.
Loading history...
684
    var idHandler       = options.idHandler;
685
686
    if (!batchProcessor) {
687
        throw new Error("Missing required dependency: batchProcessor");
688
    }
689
690
    if (!reporter) {
691
        throw new Error("Missing required dependency: reporter.");
692
    }
693
694
    //TODO: Could this perhaps be done at installation time?
695
    var scrollbarSizes = getScrollbarSizes();
696
697
    // Inject the scrollbar styling that prevents them from appearing sometimes in Chrome.
698
    // The injected container needs to have a class, so that it may be styled with CSS (pseudo elements).
699
    var styleId = "erd_scroll_detection_scrollbar_style";
700
    var detectionContainerClass = "erd_scroll_detection_container";
701
    injectScrollStyle(styleId, detectionContainerClass);
702
703
    function getScrollbarSizes() {
704
        var width = 500;
705
        var height = 500;
706
707
        var child = document.createElement("div");
708
        child.style.cssText = "position: absolute; width: " + width*2 + "px; height: " + height*2 + "px; visibility: hidden; margin: 0; padding: 0;";
709
710
        var container = document.createElement("div");
711
        container.style.cssText = "position: absolute; width: " + width + "px; height: " + height + "px; overflow: scroll; visibility: none; top: " + -width*3 + "px; left: " + -height*3 + "px; visibility: hidden; margin: 0; padding: 0;";
712
713
        container.appendChild(child);
714
715
        document.body.insertBefore(container, document.body.firstChild);
716
717
        var widthSize = width - container.clientWidth;
718
        var heightSize = height - container.clientHeight;
719
720
        document.body.removeChild(container);
721
722
        return {
723
            width: widthSize,
724
            height: heightSize
725
        };
726
    }
727
728
    function injectScrollStyle(styleId, containerClass) {
729
        function injectStyle(style, method) {
730
            method = method || function (element) {
731
                document.head.appendChild(element);
732
            };
733
734
            var styleElement = document.createElement("style");
735
            styleElement.innerHTML = style;
736
            styleElement.id = styleId;
737
            method(styleElement);
738
            return styleElement;
739
        }
740
741
        if (!document.getElementById(styleId)) {
742
            var containerAnimationClass = containerClass + "_animation";
743
            var containerAnimationActiveClass = containerClass + "_animation_active";
744
            var style = "/* Created by the element-resize-detector library. */\n";
745
            style += "." + containerClass + " > div::-webkit-scrollbar { display: none; }\n\n";
746
            style += "." + containerAnimationActiveClass + " { -webkit-animation-duration: 0.1s; animation-duration: 0.1s; -webkit-animation-name: " + containerAnimationClass + "; animation-name: " + containerAnimationClass + "; }\n";
747
            style += "@-webkit-keyframes " + containerAnimationClass +  " { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } }\n";
748
            style += "@keyframes " + containerAnimationClass +          " { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } }";
749
            injectStyle(style);
750
        }
751
    }
752
753
    function addAnimationClass(element) {
754
        element.className += " " + detectionContainerClass + "_animation_active";
755
    }
756
757
    function addEvent(el, name, cb) {
758
        if (el.addEventListener) {
759
            el.addEventListener(name, cb);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
760
        } else if(el.attachEvent) {
761
            el.attachEvent("on" + name, cb);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
762
        } else {
763
            return reporter.error("[scroll] Don't know how to add event listeners.");
764
        }
765
    }
766
767
    function removeEvent(el, name, cb) {
768
        if (el.removeEventListener) {
769
            el.removeEventListener(name, cb);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
770
        } else if(el.detachEvent) {
771
            el.detachEvent("on" + name, cb);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
772
        } else {
773
            return reporter.error("[scroll] Don't know how to remove event listeners.");
774
        }
775
    }
776
777
    function getExpandElement(element) {
778
        return getState(element).container.childNodes[0].childNodes[0].childNodes[0];
779
    }
780
781
    function getShrinkElement(element) {
782
        return getState(element).container.childNodes[0].childNodes[0].childNodes[1];
783
    }
784
785
    /**
786
     * Adds a resize event listener to the element.
787
     * @public
788
     * @param {element} element The element that should have the listener added.
789
     * @param {function} listener The listener callback to be called for each resize event of the element. The element will be given as a parameter to the listener callback.
790
     */
791
    function addListener(element, listener) {
792
        var listeners = getState(element).listeners;
793
794
        if (!listeners.push) {
795
            throw new Error("Cannot add listener to an element that is not detectable.");
796
        }
797
798
        getState(element).listeners.push(listener);
799
    }
800
801
    /**
802
     * Makes an element detectable and ready to be listened for resize events. Will call the callback when the element is ready to be listened for resize changes.
803
     * @private
804
     * @param {object} options Optional options object.
805
     * @param {element} element The element to make detectable
806
     * @param {function} callback The callback to be called when the element is ready to be listened for resize changes. Will be called with the element as first parameter.
807
     */
808
    function makeDetectable(options, element, callback) {
809
        if (!callback) {
810
            callback = element;
811
            element = options;
812
            options = null;
813
        }
814
815
        options = options || {};
816
817
        function debug() {
818
            if (options.debug) {
819
                var args = Array.prototype.slice.call(arguments);
820
                args.unshift(idHandler.get(element), "Scroll: ");
821
                if (reporter.log.apply) {
822
                    reporter.log.apply(null, args);
823
                } else {
824
                    for (var i = 0; i < args.length; i++) {
825
                        reporter.log(args[i]);
826
                    }
827
                }
828
            }
829
        }
830
831
        function isDetached(element) {
832
            function isInDocument(element) {
833
                return element === element.ownerDocument.body || element.ownerDocument.body.contains(element);
834
            }
835
            return !isInDocument(element);
836
        }
837
838
        function isUnrendered(element) {
839
            // Check the absolute positioned container since the top level container is display: inline.
840
            var container = getState(element).container.childNodes[0];
841
            return getComputedStyle(container).width.indexOf("px") === -1; //Can only compute pixel value when rendered.
842
        }
843
844
        function getStyle() {
845
            // Some browsers only force layouts when actually reading the style properties of the style object, so make sure that they are all read here,
846
            // so that the user of the function can be sure that it will perform the layout here, instead of later (important for batching).
847
            var elementStyle            = getComputedStyle(element);
848
            var style                   = {};
849
            style.position              = elementStyle.position;
850
            style.width                 = element.offsetWidth;
851
            style.height                = element.offsetHeight;
852
            style.top                   = elementStyle.top;
853
            style.right                 = elementStyle.right;
854
            style.bottom                = elementStyle.bottom;
855
            style.left                  = elementStyle.left;
856
            style.widthCSS              = elementStyle.width;
857
            style.heightCSS             = elementStyle.height;
858
            return style;
859
        }
860
861
        function storeStartSize() {
862
            var style = getStyle();
863
            getState(element).startSize = {
864
                width: style.width,
865
                height: style.height
866
            };
867
            debug("Element start size", getState(element).startSize);
868
        }
869
870
        function initListeners() {
871
            getState(element).listeners = [];
872
        }
873
874
        function storeStyle() {
875
            debug("storeStyle invoked.");
876
            if (!getState(element)) {
877
                debug("Aborting because element has been uninstalled");
878
                return;
879
            }
880
881
            var style = getStyle();
882
            getState(element).style = style;
883
        }
884
885
        function storeCurrentSize(element, width, height) {
886
            getState(element).lastWidth = width;
887
            getState(element).lastHeight  = height;
888
        }
889
890
        function getExpandChildElement(element) {
891
            return getExpandElement(element).childNodes[0];
892
        }
893
894
        function getWidthOffset() {
895
            return 2 * scrollbarSizes.width + 1;
896
        }
897
898
        function getHeightOffset() {
899
            return 2 * scrollbarSizes.height + 1;
900
        }
901
902
        function getExpandWidth(width) {
903
            return width + 10 + getWidthOffset();
904
        }
905
906
        function getExpandHeight(height) {
907
            return height + 10 + getHeightOffset();
908
        }
909
910
        function getShrinkWidth(width) {
911
            return width * 2 + getWidthOffset();
912
        }
913
914
        function getShrinkHeight(height) {
915
            return height * 2 + getHeightOffset();
916
        }
917
918
        function positionScrollbars(element, width, height) {
919
            var expand          = getExpandElement(element);
920
            var shrink          = getShrinkElement(element);
921
            var expandWidth     = getExpandWidth(width);
922
            var expandHeight    = getExpandHeight(height);
923
            var shrinkWidth     = getShrinkWidth(width);
924
            var shrinkHeight    = getShrinkHeight(height);
925
            expand.scrollLeft   = expandWidth;
926
            expand.scrollTop    = expandHeight;
927
            shrink.scrollLeft   = shrinkWidth;
928
            shrink.scrollTop    = shrinkHeight;
929
        }
930
931
        function injectContainerElement() {
932
            var container = getState(element).container;
933
934
            if (!container) {
935
                container                   = document.createElement("div");
936
                container.className         = detectionContainerClass;
937
                container.style.cssText     = "visibility: hidden; display: inline; width: 0px; height: 0px; z-index: -1; overflow: hidden; margin: 0; padding: 0;";
938
                getState(element).container = container;
939
                addAnimationClass(container);
940
                element.appendChild(container);
941
942
                var onAnimationStart = function () {
943
                    getState(element).onRendered && getState(element).onRendered();
944
                };
945
946
                addEvent(container, "animationstart", onAnimationStart);
947
948
                // Store the event handler here so that they may be removed when uninstall is called.
949
                // See uninstall function for an explanation why it is needed.
950
                getState(element).onAnimationStart = onAnimationStart;
951
            }
952
953
            return container;
954
        }
955
956
        function injectScrollElements() {
957
            function alterPositionStyles() {
958
                var style = getState(element).style;
959
960
                if(style.position === "static") {
961
                    element.style.position = "relative";
962
963
                    var removeRelativeStyles = function(reporter, element, style, property) {
964
                        function getNumericalValue(value) {
965
                            return value.replace(/[^-\d\.]/g, "");
966
                        }
967
968
                        var value = style[property];
969
970
                        if(value !== "auto" && getNumericalValue(value) !== "0") {
971
                            reporter.warn("An element that is positioned static has style." + property + "=" + value + " which is ignored due to the static positioning. The element will need to be positioned relative, so the style." + property + " will be set to 0. Element: ", element);
972
                            element.style[property] = 0;
973
                        }
974
                    };
975
976
                    //Check so that there are no accidental styles that will make the element styled differently now that is is relative.
977
                    //If there are any, set them to 0 (this should be okay with the user since the style properties did nothing before [since the element was positioned static] anyway).
978
                    removeRelativeStyles(reporter, element, style, "top");
979
                    removeRelativeStyles(reporter, element, style, "right");
980
                    removeRelativeStyles(reporter, element, style, "bottom");
981
                    removeRelativeStyles(reporter, element, style, "left");
982
                }
983
            }
984
985
            function getLeftTopBottomRightCssText(left, top, bottom, right) {
986
                left = (!left ? "0" : (left + "px"));
987
                top = (!top ? "0" : (top + "px"));
988
                bottom = (!bottom ? "0" : (bottom + "px"));
989
                right = (!right ? "0" : (right + "px"));
990
991
                return "left: " + left + "; top: " + top + "; right: " + right + "; bottom: " + bottom + ";";
992
            }
993
994
            debug("Injecting elements");
995
996
            if (!getState(element)) {
997
                debug("Aborting because element has been uninstalled");
998
                return;
999
            }
1000
1001
            alterPositionStyles();
1002
1003
            var rootContainer = getState(element).container;
1004
1005
            if (!rootContainer) {
1006
                rootContainer = injectContainerElement();
1007
            }
1008
1009
            // Due to this WebKit bug https://bugs.webkit.org/show_bug.cgi?id=80808 (currently fixed in Blink, but still present in WebKit browsers such as Safari),
1010
            // we need to inject two containers, one that is width/height 100% and another that is left/top -1px so that the final container always is 1x1 pixels bigger than
1011
            // the targeted element.
1012
            // When the bug is resolved, "containerContainer" may be removed.
1013
1014
            // The outer container can occasionally be less wide than the targeted when inside inline elements element in WebKit (see https://bugs.webkit.org/show_bug.cgi?id=152980).
1015
            // This should be no problem since the inner container either way makes sure the injected scroll elements are at least 1x1 px.
1016
1017
            var scrollbarWidth          = scrollbarSizes.width;
1018
            var scrollbarHeight         = scrollbarSizes.height;
1019
            var containerContainerStyle = "position: absolute; flex: none; overflow: hidden; z-index: -1; visibility: hidden; width: 100%; height: 100%; left: 0px; top: 0px;";
1020
            var containerStyle          = "position: absolute; flex: none; overflow: hidden; z-index: -1; visibility: hidden; " + getLeftTopBottomRightCssText(-(1 + scrollbarWidth), -(1 + scrollbarHeight), -scrollbarHeight, -scrollbarWidth);
1021
            var expandStyle             = "position: absolute; flex: none; overflow: scroll; z-index: -1; visibility: hidden; width: 100%; height: 100%;";
1022
            var shrinkStyle             = "position: absolute; flex: none; overflow: scroll; z-index: -1; visibility: hidden; width: 100%; height: 100%;";
1023
            var expandChildStyle        = "position: absolute; left: 0; top: 0;";
1024
            var shrinkChildStyle        = "position: absolute; width: 200%; height: 200%;";
1025
1026
            var containerContainer      = document.createElement("div");
1027
            var container               = document.createElement("div");
1028
            var expand                  = document.createElement("div");
1029
            var expandChild             = document.createElement("div");
1030
            var shrink                  = document.createElement("div");
1031
            var shrinkChild             = document.createElement("div");
1032
1033
            // Some browsers choke on the resize system being rtl, so force it to ltr. https://github.com/wnr/element-resize-detector/issues/56
1034
            // However, dir should not be set on the top level container as it alters the dimensions of the target element in some browsers.
1035
            containerContainer.dir              = "ltr";
1036
1037
            containerContainer.style.cssText    = containerContainerStyle;
1038
            containerContainer.className        = detectionContainerClass;
1039
            container.className                 = detectionContainerClass;
1040
            container.style.cssText             = containerStyle;
1041
            expand.style.cssText                = expandStyle;
1042
            expandChild.style.cssText           = expandChildStyle;
1043
            shrink.style.cssText                = shrinkStyle;
1044
            shrinkChild.style.cssText           = shrinkChildStyle;
1045
1046
            expand.appendChild(expandChild);
1047
            shrink.appendChild(shrinkChild);
1048
            container.appendChild(expand);
1049
            container.appendChild(shrink);
1050
            containerContainer.appendChild(container);
1051
            rootContainer.appendChild(containerContainer);
1052
1053
            function onExpandScroll() {
1054
                getState(element).onExpand && getState(element).onExpand();
1055
            }
1056
1057
            function onShrinkScroll() {
1058
                getState(element).onShrink && getState(element).onShrink();
1059
            }
1060
1061
            addEvent(expand, "scroll", onExpandScroll);
1062
            addEvent(shrink, "scroll", onShrinkScroll);
1063
1064
            // Store the event handlers here so that they may be removed when uninstall is called.
1065
            // See uninstall function for an explanation why it is needed.
1066
            getState(element).onExpandScroll = onExpandScroll;
1067
            getState(element).onShrinkScroll = onShrinkScroll;
1068
        }
1069
1070
        function registerListenersAndPositionElements() {
1071
            function updateChildSizes(element, width, height) {
1072
                var expandChild             = getExpandChildElement(element);
1073
                var expandWidth             = getExpandWidth(width);
1074
                var expandHeight            = getExpandHeight(height);
1075
                expandChild.style.width     = expandWidth + "px";
1076
                expandChild.style.height    = expandHeight + "px";
1077
            }
1078
1079
            function updateDetectorElements(done) {
1080
                var width           = element.offsetWidth;
1081
                var height          = element.offsetHeight;
1082
1083
                debug("Storing current size", width, height);
1084
1085
                // Store the size of the element sync here, so that multiple scroll events may be ignored in the event listeners.
1086
                // Otherwise the if-check in handleScroll is useless.
1087
                storeCurrentSize(element, width, height);
1088
1089
                // Since we delay the processing of the batch, there is a risk that uninstall has been called before the batch gets to execute.
1090
                // Since there is no way to cancel the fn executions, we need to add an uninstall guard to all fns of the batch.
1091
1092
                batchProcessor.add(0, function performUpdateChildSizes() {
1093
                    if (!getState(element)) {
1094
                        debug("Aborting because element has been uninstalled");
1095
                        return;
1096
                    }
1097
1098
                    if (options.debug) {
1099
                        var w = element.offsetWidth;
1100
                        var h = element.offsetHeight;
1101
1102
                        if (w !== width || h !== height) {
1103
                            reporter.warn(idHandler.get(element), "Scroll: Size changed before updating detector elements.");
1104
                        }
1105
                    }
1106
1107
                    updateChildSizes(element, width, height);
1108
                });
1109
1110
                batchProcessor.add(1, function updateScrollbars() {
1111
                    if (!getState(element)) {
1112
                        debug("Aborting because element has been uninstalled");
1113
                        return;
1114
                    }
1115
1116
                    positionScrollbars(element, width, height);
1117
                });
1118
1119
                if (done) {
1120
                    batchProcessor.add(2, function () {
1121
                        if (!getState(element)) {
1122
                            debug("Aborting because element has been uninstalled");
1123
                            return;
1124
                        }
1125
1126
                        done();
1127
                    });
1128
                }
1129
            }
1130
1131
            function areElementsInjected() {
0 ignored issues
show
introduced by
The function areElementsInjected does not seem to be used and can be removed.
Loading history...
1132
                return !!getState(element).container;
1133
            }
1134
1135
            function notifyListenersIfNeeded() {
1136
                function isFirstNotify() {
1137
                    return getState(element).lastNotifiedWidth === undefined;
1138
                }
1139
1140
                debug("notifyListenersIfNeeded invoked");
1141
1142
                var state = getState(element);
1143
1144
                // Don't notify the if the current size is the start size, and this is the first notification.
1145
                if (isFirstNotify() && state.lastWidth === state.startSize.width && state.lastHeight === state.startSize.height) {
1146
                    return debug("Not notifying: Size is the same as the start size, and there has been no notification yet.");
1147
                }
1148
1149
                // Don't notify if the size already has been notified.
1150
                if (state.lastWidth === state.lastNotifiedWidth && state.lastHeight === state.lastNotifiedHeight) {
1151
                    return debug("Not notifying: Size already notified");
1152
                }
1153
1154
1155
                debug("Current size not notified, notifying...");
1156
                state.lastNotifiedWidth = state.lastWidth;
1157
                state.lastNotifiedHeight = state.lastHeight;
1158
                forEach(getState(element).listeners, function (listener) {
1159
                    listener(element);
1160
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1161
            }
1162
1163
            function handleRender() {
1164
                debug("startanimation triggered.");
1165
1166
                if (isUnrendered(element)) {
1167
                    debug("Ignoring since element is still unrendered...");
1168
                    return;
1169
                }
1170
1171
                debug("Element rendered.");
1172
                var expand = getExpandElement(element);
1173
                var shrink = getShrinkElement(element);
1174
                if (expand.scrollLeft === 0 || expand.scrollTop === 0 || shrink.scrollLeft === 0 || shrink.scrollTop === 0) {
1175
                    debug("Scrollbars out of sync. Updating detector elements...");
1176
                    updateDetectorElements(notifyListenersIfNeeded);
1177
                }
1178
            }
1179
1180
            function handleScroll() {
1181
                debug("Scroll detected.");
1182
1183
                if (isUnrendered(element)) {
1184
                    // Element is still unrendered. Skip this scroll event.
1185
                    debug("Scroll event fired while unrendered. Ignoring...");
1186
                    return;
1187
                }
1188
1189
                var width = element.offsetWidth;
1190
                var height = element.offsetHeight;
1191
1192
                if (width !== element.lastWidth || height !== element.lastHeight) {
1193
                    debug("Element size changed.");
1194
                    updateDetectorElements(notifyListenersIfNeeded);
1195
                } else {
1196
                    debug("Element size has not changed (" + width + "x" + height + ").");
1197
                }
1198
            }
1199
1200
            debug("registerListenersAndPositionElements invoked.");
1201
1202
            if (!getState(element)) {
1203
                debug("Aborting because element has been uninstalled");
1204
                return;
1205
            }
1206
1207
            getState(element).onRendered = handleRender;
1208
            getState(element).onExpand = handleScroll;
1209
            getState(element).onShrink = handleScroll;
1210
1211
            var style = getState(element).style;
1212
            updateChildSizes(element, style.width, style.height);
1213
        }
1214
1215
        function finalizeDomMutation() {
1216
            debug("finalizeDomMutation invoked.");
1217
1218
            if (!getState(element)) {
1219
                debug("Aborting because element has been uninstalled");
1220
                return;
1221
            }
1222
1223
            var style = getState(element).style;
1224
            storeCurrentSize(element, style.width, style.height);
1225
            positionScrollbars(element, style.width, style.height);
1226
        }
1227
1228
        function ready() {
1229
            callback(element);
1230
        }
1231
1232
        function install() {
1233
            debug("Installing...");
1234
            initListeners();
1235
            storeStartSize();
1236
1237
            batchProcessor.add(0, storeStyle);
1238
            batchProcessor.add(1, injectScrollElements);
1239
            batchProcessor.add(2, registerListenersAndPositionElements);
1240
            batchProcessor.add(3, finalizeDomMutation);
1241
            batchProcessor.add(4, ready);
1242
        }
1243
1244
        debug("Making detectable...");
1245
1246
        if (isDetached(element)) {
1247
            debug("Element is detached");
1248
1249
            injectContainerElement();
1250
1251
            debug("Waiting until element is attached...");
1252
1253
            getState(element).onRendered = function () {
1254
                debug("Element is now attached");
1255
                install();
1256
            };
1257
        } else {
1258
            install();
1259
        }
1260
    }
1261
1262
    function uninstall(element) {
1263
        var state = getState(element);
1264
1265
        if (!state) {
1266
            // Uninstall has been called on a non-erd element.
1267
            return;
1268
        }
1269
1270
        // Uninstall may have been called in the following scenarios:
1271
        // (1) Right between the sync code and async batch (here state.busy = true, but nothing have been registered or injected).
1272
        // (2) In the ready callback of the last level of the batch by another element (here, state.busy = true, but all the stuff has been injected).
1273
        // (3) After the installation process (here, state.busy = false and all the stuff has been injected).
1274
        // So to be on the safe side, let's check for each thing before removing.
1275
1276
        // We need to remove the event listeners, because otherwise the event might fire on an uninstall element which results in an error when trying to get the state of the element.
1277
        state.onExpandScroll && removeEvent(getExpandElement(element), "scroll", state.onExpandScroll);
1278
        state.onShrinkScroll && removeEvent(getShrinkElement(element), "scroll", state.onShrinkScroll);
1279
        state.onAnimationStart && removeEvent(state.container, "animationstart", state.onAnimationStart);
1280
1281
        state.container && element.removeChild(state.container);
1282
    }
1283
1284
    return {
1285
        makeDetectable: makeDetectable,
1286
        addListener: addListener,
1287
        uninstall: uninstall
1288
    };
1289
};
1290
1291
},{"../collection-utils":5}],8:[function(require,module,exports){
1292
"use strict";
1293
1294
var forEach                 = require("./collection-utils").forEach;
1295
var elementUtilsMaker       = require("./element-utils");
1296
var listenerHandlerMaker    = require("./listener-handler");
1297
var idGeneratorMaker        = require("./id-generator");
1298
var idHandlerMaker          = require("./id-handler");
1299
var reporterMaker           = require("./reporter");
1300
var browserDetector         = require("./browser-detector");
1301
var batchProcessorMaker     = require("batch-processor");
1302
var stateHandler            = require("./state-handler");
1303
1304
//Detection strategies.
1305
var objectStrategyMaker     = require("./detection-strategy/object.js");
1306
var scrollStrategyMaker     = require("./detection-strategy/scroll.js");
1307
1308
function isCollection(obj) {
1309
    return Array.isArray(obj) || obj.length !== undefined;
1310
}
1311
1312
function toArray(collection) {
1313
    if (!Array.isArray(collection)) {
1314
        var array = [];
1315
        forEach(collection, function (obj) {
1316
            array.push(obj);
1317
        });
1318
        return array;
1319
    } else {
1320
        return collection;
1321
    }
1322
}
1323
1324
function isElement(obj) {
1325
    return obj && obj.nodeType === 1;
1326
}
1327
1328
/**
1329
 * @typedef idHandler
1330
 * @type {object}
1331
 * @property {function} get Gets the resize detector id of the element.
1332
 * @property {function} set Generate and sets the resize detector id of the element.
1333
 */
1334
1335
/**
1336
 * @typedef Options
1337
 * @type {object}
1338
 * @property {boolean} callOnAdd    Determines if listeners should be called when they are getting added.
1339
                                    Default is true. If true, the listener is guaranteed to be called when it has been added.
1340
                                    If false, the listener will not be guarenteed to be called when it has been added (does not prevent it from being called).
1341
 * @property {idHandler} idHandler  A custom id handler that is responsible for generating, setting and retrieving id's for elements.
1342
                                    If not provided, a default id handler will be used.
1343
 * @property {reporter} reporter    A custom reporter that handles reporting logs, warnings and errors.
1344
                                    If not provided, a default id handler will be used.
1345
                                    If set to false, then nothing will be reported.
1346
 * @property {boolean} debug        If set to true, the the system will report debug messages as default for the listenTo method.
1347
 */
1348
1349
/**
1350
 * Creates an element resize detector instance.
1351
 * @public
1352
 * @param {Options?} options Optional global options object that will decide how this instance will work.
1353
 */
1354
module.exports = function(options) {
1355
    options = options || {};
1356
1357
    //idHandler is currently not an option to the listenTo function, so it should not be added to globalOptions.
1358
    var idHandler;
1359
1360
    if (options.idHandler) {
1361
        // To maintain compatability with idHandler.get(element, readonly), make sure to wrap the given idHandler
1362
        // so that readonly flag always is true when it's used here. This may be removed next major version bump.
1363
        idHandler = {
1364
            get: function (element) { return options.idHandler.get(element, true); },
1365
            set: options.idHandler.set
1366
        };
1367
    } else {
1368
        var idGenerator = idGeneratorMaker();
1369
        var defaultIdHandler = idHandlerMaker({
1370
            idGenerator: idGenerator,
1371
            stateHandler: stateHandler
1372
        });
1373
        idHandler = defaultIdHandler;
1374
    }
1375
1376
    //reporter is currently not an option to the listenTo function, so it should not be added to globalOptions.
1377
    var reporter = options.reporter;
1378
1379
    if(!reporter) {
1380
        //If options.reporter is false, then the reporter should be quiet.
1381
        var quiet = reporter === false;
1382
        reporter = reporterMaker(quiet);
1383
    }
1384
1385
    //batchProcessor is currently not an option to the listenTo function, so it should not be added to globalOptions.
1386
    var batchProcessor = getOption(options, "batchProcessor", batchProcessorMaker({ reporter: reporter }));
1387
1388
    //Options to be used as default for the listenTo function.
1389
    var globalOptions = {};
1390
    globalOptions.callOnAdd     = !!getOption(options, "callOnAdd", true);
1391
    globalOptions.debug         = !!getOption(options, "debug", false);
1392
1393
    var eventListenerHandler    = listenerHandlerMaker(idHandler);
1394
    var elementUtils            = elementUtilsMaker({
1395
        stateHandler: stateHandler
1396
    });
1397
1398
    //The detection strategy to be used.
1399
    var detectionStrategy;
1400
    var desiredStrategy = getOption(options, "strategy", "object");
1401
    var strategyOptions = {
1402
        reporter: reporter,
1403
        batchProcessor: batchProcessor,
1404
        stateHandler: stateHandler,
1405
        idHandler: idHandler
1406
    };
1407
1408
    if(desiredStrategy === "scroll") {
1409
        if (browserDetector.isLegacyOpera()) {
1410
            reporter.warn("Scroll strategy is not supported on legacy Opera. Changing to object strategy.");
1411
            desiredStrategy = "object";
1412
        } else if (browserDetector.isIE(9)) {
1413
            reporter.warn("Scroll strategy is not supported on IE9. Changing to object strategy.");
1414
            desiredStrategy = "object";
1415
        }
1416
    }
1417
1418
    if(desiredStrategy === "scroll") {
1419
        detectionStrategy = scrollStrategyMaker(strategyOptions);
1420
    } else if(desiredStrategy === "object") {
1421
        detectionStrategy = objectStrategyMaker(strategyOptions);
1422
    } else {
1423
        throw new Error("Invalid strategy name: " + desiredStrategy);
1424
    }
1425
1426
    //Calls can be made to listenTo with elements that are still being installed.
1427
    //Also, same elements can occur in the elements list in the listenTo function.
1428
    //With this map, the ready callbacks can be synchronized between the calls
1429
    //so that the ready callback can always be called when an element is ready - even if
1430
    //it wasn't installed from the function itself.
1431
    var onReadyCallbacks = {};
1432
1433
    /**
1434
     * Makes the given elements resize-detectable and starts listening to resize events on the elements. Calls the event callback for each event for each element.
1435
     * @public
1436
     * @param {Options?} options Optional options object. These options will override the global options. Some options may not be overriden, such as idHandler.
1437
     * @param {element[]|element} elements The given array of elements to detect resize events of. Single element is also valid.
1438
     * @param {function} listener The callback to be executed for each resize event for each element.
1439
     */
1440
    function listenTo(options, elements, listener) {
1441
        function onResizeCallback(element) {
1442
            var listeners = eventListenerHandler.get(element);
1443
            forEach(listeners, function callListenerProxy(listener) {
1444
                listener(element);
1445
            });
1446
        }
1447
1448
        function addListener(callOnAdd, element, listener) {
1449
            eventListenerHandler.add(element, listener);
1450
1451
            if(callOnAdd) {
1452
                listener(element);
1453
            }
1454
        }
1455
1456
        //Options object may be omitted.
1457
        if(!listener) {
1458
            listener = elements;
1459
            elements = options;
1460
            options = {};
1461
        }
1462
1463
        if(!elements) {
1464
            throw new Error("At least one element required.");
1465
        }
1466
1467
        if(!listener) {
1468
            throw new Error("Listener required.");
1469
        }
1470
1471
        if (isElement(elements)) {
1472
            // A single element has been passed in.
1473
            elements = [elements];
1474
        } else if (isCollection(elements)) {
1475
            // Convert collection to array for plugins.
1476
            // TODO: May want to check so that all the elements in the collection are valid elements.
1477
            elements = toArray(elements);
1478
        } else {
1479
            return reporter.error("Invalid arguments. Must be a DOM element or a collection of DOM elements.");
1480
        }
1481
1482
        var elementsReady = 0;
1483
1484
        var callOnAdd = getOption(options, "callOnAdd", globalOptions.callOnAdd);
1485
        var onReadyCallback = getOption(options, "onReady", function noop() {});
1486
        var debug = getOption(options, "debug", globalOptions.debug);
1487
1488
        forEach(elements, function attachListenerToElement(element) {
1489
            if (!stateHandler.getState(element)) {
1490
                stateHandler.initState(element);
1491
                idHandler.set(element);
1492
            }
1493
1494
            var id = idHandler.get(element);
1495
1496
            debug && reporter.log("Attaching listener to element", id, element);
1497
1498
            if(!elementUtils.isDetectable(element)) {
1499
                debug && reporter.log(id, "Not detectable.");
1500
                if(elementUtils.isBusy(element)) {
1501
                    debug && reporter.log(id, "System busy making it detectable");
1502
1503
                    //The element is being prepared to be detectable. Do not make it detectable.
1504
                    //Just add the listener, because the element will soon be detectable.
1505
                    addListener(callOnAdd, element, listener);
1506
                    onReadyCallbacks[id] = onReadyCallbacks[id] || [];
1507
                    onReadyCallbacks[id].push(function onReady() {
1508
                        elementsReady++;
1509
1510
                        if(elementsReady === elements.length) {
1511
                            onReadyCallback();
1512
                        }
1513
                    });
1514
                    return;
1515
                }
1516
1517
                debug && reporter.log(id, "Making detectable...");
1518
                //The element is not prepared to be detectable, so do prepare it and add a listener to it.
1519
                elementUtils.markBusy(element, true);
1520
                return detectionStrategy.makeDetectable({ debug: debug }, element, function onElementDetectable(element) {
1521
                    debug && reporter.log(id, "onElementDetectable");
1522
1523
                    if (stateHandler.getState(element)) {
1524
                        elementUtils.markAsDetectable(element);
1525
                        elementUtils.markBusy(element, false);
1526
                        detectionStrategy.addListener(element, onResizeCallback);
1527
                        addListener(callOnAdd, element, listener);
1528
1529
                        // Since the element size might have changed since the call to "listenTo", we need to check for this change,
1530
                        // so that a resize event may be emitted.
1531
                        // Having the startSize object is optional (since it does not make sense in some cases such as unrendered elements), so check for its existance before.
1532
                        // Also, check the state existance before since the element may have been uninstalled in the installation process.
1533
                        var state = stateHandler.getState(element);
1534
                        if (state && state.startSize) {
1535
                            var width = element.offsetWidth;
1536
                            var height = element.offsetHeight;
1537
                            if (state.startSize.width !== width || state.startSize.height !== height) {
1538
                                onResizeCallback(element);
1539
                            }
1540
                        }
1541
1542
                        if(onReadyCallbacks[id]) {
1543
                            forEach(onReadyCallbacks[id], function(callback) {
1544
                                callback();
1545
                            });
1546
                        }
1547
                    } else {
1548
                        // The element has been unisntalled before being detectable.
1549
                        debug && reporter.log(id, "Element uninstalled before being detectable.");
1550
                    }
1551
1552
                    delete onReadyCallbacks[id];
1553
1554
                    elementsReady++;
1555
                    if(elementsReady === elements.length) {
1556
                        onReadyCallback();
1557
                    }
1558
                });
1559
            }
1560
1561
            debug && reporter.log(id, "Already detecable, adding listener.");
1562
1563
            //The element has been prepared to be detectable and is ready to be listened to.
1564
            addListener(callOnAdd, element, listener);
1565
            elementsReady++;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1566
        });
1567
1568
        if(elementsReady === elements.length) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if elementsReady === elements.length is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
1569
            onReadyCallback();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1570
        }
1571
    }
1572
1573
    function uninstall(elements) {
1574
        if(!elements) {
1575
            return reporter.error("At least one element is required.");
1576
        }
1577
1578
        if (isElement(elements)) {
1579
            // A single element has been passed in.
1580
            elements = [elements];
1581
        } else if (isCollection(elements)) {
1582
            // Convert collection to array for plugins.
1583
            // TODO: May want to check so that all the elements in the collection are valid elements.
1584
            elements = toArray(elements);
1585
        } else {
1586
            return reporter.error("Invalid arguments. Must be a DOM element or a collection of DOM elements.");
1587
        }
1588
1589
        forEach(elements, function (element) {
1590
            eventListenerHandler.removeAllListeners(element);
1591
            detectionStrategy.uninstall(element);
1592
            stateHandler.cleanState(element);
1593
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1594
    }
1595
1596
    return {
1597
        listenTo: listenTo,
1598
        removeListener: eventListenerHandler.removeListener,
1599
        removeAllListeners: eventListenerHandler.removeAllListeners,
1600
        uninstall: uninstall
1601
    };
1602
};
1603
1604
function getOption(options, name, defaultValue) {
1605
    var value = options[name];
1606
1607
    if((value === undefined || value === null) && defaultValue !== undefined) {
1608
        return defaultValue;
1609
    }
1610
1611
    return value;
1612
}
1613
1614
},{"./browser-detector":4,"./collection-utils":5,"./detection-strategy/object.js":6,"./detection-strategy/scroll.js":7,"./element-utils":9,"./id-generator":10,"./id-handler":11,"./listener-handler":12,"./reporter":13,"./state-handler":14,"batch-processor":2}],9:[function(require,module,exports){
1615
"use strict";
1616
1617
module.exports = function(options) {
1618
    var getState = options.stateHandler.getState;
1619
1620
    /**
1621
     * Tells if the element has been made detectable and ready to be listened for resize events.
1622
     * @public
1623
     * @param {element} The element to check.
0 ignored issues
show
Documentation introduced by
The parameter The does not exist. Did you maybe forget to remove this comment?
Loading history...
1624
     * @returns {boolean} True or false depending on if the element is detectable or not.
1625
     */
1626
    function isDetectable(element) {
1627
        var state = getState(element);
1628
        return state && !!state.isDetectable;
1629
    }
1630
1631
    /**
1632
     * Marks the element that it has been made detectable and ready to be listened for resize events.
1633
     * @public
1634
     * @param {element} The element to mark.
0 ignored issues
show
Documentation introduced by
The parameter The does not exist. Did you maybe forget to remove this comment?
Loading history...
1635
     */
1636
    function markAsDetectable(element) {
1637
        getState(element).isDetectable = true;
1638
    }
1639
1640
    /**
1641
     * Tells if the element is busy or not.
1642
     * @public
1643
     * @param {element} The element to check.
0 ignored issues
show
Documentation introduced by
The parameter The does not exist. Did you maybe forget to remove this comment?
Loading history...
1644
     * @returns {boolean} True or false depending on if the element is busy or not.
1645
     */
1646
    function isBusy(element) {
1647
        return !!getState(element).busy;
1648
    }
1649
1650
    /**
1651
     * Marks the object is busy and should not be made detectable.
1652
     * @public
1653
     * @param {element} element The element to mark.
1654
     * @param {boolean} busy If the element is busy or not.
1655
     */
1656
    function markBusy(element, busy) {
1657
        getState(element).busy = !!busy;
1658
    }
1659
1660
    return {
1661
        isDetectable: isDetectable,
1662
        markAsDetectable: markAsDetectable,
1663
        isBusy: isBusy,
1664
        markBusy: markBusy
1665
    };
1666
};
1667
1668
},{}],10:[function(require,module,exports){
1669
"use strict";
1670
1671
module.exports = function() {
1672
    var idCount = 1;
1673
1674
    /**
1675
     * Generates a new unique id in the context.
1676
     * @public
1677
     * @returns {number} A unique id in the context.
1678
     */
1679
    function generate() {
1680
        return idCount++;
1681
    }
1682
1683
    return {
1684
        generate: generate
1685
    };
1686
};
1687
1688
},{}],11:[function(require,module,exports){
1689
"use strict";
1690
1691
module.exports = function(options) {
1692
    var idGenerator     = options.idGenerator;
1693
    var getState        = options.stateHandler.getState;
1694
1695
    /**
1696
     * Gets the resize detector id of the element.
1697
     * @public
1698
     * @param {element} element The target element to get the id of.
1699
     * @returns {string|number|null} The id of the element. Null if it has no id.
1700
     */
1701
    function getId(element) {
1702
        var state = getState(element);
1703
1704
        if (state && state.id !== undefined) {
1705
            return state.id;
1706
        }
1707
1708
        return null;
1709
    }
1710
1711
    /**
1712
     * Sets the resize detector id of the element. Requires the element to have a resize detector state initialized.
1713
     * @public
1714
     * @param {element} element The target element to set the id of.
1715
     * @returns {string|number|null} The id of the element.
1716
     */
1717
    function setId(element) {
1718
        var state = getState(element);
1719
1720
        if (!state) {
1721
            throw new Error("setId required the element to have a resize detection state.");
1722
        }
1723
1724
        var id = idGenerator.generate();
1725
1726
        state.id = id;
1727
1728
        return id;
1729
    }
1730
1731
    return {
1732
        get: getId,
1733
        set: setId
1734
    };
1735
};
1736
1737
},{}],12:[function(require,module,exports){
1738
"use strict";
1739
1740
module.exports = function(idHandler) {
1741
    var eventListeners = {};
1742
1743
    /**
1744
     * Gets all listeners for the given element.
1745
     * @public
1746
     * @param {element} element The element to get all listeners for.
1747
     * @returns All listeners for the given element.
1748
     */
1749
    function getListeners(element) {
1750
        var id = idHandler.get(element);
1751
1752
        if (id === undefined) {
1753
            return [];
1754
        }
1755
1756
        return eventListeners[id] || [];
1757
    }
1758
1759
    /**
1760
     * Stores the given listener for the given element. Will not actually add the listener to the element.
1761
     * @public
1762
     * @param {element} element The element that should have the listener added.
1763
     * @param {function} listener The callback that the element has added.
1764
     */
1765
    function addListener(element, listener) {
1766
        var id = idHandler.get(element);
1767
1768
        if(!eventListeners[id]) {
1769
            eventListeners[id] = [];
1770
        }
1771
1772
        eventListeners[id].push(listener);
1773
    }
1774
1775
    function removeListener(element, listener) {
1776
        var listeners = getListeners(element);
1777
        for (var i = 0, len = listeners.length; i < len; ++i) {
1778
            if (listeners[i] === listener) {
1779
              listeners.splice(i, 1);
1780
              break;
1781
            }
1782
        }
1783
    }
1784
1785
    function removeAllListeners(element) {
1786
      var listeners = getListeners(element);
1787
      if (!listeners) { return; }
1788
      listeners.length = 0;
1789
    }
1790
1791
    return {
1792
        get: getListeners,
1793
        add: addListener,
1794
        removeListener: removeListener,
1795
        removeAllListeners: removeAllListeners
1796
    };
1797
};
1798
1799
},{}],13:[function(require,module,exports){
1800
"use strict";
1801
1802
/* global console: false */
1803
1804
/**
1805
 * Reporter that handles the reporting of logs, warnings and errors.
1806
 * @public
1807
 * @param {boolean} quiet Tells if the reporter should be quiet or not.
1808
 */
1809
module.exports = function(quiet) {
1810
    function noop() {
1811
        //Does nothing.
1812
    }
1813
1814
    var reporter = {
1815
        log: noop,
1816
        warn: noop,
1817
        error: noop
1818
    };
1819
1820
    if(!quiet && window.console) {
1821
        var attachFunction = function(reporter, name) {
1822
            //The proxy is needed to be able to call the method with the console context,
1823
            //since we cannot use bind.
1824
            reporter[name] = function reporterProxy() {
1825
                var f = console[name];
1826
                if (f.apply) { //IE9 does not support console.log.apply :)
1827
                    f.apply(console, arguments);
1828
                } else {
1829
                    for (var i = 0; i < arguments.length; i++) {
1830
                        f(arguments[i]);
1831
                    }
1832
                }
1833
            };
1834
        };
1835
1836
        attachFunction(reporter, "log");
1837
        attachFunction(reporter, "warn");
1838
        attachFunction(reporter, "error");
1839
    }
1840
1841
    return reporter;
1842
};
1843
},{}],14:[function(require,module,exports){
1844
"use strict";
1845
1846
var prop = "_erd";
1847
1848
function initState(element) {
1849
    element[prop] = {};
1850
    return getState(element);
1851
}
1852
1853
function getState(element) {
1854
    return element[prop];
1855
}
1856
1857
function cleanState(element) {
1858
    delete element[prop];
1859
}
1860
1861
module.exports = {
1862
    initState: initState,
1863
    getState: getState,
1864
    cleanState: cleanState
1865
};
1866
1867
},{}]},{},[1]);
1868