Passed
Push — master ( 445199...b3e6af )
by Davide
03:36
created

jquery.stellar.js ➔ ... ➔ this.$element.each   D

Complexity

Conditions 13
Paths 2049

Size

Total Lines 83
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 13
eloc 60
nc 2049
nop 1
dl 0
loc 83
rs 4.2
c 1
b 1
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like jquery.stellar.js ➔ ... ➔ this.$element.each often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * Stellar.js v0.6.5
3
 * http://markdalgleish.com/projects/stellar.js
4
 *
5
 * Copyright 2018, Mark Dalgleish
6
 * This content is released under the MIT license
7
 * http://markdalgleish.mit-license.org
8
 */
9
10
(function(factory) {
11
    if (typeof define === "function" && define.amd) {
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ 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...
12
        define(['jquery'], function($) {
13
            return factory($);
14
        });
15
    } else if (typeof module === "object" && typeof module.exports === "object") {
16
        exports = factory(require('jquery'));
17
    } else {
18
        factory(jQuery);
19
    }
20
})(function($) {
21
22
    var pluginName = 'stellar',
23
        defaults = {
24
            scrollProperty: 'scroll',
25
            positionProperty: 'position',
26
            horizontalScrolling: true,
27
            verticalScrolling: true,
28
            horizontalOffset: 0,
29
            verticalOffset: 0,
30
            responsive: false,
31
            parallaxBackgrounds: true,
32
            parallaxElements: true,
33
            hideDistantElements: true,
34
            hideElement: function($elem) { $elem.hide(); },
35
            showElement: function($elem) { $elem.show(); }
36
        },
37
38
        scrollProperty = {
39
            scroll: {
40
                getLeft: function($elem) { return $elem.scrollLeft(); },
41
                setLeft: function($elem, val) { $elem.scrollLeft(val); },
42
43
                getTop: function($elem) { return $elem.scrollTop(); },
44
                setTop: function($elem, val) { $elem.scrollTop(val); }
45
            },
46
            position: {
47
                getLeft: function($elem) { return parseInt($elem.css('left'), 10) * -1; },
48
                getTop: function($elem) { return parseInt($elem.css('top'), 10) * -1; }
49
            },
50
            margin: {
51
                getLeft: function($elem) { return parseInt($elem.css('margin-left'), 10) * -1; },
52
                getTop: function($elem) { return parseInt($elem.css('margin-top'), 10) * -1; }
53
            },
54
            transform: {
55
                getLeft: function($elem) {
56
                    var computedTransform = getComputedStyle($elem[0])[prefixedTransform];
57
                    return (computedTransform !== 'none' ? parseInt(computedTransform.match(/(-?[0-9]+)/g)[4], 10) * -1 : 0);
58
                },
59
                getTop: function($elem) {
60
                    var computedTransform = getComputedStyle($elem[0])[prefixedTransform];
61
                    return (computedTransform !== 'none' ? parseInt(computedTransform.match(/(-?[0-9]+)/g)[5], 10) * -1 : 0);
62
                }
63
            }
64
        },
65
66
        positionProperty = {
67
            position: {
68
                setLeft: function($elem, left) { $elem.css('left', left); },
69
                setTop: function($elem, top) { $elem.css('top', top); }
70
            },
71
            transform: {
72
                setPosition: function($elem, left, startingLeft, top, startingTop) {
73
                    $elem[0].style[prefixedTransform] = 'translate3d(' + (left - startingLeft) + 'px, ' + (top - startingTop) + 'px, 0)';
74
                }
75
            }
76
        },
77
78
        // Returns a function which adds a vendor prefix to any CSS property name
79
        vendorPrefix = (function() {
80
            var prefixes = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/,
81
                style = $('script')[0].style,
82
                prefix = '',
83
                prop;
84
85
            for (prop in style) {
86
                if (prefixes.test(prop)) {
87
                    prefix = prop.match(prefixes)[0];
88
                    break;
89
                }
90
            }
91
92
            if ('WebkitOpacity' in style) { prefix = 'Webkit'; }
93
            if ('KhtmlOpacity' in style) { prefix = 'Khtml'; }
94
95
            return function(property) {
96
                return prefix + (prefix.length > 0 ? property.charAt(0).toUpperCase() + property.slice(1) : property);
97
            };
98
        }()),
99
100
        prefixedTransform = vendorPrefix('transform'),
101
102
        supportsBackgroundPositionXY = $('<div />', { style: 'background:#fff' }).css('background-position-x') !== undefined,
103
104
        setBackgroundPosition = (supportsBackgroundPositionXY ?
105
            function($elem, x, y) {
106
                $elem.css({
107
                    'background-position-x': x,
108
                    'background-position-y': y
109
                });
110
            } :
111
            function($elem, x, y) {
112
                $elem.css('background-position', x + ' ' + y);
113
            }
114
        ),
115
116
        getBackgroundPosition = (supportsBackgroundPositionXY ?
117
            function($elem) {
118
                return [
119
                    $elem.css('background-position-x'),
120
                    $elem.css('background-position-y')
121
                ];
122
            } :
123
            function($elem) {
124
                return $elem.css('background-position').split(' ');
125
            }
126
        ),
127
128
        requestAnimFrame = (
129
            window.requestAnimationFrame ||
130
            window.webkitRequestAnimationFrame ||
131
            window.mozRequestAnimationFrame ||
132
            window.oRequestAnimationFrame ||
133
            window.msRequestAnimationFrame ||
134
            function(callback) {
135
                setTimeout(callback, 1000 / 60);
136
            }
137
        );
138
139
    function Plugin(element, options) {
140
        this.element = element;
141
        this.options = $.extend({}, defaults, options);
142
143
        this._defaults = defaults;
144
        this._name = pluginName;
145
146
        this.init();
147
    }
148
149
    Plugin.prototype = {
150
        init: function() {
151
            this.options.name = pluginName + '_' + Math.floor(Math.random() * 1e9);
152
153
            this._defineElements();
154
            this._defineGetters();
155
            this._defineSetters();
156
            this._handleWindowLoadAndResize();
157
            this._detectViewport();
158
159
            this.refresh({ firstLoad: true });
160
161
            if (this.options.scrollProperty === 'scroll') {
162
                this._handleScrollEvent();
163
            } else {
164
                this._startAnimationLoop();
165
            }
166
        },
167
        _defineElements: function() {
168
            if (this.element === document.body) this.element = window;
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...
169
            this.$scrollElement = $(this.element);
170
            this.$element = (this.element === window ? $('body') : this.$scrollElement);
171
            this.$viewportElement = (this.options.viewportElement !== undefined ? $(this.options.viewportElement) : (this.$scrollElement[0] === window || this.options.scrollProperty === 'scroll' ? this.$scrollElement : this.$scrollElement.parent()));
172
        },
173
        _defineGetters: function() {
174
            var self = this,
175
                scrollPropertyAdapter = scrollProperty[self.options.scrollProperty];
176
177
            this._getScrollLeft = function() {
178
                return scrollPropertyAdapter.getLeft(self.$scrollElement);
179
            };
180
181
            this._getScrollTop = function() {
182
                return scrollPropertyAdapter.getTop(self.$scrollElement);
183
            };
184
        },
185
        _defineSetters: function() {
186
            var self = this,
187
                scrollPropertyAdapter = scrollProperty[self.options.scrollProperty],
188
                positionPropertyAdapter = positionProperty[self.options.positionProperty],
189
                setScrollLeft = scrollPropertyAdapter.setLeft,
190
                setScrollTop = scrollPropertyAdapter.setTop;
191
192
            this._setScrollLeft = (typeof setScrollLeft === 'function' ? function(val) {
193
                setScrollLeft(self.$scrollElement, val);
194
            } : $.noop);
195
196
            this._setScrollTop = (typeof setScrollTop === 'function' ? function(val) {
197
                setScrollTop(self.$scrollElement, val);
198
            } : $.noop);
199
200
            this._setPosition = positionPropertyAdapter.setPosition ||
201
                function($elem, left, startingLeft, top, startingTop) {
202
                    if (self.options.horizontalScrolling) {
203
                        positionPropertyAdapter.setLeft($elem, left, startingLeft);
204
                    }
205
206
                    if (self.options.verticalScrolling) {
207
                        positionPropertyAdapter.setTop($elem, top, startingTop);
208
                    }
209
                };
210
        },
211
        _handleWindowLoadAndResize: function() {
212
            var self = this,
213
                $window = $(window);
214
215
            if (self.options.responsive) {
216
                $window.bind('load.' + this.name, function() {
217
                    self.refresh();
218
                });
219
            }
220
221
            $window.bind('resize.' + this.name, function() {
222
                self._detectViewport();
223
224
                if (self.options.responsive) {
225
                    self.refresh();
226
                }
227
            });
228
        },
229
        refresh: function(options) {
230
            var self = this,
231
                oldLeft = self._getScrollLeft(),
232
                oldTop = self._getScrollTop();
233
234
            if (!options || !options.firstLoad) {
235
                this._reset();
236
            }
237
238
            this._setScrollLeft(0);
239
            this._setScrollTop(0);
240
241
            this._setOffsets();
242
            this._findParticles();
243
            this._findBackgrounds();
244
245
            // Fix for WebKit background rendering bug
246
            if (options && options.firstLoad && /WebKit/.test(navigator.userAgent)) {
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...
247
                $(window).on('load', function() {
248
                    var oldLeft = self._getScrollLeft(),
249
                        oldTop = self._getScrollTop();
250
251
                    self._setScrollLeft(oldLeft + 1);
252
                    self._setScrollTop(oldTop + 1);
253
254
                    self._setScrollLeft(oldLeft);
255
                    self._setScrollTop(oldTop);
256
                });
257
            }
258
259
            this._setScrollLeft(oldLeft);
260
            this._setScrollTop(oldTop);
261
        },
262
        _detectViewport: function() {
263
            var viewportOffsets = this.$viewportElement[0] !== window ? this.$viewportElement.offset() : { top: 0, left: 0 },
264
                hasOffsets = viewportOffsets !== null && viewportOffsets !== undefined;
265
266
            this.viewportWidth = this.$viewportElement.width();
267
            this.viewportHeight = this.$viewportElement.height();
268
269
            this.viewportOffsetTop = (hasOffsets ? viewportOffsets.top : 0);
270
            this.viewportOffsetLeft = (hasOffsets ? viewportOffsets.left : 0);
271
        },
272
        _findParticles: function() {
273
            var self = this,
274
                scrollLeft = this._getScrollLeft(),
0 ignored issues
show
Unused Code introduced by
The variable scrollLeft seems to be never used. Consider removing it.
Loading history...
275
                scrollTop = this._getScrollTop();
0 ignored issues
show
Unused Code introduced by
The variable scrollTop seems to be never used. Consider removing it.
Loading history...
276
277
            if (this.particles !== undefined) {
278
                for (var i = this.particles.length - 1; i >= 0; i--) {
279
                    this.particles[i].$element.data('stellar-elementIsActive', undefined);
280
                }
281
            }
282
283
            this.particles = [];
284
285
            if (!this.options.parallaxElements) 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...
286
287
            this.$element.find('[data-stellar-ratio]').each(function(i) {
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...
288
                var $this = $(this),
289
                    horizontalOffset,
290
                    verticalOffset,
291
                    positionLeft,
292
                    positionTop,
293
                    marginLeft,
294
                    marginTop,
295
                    $offsetParent,
296
                    offsetLeft,
297
                    offsetTop,
298
                    parentOffsetLeft = 0,
299
                    parentOffsetTop = 0,
300
                    tempParentOffsetLeft = 0,
301
                    tempParentOffsetTop = 0;
302
303
                // Ensure this element isn't already part of another scrolling element
304
                if (!$this.data('stellar-elementIsActive')) {
305
                    $this.data('stellar-elementIsActive', this);
306
                } else if ($this.data('stellar-elementIsActive') !== this) {
307
                    return;
308
                }
309
310
                self.options.showElement($this);
311
312
                // Save/restore the original top and left CSS values in case we refresh the particles or destroy the instance
313
                if (!$this.data('stellar-startingLeft')) {
314
                    $this.data('stellar-startingLeft', $this.css('left'));
315
                    $this.data('stellar-startingTop', $this.css('top'));
316
                } else {
317
                    $this.css('left', $this.data('stellar-startingLeft'));
318
                    $this.css('top', $this.data('stellar-startingTop'));
319
                }
320
321
                positionLeft = $this.position().left;
322
                positionTop = $this.position().top;
323
324
                // Catch-all for margin top/left properties (these evaluate to 'auto' in IE7 and IE8)
325
                marginLeft = ($this.css('margin-left') === 'auto') ? 0 : parseInt($this.css('margin-left'), 10);
326
                marginTop = ($this.css('margin-top') === 'auto') ? 0 : parseInt($this.css('margin-top'), 10);
327
328
                offsetLeft = $this.offset().left - marginLeft;
329
                offsetTop = $this.offset().top - marginTop;
330
331
                // Calculate the offset parent
332
                $this.parents().each(function() {
333
                    var $this = $(this);
334
335
                    if ($this.data('stellar-offset-parent') === true) {
336
                        parentOffsetLeft = tempParentOffsetLeft;
337
                        parentOffsetTop = tempParentOffsetTop;
338
                        $offsetParent = $this;
339
340
                        return false;
341
                    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
342
                        tempParentOffsetLeft += $this.position().left;
343
                        tempParentOffsetTop += $this.position().top;
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...
344
                    }
345
                });
346
347
                // Detect the offsets
348
                horizontalOffset = ($this.data('stellar-horizontal-offset') !== undefined ? $this.data('stellar-horizontal-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-horizontal-offset') !== undefined ? $offsetParent.data('stellar-horizontal-offset') : self.horizontalOffset));
349
                verticalOffset = ($this.data('stellar-vertical-offset') !== undefined ? $this.data('stellar-vertical-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-vertical-offset') !== undefined ? $offsetParent.data('stellar-vertical-offset') : self.verticalOffset));
350
351
                // Add our object to the particles collection
352
                self.particles.push({
353
                    $element: $this,
354
                    $offsetParent: $offsetParent,
355
                    isFixed: $this.css('position') === 'fixed',
356
                    horizontalOffset: horizontalOffset,
357
                    verticalOffset: verticalOffset,
358
                    startingPositionLeft: positionLeft,
359
                    startingPositionTop: positionTop,
360
                    startingOffsetLeft: offsetLeft,
361
                    startingOffsetTop: offsetTop,
362
                    parentOffsetLeft: parentOffsetLeft,
363
                    parentOffsetTop: parentOffsetTop,
364
                    stellarRatio: ($this.data('stellar-ratio') !== undefined ? $this.data('stellar-ratio') : 1),
365
                    width: $this.outerWidth(true),
366
                    height: $this.outerHeight(true),
367
                    isHidden: false
368
                });
369
            });
370
        },
371
        _findBackgrounds: function() {
372
            var self = this,
373
                scrollLeft = this._getScrollLeft(),
374
                scrollTop = this._getScrollTop(),
375
                $backgroundElements;
376
377
            this.backgrounds = [];
378
379
            if (!this.options.parallaxBackgrounds) 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...
380
381
            $backgroundElements = this.$element.find('[data-stellar-background-ratio]');
382
383
            if (this.$element.data('stellar-background-ratio')) {
384
                $backgroundElements = $backgroundElements.add(this.$element);
385
            }
386
387
            $backgroundElements.each(function() {
388
                var $this = $(this),
389
                    backgroundPosition = getBackgroundPosition($this),
390
                    horizontalOffset,
391
                    verticalOffset,
392
                    positionLeft,
0 ignored issues
show
Unused Code introduced by
The variable positionLeft seems to be never used. Consider removing it.
Loading history...
393
                    positionTop,
0 ignored issues
show
Unused Code introduced by
The variable positionTop seems to be never used. Consider removing it.
Loading history...
394
                    marginLeft,
395
                    marginTop,
396
                    offsetLeft,
397
                    offsetTop,
398
                    $offsetParent,
399
                    parentOffsetLeft = 0,
400
                    parentOffsetTop = 0,
401
                    tempParentOffsetLeft = 0,
402
                    tempParentOffsetTop = 0;
403
404
                // Ensure this element isn't already part of another scrolling element
405
                if (!$this.data('stellar-backgroundIsActive')) {
406
                    $this.data('stellar-backgroundIsActive', this);
407
                } else if ($this.data('stellar-backgroundIsActive') !== this) {
408
                    return;
409
                }
410
411
                // Save/restore the original top and left CSS values in case we destroy the instance
412
                if (!$this.data('stellar-backgroundStartingLeft')) {
413
                    $this.data('stellar-backgroundStartingLeft', backgroundPosition[0]);
414
                    $this.data('stellar-backgroundStartingTop', backgroundPosition[1]);
415
                } else {
416
                    setBackgroundPosition($this, $this.data('stellar-backgroundStartingLeft'), $this.data('stellar-backgroundStartingTop'));
417
                }
418
419
                // Catch-all for margin top/left properties (these evaluate to 'auto' in IE7 and IE8)
420
                marginLeft = ($this.css('margin-left') === 'auto') ? 0 : parseInt($this.css('margin-left'), 10);
421
                marginTop = ($this.css('margin-top') === 'auto') ? 0 : parseInt($this.css('margin-top'), 10);
422
423
                offsetLeft = $this.offset().left - marginLeft - scrollLeft;
424
                offsetTop = $this.offset().top - marginTop - scrollTop;
425
426
                // Calculate the offset parent
427
                $this.parents().each(function() {
428
                    var $this = $(this);
429
430
                    if ($this.data('stellar-offset-parent') === true) {
431
                        parentOffsetLeft = tempParentOffsetLeft;
432
                        parentOffsetTop = tempParentOffsetTop;
433
                        $offsetParent = $this;
434
435
                        return false;
436
                    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
437
                        tempParentOffsetLeft += $this.position().left;
438
                        tempParentOffsetTop += $this.position().top;
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...
439
                    }
440
                });
441
442
                // Detect the offsets
443
                horizontalOffset = ($this.data('stellar-horizontal-offset') !== undefined ? $this.data('stellar-horizontal-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-horizontal-offset') !== undefined ? $offsetParent.data('stellar-horizontal-offset') : self.horizontalOffset));
444
                verticalOffset = ($this.data('stellar-vertical-offset') !== undefined ? $this.data('stellar-vertical-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-vertical-offset') !== undefined ? $offsetParent.data('stellar-vertical-offset') : self.verticalOffset));
445
446
                self.backgrounds.push({
447
                    $element: $this,
448
                    $offsetParent: $offsetParent,
449
                    isFixed: $this.css('background-attachment') === 'fixed',
450
                    horizontalOffset: horizontalOffset,
451
                    verticalOffset: verticalOffset,
452
                    startingValueLeft: backgroundPosition[0],
453
                    startingValueTop: backgroundPosition[1],
454
                    startingBackgroundPositionLeft: (isNaN(parseInt(backgroundPosition[0], 10)) ? 0 : parseInt(backgroundPosition[0], 10)),
455
                    startingBackgroundPositionTop: (isNaN(parseInt(backgroundPosition[1], 10)) ? 0 : parseInt(backgroundPosition[1], 10)),
456
                    startingPositionLeft: $this.position().left,
457
                    startingPositionTop: $this.position().top,
458
                    startingOffsetLeft: offsetLeft,
459
                    startingOffsetTop: offsetTop,
460
                    parentOffsetLeft: parentOffsetLeft,
461
                    parentOffsetTop: parentOffsetTop,
462
                    stellarRatio: ($this.data('stellar-background-ratio') === undefined ? 1 : $this.data('stellar-background-ratio'))
463
                });
464
            });
465
        },
466
        _reset: function() {
467
            var particle,
468
                startingPositionLeft,
469
                startingPositionTop,
470
                background,
471
                i;
472
473
            for (i = this.particles.length - 1; i >= 0; i--) {
474
                particle = this.particles[i];
475
                startingPositionLeft = particle.$element.data('stellar-startingLeft');
476
                startingPositionTop = particle.$element.data('stellar-startingTop');
477
478
                this._setPosition(particle.$element, startingPositionLeft, startingPositionLeft, startingPositionTop, startingPositionTop);
479
480
                this.options.showElement(particle.$element);
481
482
                particle.$element.data('stellar-startingLeft', null).data('stellar-elementIsActive', null).data('stellar-backgroundIsActive', null);
483
            }
484
485
            for (i = this.backgrounds.length - 1; i >= 0; i--) {
486
                background = this.backgrounds[i];
487
488
                background.$element.data('stellar-backgroundStartingLeft', null).data('stellar-backgroundStartingTop', null);
489
490
                setBackgroundPosition(background.$element, background.startingValueLeft, background.startingValueTop);
491
            }
492
        },
493
        destroy: function() {
494
            this._reset();
495
496
            this.$scrollElement.unbind('resize.' + this.name).unbind('scroll.' + this.name);
497
            this._animationLoop = $.noop;
498
499
            $(window).unbind('load.' + this.name).unbind('resize.' + this.name);
500
        },
501
        _setOffsets: function() {
502
            var self = this,
503
                $window = $(window);
504
505
            $window.unbind('resize.horizontal-' + this.name).unbind('resize.vertical-' + this.name);
506
507
            if (typeof this.options.horizontalOffset === 'function') {
508
                this.horizontalOffset = this.options.horizontalOffset();
509
                $window.bind('resize.horizontal-' + this.name, function() {
510
                    self.horizontalOffset = self.options.horizontalOffset();
511
                });
512
            } else {
513
                this.horizontalOffset = this.options.horizontalOffset;
514
            }
515
516
            if (typeof this.options.verticalOffset === 'function') {
517
                this.verticalOffset = this.options.verticalOffset();
518
                $window.bind('resize.vertical-' + this.name, function() {
519
                    self.verticalOffset = self.options.verticalOffset();
520
                });
521
            } else {
522
                this.verticalOffset = this.options.verticalOffset;
523
            }
524
        },
525
        _repositionElements: function() {
526
            var scrollLeft = this._getScrollLeft(),
527
                scrollTop = this._getScrollTop(),
528
                horizontalOffset,
529
                verticalOffset,
530
                particle,
531
                fixedRatioOffset,
532
                background,
533
                bgLeft,
534
                bgTop,
535
                isVisibleVertical = true,
536
                isVisibleHorizontal = true,
537
                newPositionLeft,
538
                newPositionTop,
539
                newOffsetLeft,
540
                newOffsetTop,
541
                i;
542
543
            // First check that the scroll position or container size has changed
544
            if (this.currentScrollLeft === scrollLeft && this.currentScrollTop === scrollTop && this.currentWidth === this.viewportWidth && this.currentHeight === this.viewportHeight) {
545
                return;
546
            } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
547
                this.currentScrollLeft = scrollLeft;
548
                this.currentScrollTop = scrollTop;
549
                this.currentWidth = this.viewportWidth;
550
                this.currentHeight = this.viewportHeight;
551
            }
552
553
            // Reposition elements
554
            for (i = this.particles.length - 1; i >= 0; i--) {
555
                particle = this.particles[i];
556
557
                fixedRatioOffset = (particle.isFixed ? 1 : 0);
558
559
                // Calculate position, then calculate what the particle's new offset will be (for visibility check)
560
                if (this.options.horizontalScrolling) {
561
                    newPositionLeft = (scrollLeft + particle.horizontalOffset + this.viewportOffsetLeft + particle.startingPositionLeft - particle.startingOffsetLeft + particle.parentOffsetLeft) * -(particle.stellarRatio + fixedRatioOffset - 1) + particle.startingPositionLeft;
562
                    newOffsetLeft = newPositionLeft - particle.startingPositionLeft + particle.startingOffsetLeft;
563
                } else {
564
                    newPositionLeft = particle.startingPositionLeft;
565
                    newOffsetLeft = particle.startingOffsetLeft;
566
                }
567
568
                if (this.options.verticalScrolling) {
569
                    newPositionTop = (scrollTop + particle.verticalOffset + this.viewportOffsetTop + particle.startingPositionTop - particle.startingOffsetTop + particle.parentOffsetTop) * -(particle.stellarRatio + fixedRatioOffset - 1) + particle.startingPositionTop;
570
                    newOffsetTop = newPositionTop - particle.startingPositionTop + particle.startingOffsetTop;
571
                } else {
572
                    newPositionTop = particle.startingPositionTop;
573
                    newOffsetTop = particle.startingOffsetTop;
574
                }
575
576
                // Check visibility
577
                if (this.options.hideDistantElements) {
578
                    isVisibleHorizontal = !this.options.horizontalScrolling || newOffsetLeft + particle.width > (particle.isFixed ? 0 : scrollLeft) && newOffsetLeft < (particle.isFixed ? 0 : scrollLeft) + this.viewportWidth + this.viewportOffsetLeft;
579
                    isVisibleVertical = !this.options.verticalScrolling || newOffsetTop + particle.height > (particle.isFixed ? 0 : scrollTop) && newOffsetTop < (particle.isFixed ? 0 : scrollTop) + this.viewportHeight + this.viewportOffsetTop;
580
                }
581
582
                if (isVisibleHorizontal && isVisibleVertical) {
583
                    if (particle.isHidden) {
584
                        this.options.showElement(particle.$element);
585
                        particle.isHidden = false;
586
                    }
587
588
                    this._setPosition(particle.$element, newPositionLeft, particle.startingPositionLeft, newPositionTop, particle.startingPositionTop);
589
                } else {
590
                    if (!particle.isHidden) {
591
                        this.options.hideElement(particle.$element);
592
                        particle.isHidden = true;
593
                    }
594
                }
595
            }
596
597
            // Reposition backgrounds
598
            for (i = this.backgrounds.length - 1; i >= 0; i--) {
599
                background = this.backgrounds[i];
600
601
                fixedRatioOffset = (background.isFixed ? 0 : 1);
602
                bgLeft = (this.options.horizontalScrolling ? (scrollLeft + background.horizontalOffset - this.viewportOffsetLeft - background.startingOffsetLeft + background.parentOffsetLeft - background.startingBackgroundPositionLeft) * (fixedRatioOffset - background.stellarRatio) + 'px' : background.startingValueLeft);
603
                bgTop = (this.options.verticalScrolling ? (scrollTop + background.verticalOffset - this.viewportOffsetTop - background.startingOffsetTop + background.parentOffsetTop - background.startingBackgroundPositionTop) * (fixedRatioOffset - background.stellarRatio) + 'px' : background.startingValueTop);
604
605
                setBackgroundPosition(background.$element, bgLeft, bgTop);
606
            }
607
        },
608
        _handleScrollEvent: function() {
609
            var self = this,
610
                ticking = false;
611
612
            var update = function() {
613
                self._repositionElements();
614
                ticking = false;
615
            };
616
617
            var requestTick = function() {
618
                if (!ticking) {
619
                    requestAnimFrame(update);
620
                    ticking = true;
621
                }
622
            };
623
624
            this.$scrollElement.bind('scroll.' + this.name, requestTick);
625
            requestTick();
626
        },
627
        _startAnimationLoop: function() {
628
            var self = this;
629
630
            this._animationLoop = function() {
631
                requestAnimFrame(self._animationLoop);
632
                self._repositionElements();
633
            };
634
            this._animationLoop();
635
        }
636
    };
637
638
    $.fn[pluginName] = function(options) {
639
        var args = arguments;
640
        if (options === undefined || typeof options === 'object') {
641
            return this.each(function() {
642
                if (!$.data(this, 'plugin_' + pluginName)) {
643
                    $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
644
                }
645
            });
646
        } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if typeof options === "str..." && options !== "init" 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...
647
            return this.each(function() {
648
                var instance = $.data(this, 'plugin_' + pluginName);
649
                if (instance instanceof Plugin && typeof instance[options] === 'function') {
650
                    instance[options].apply(instance, Array.prototype.slice.call(args, 1));
651
                }
652
                if (options === 'destroy') {
653
                    $.data(this, 'plugin_' + pluginName, null);
654
                }
655
            });
656
        }
657
    };
658
659
    $[pluginName] = function(options) {
0 ignored issues
show
Unused Code introduced by
The parameter options 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...
660
        var $window = $(window);
661
        return $window.stellar.apply($window, Array.prototype.slice.call(arguments, 0));
662
    };
663
664
    // Expose the scroll and position property function hashes so they can be extended
665
    $[pluginName].scrollProperty = scrollProperty;
666
    $[pluginName].positionProperty = positionProperty;
667
668
    // Expose the plugin class so it can be modified
669
    window.Stellar = Plugin;
670
});