Completed
Push — master ( 5bf5a9...80b817 )
by Rain
01:36
created

vendors/jquery-nanoscroller/jquery.nanoscroller.orig.js   F

Complexity

Total Complexity 97
Complexity/F 3.34

Size

Lines of Code 748
Function Count 29

Duplication

Duplicated Lines 77
Ratio 10.29 %

Importance

Changes 0
Metric Value
cc 0
wmc 97
nc 3686400
mnd 3
bc 80
fnc 29
dl 77
loc 748
rs 2.1818
bpm 2.7586
cpm 3.3448
noi 8
c 0
b 0
f 0

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like vendors/jquery-nanoscroller/jquery.nanoscroller.orig.js 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
/*! nanoScrollerJS - v0.7
2
* http://jamesflorentino.github.com/nanoScrollerJS/
3
* Copyright (c) 2013 James Florentino; Licensed under MIT */
4
5
6
(function($, window, document) {
7
  "use strict";
8
9
  var BROWSER_IS_IE7, BROWSER_SCROLLBAR_WIDTH, DOMSCROLL, DOWN, DRAG, KEYDOWN, KEYUP, MOUSEDOWN, MOUSEMOVE, MOUSEUP, MOUSEWHEEL, NanoScroll, PANEDOWN, RESIZE, SCROLL, SCROLLBAR, TOUCHMOVE, UP, WHEEL, defaults, getBrowserScrollbarWidth;
10
  defaults = {
11
    /**
12
      a classname for the pane element.
13
      @property paneClass
14
      @type String
15
      @default 'pane'
16
    */
17
18
    paneClass: 'pane',
19
    /**
20
      a classname for the slider element.
21
      @property sliderClass
22
      @type String
23
      @default 'slider'
24
    */
25
26
    sliderClass: 'slider',
27
    /**
28
      a classname for the content element.
29
      @property contentClass
30
      @type String
31
      @default 'content'
32
    */
33
34
    contentClass: 'content',
35
    /**
36
      a setting to enable native scrolling in iOS devices.
37
      @property iOSNativeScrolling
38
      @type Boolean
39
      @default false
40
    */
41
42
    iOSNativeScrolling: false,
43
    /**
44
      a setting to prevent the rest of the page being
45
      scrolled when user scrolls the `.content` element.
46
      @property preventPageScrolling
47
      @type Boolean
48
      @default false
49
    */
50
51
    preventPageScrolling: false,
52
    /**
53
      a setting to disable binding to the resize event.
54
      @property disableResize
55
      @type Boolean
56
      @default false
57
    */
58
59
    disableResize: false,
60
    /**
61
      a setting to make the scrollbar always visible.
62
      @property alwaysVisible
63
      @type Boolean
64
      @default false
65
    */
66
67
    alwaysVisible: false,
68
    /**
69
      a default timeout for the `flash()` method.
70
      @property flashDelay
71
      @type Number
72
      @default 1500
73
    */
74
75
    flashDelay: 1500,
76
    /**
77
      a minimum height for the `.slider` element.
78
      @property sliderMinHeight
79
      @type Number
80
      @default 20
81
    */
82
83
    sliderMinHeight: 20,
84
    /**
85
      a maximum height for the `.slider` element.
86
      @property sliderMaxHeight
87
      @type Number
88
      @default null
89
    */
90
91
    sliderMaxHeight: null
92
  };
93
  /**
94
    @property SCROLLBAR
95
    @type String
96
    @static
97
    @final
98
    @private
99
  */
100
101
  SCROLLBAR = 'scrollbar';
102
  /**
103
    @property SCROLL
104
    @type String
105
    @static
106
    @final
107
    @private
108
  */
109
110
  SCROLL = 'scroll';
111
  /**
112
    @property MOUSEDOWN
113
    @type String
114
    @final
115
    @private
116
  */
117
118
  MOUSEDOWN = 'mousedown';
119
  /**
120
    @property MOUSEMOVE
121
    @type String
122
    @static
123
    @final
124
    @private
125
  */
126
127
  MOUSEMOVE = 'mousemove';
128
  /**
129
    @property MOUSEWHEEL
130
    @type String
131
    @final
132
    @private
133
  */
134
135
  MOUSEWHEEL = 'mousewheel';
136
  /**
137
    @property MOUSEUP
138
    @type String
139
    @static
140
    @final
141
    @private
142
  */
143
144
  MOUSEUP = 'mouseup';
145
  /**
146
    @property RESIZE
147
    @type String
148
    @final
149
    @private
150
  */
151
152
  RESIZE = 'resize';
153
  /**
154
    @property DRAG
155
    @type String
156
    @static
157
    @final
158
    @private
159
  */
160
161
  DRAG = 'drag';
162
  /**
163
    @property UP
164
    @type String
165
    @static
166
    @final
167
    @private
168
  */
169
170
  UP = 'up';
171
  /**
172
    @property PANEDOWN
173
    @type String
174
    @static
175
    @final
176
    @private
177
  */
178
179
  PANEDOWN = 'panedown';
180
  /**
181
    @property DOMSCROLL
182
    @type String
183
    @static
184
    @final
185
    @private
186
  */
187
188
  DOMSCROLL = 'DOMMouseScroll';
189
  /**
190
    @property DOWN
191
    @type String
192
    @static
193
    @final
194
    @private
195
  */
196
197
  DOWN = 'down';
198
  /**
199
    @property WHEEL
200
    @type String
201
    @static
202
    @final
203
    @private
204
  */
205
206
  WHEEL = 'wheel';
207
  /**
208
    @property KEYDOWN
209
    @type String
210
    @static
211
    @final
212
    @private
213
  */
214
215
  KEYDOWN = 'keydown';
216
  /**
217
    @property KEYUP
218
    @type String
219
    @static
220
    @final
221
    @private
222
  */
223
224
  KEYUP = 'keyup';
225
  /**
226
    @property TOUCHMOVE
227
    @type String
228
    @static
229
    @final
230
    @private
231
  */
232
233
  TOUCHMOVE = 'touchmove';
234
  /**
235
    @property BROWSER_IS_IE7
236
    @type Boolean
237
    @static
238
    @final
239
    @private
240
  */
241
242
  BROWSER_IS_IE7 = window.navigator.appName === 'Microsoft Internet Explorer' && /msie 7./i.test(window.navigator.appVersion) && window.ActiveXObject;
243
  /**
244
    @property BROWSER_SCROLLBAR_WIDTH
245
    @type Number
246
    @static
247
    @default null
248
    @private
249
  */
250
251
  BROWSER_SCROLLBAR_WIDTH = null;
252
  /**
253
    Returns browser's native scrollbar width
254
    @method getBrowserScrollbarWidth
255
    @return {Number} the scrollbar width in pixels
256
    @static
257
    @private
258
  */
259
260
  getBrowserScrollbarWidth = function() {
261
    var outer, outerStyle, scrollbarWidth;
262
    outer = document.createElement('div');
263
    outerStyle = outer.style;
264
    outerStyle.position = 'absolute';
265
    outerStyle.width = '100px';
266
    outerStyle.height = '100px';
267
    outerStyle.overflow = SCROLL;
268
    outerStyle.top = '-9999px';
269
    document.body.appendChild(outer);
270
    scrollbarWidth = outer.offsetWidth - outer.clientWidth;
271
    document.body.removeChild(outer);
272
    return scrollbarWidth;
273
  };
274
  /**
275
    @class NanoScroll
276
    @param element {HTMLElement|Node} the main element
277
    @param options {Object} nanoScroller's options
278
    @constructor
279
  */
280
281
  NanoScroll = (function() {
282
283
    function NanoScroll(el, options) {
284
      this.el = el;
285
      this.options = options;
286
      BROWSER_SCROLLBAR_WIDTH || (BROWSER_SCROLLBAR_WIDTH = getBrowserScrollbarWidth());
287
      this.$el = $(this.el);
288
      this.doc = $(document);
289
      this.win = $(window);
290
      this.$content = this.$el.children("." + options.contentClass);
291
      this.$content.attr('tabindex', 0);
292
      this.content = this.$content[0];
293
      if (this.options.iOSNativeScrolling && (this.el.style.WebkitOverflowScrolling != null)) {
294
        this.nativeScrolling();
295
      } else {
296
        this.generate();
297
      }
298
      this.createEvents();
299
      this.addEvents();
300
      this.reset();
301
    }
302
303
    /**
304
      Prevents the rest of the page being scrolled
305
      when user scrolls the `.content` element.
306
      @method preventScrolling
307
      @param event {Event}
308
      @param direction {String} Scroll direction (up or down)
309
      @private
310
    */
311
312
313
    NanoScroll.prototype.preventScrolling = function(e, direction) {
314
      if (!this.isActive) {
315
        return;
316
      }
317
      if (e.type === DOMSCROLL) {
318
        if (direction === DOWN && e.originalEvent.detail > 0 || direction === UP && e.originalEvent.detail < 0) {
319
          e.preventDefault();
320
        }
321
      } else if (e.type === MOUSEWHEEL) {
322
        if (!e.originalEvent || !e.originalEvent.wheelDelta) {
323
          return;
324
        }
325
        if (direction === DOWN && e.originalEvent.wheelDelta < 0 || direction === UP && e.originalEvent.wheelDelta > 0) {
326
          e.preventDefault();
327
        }
328
      }
329
    };
330
331
    /**
332
      Enable iOS native scrolling
333
    */
334
335
336
    NanoScroll.prototype.nativeScrolling = function() {
337
      this.$content.css({
338
        WebkitOverflowScrolling: 'touch'
339
      });
340
      this.iOSNativeScrolling = true;
341
      this.isActive = true;
342
    };
343
344
    /**
345
      Updates those nanoScroller properties that
346
      are related to current scrollbar position.
347
      @method updateScrollValues
348
      @private
349
    */
350
351
352
    NanoScroll.prototype.updateScrollValues = function() {
353
      var content;
354
      content = this.content;
355
      this.maxScrollTop = content.scrollHeight - content.clientHeight;
356
      this.contentScrollTop = content.scrollTop;
357
      if (!this.iOSNativeScrolling) {
358
        this.maxSliderTop = this.paneHeight - this.sliderHeight;
359
        this.sliderTop = this.contentScrollTop * this.maxSliderTop / this.maxScrollTop;
360
      }
361
    };
362
363
    /**
364
      Creates event related methods
365
      @method createEvents
366
      @private
367
    */
368
369
370
    NanoScroll.prototype.createEvents = function() {
371
      var _this = this;
372
      this.events = {
373
        down: function(e) {
374
          _this.isBeingDragged = true;
375
          _this.offsetY = e.pageY - _this.slider.offset().top;
376
          _this.pane.addClass('active');
377
          _this.doc.bind(MOUSEMOVE, _this.events[DRAG]).bind(MOUSEUP, _this.events[UP]);
378
          return false;
379
        },
380
        drag: function(e) {
381
          _this.sliderY = e.pageY - _this.$el.offset().top - _this.offsetY;
382
          _this.scroll();
383
          _this.updateScrollValues();
384
          if (_this.contentScrollTop >= _this.maxScrollTop) {
385
            _this.$el.trigger('scrollend');
386
          } else if (_this.contentScrollTop === 0) {
387
            _this.$el.trigger('scrolltop');
388
          }
389
          return false;
390
        },
391
        up: function(e) {
392
          _this.isBeingDragged = false;
393
          _this.pane.removeClass('active');
394
          _this.doc.unbind(MOUSEMOVE, _this.events[DRAG]).unbind(MOUSEUP, _this.events[UP]);
395
          return false;
396
        },
397
        resize: function(e) {
398
          _this.reset();
399
        },
400
        panedown: function(e) {
401
          _this.sliderY = (e.offsetY || e.originalEvent.layerY) - (_this.sliderHeight * 0.5);
402
          _this.scroll();
403
          _this.events.down(e);
404
          return false;
405
        },
406
        scroll: function(e) {
407
          if (_this.isBeingDragged) {
408
            return;
409
          }
410
          _this.updateScrollValues();
411
          if (!_this.iOSNativeScrolling) {
412
            _this.sliderY = _this.sliderTop;
413
            _this.slider.css({
414
              top: _this.sliderTop
415
            });
416
          }
417
          if (e == null) {
418
            return;
419
          }
420
          if (_this.contentScrollTop >= _this.maxScrollTop) {
421
            if (_this.options.preventPageScrolling) {
422
              _this.preventScrolling(e, DOWN);
423
            }
424
            _this.$el.trigger('scrollend');
425
          } else if (_this.contentScrollTop === 0) {
426
            if (_this.options.preventPageScrolling) {
427
              _this.preventScrolling(e, UP);
428
            }
429
            _this.$el.trigger('scrolltop');
430
          }
431
        },
432
        wheel: function(e) {
433
          if (e == null) {
434
            return;
435
          }
436
          _this.sliderY += -e.wheelDeltaY || -e.delta;
437
          _this.scroll();
438
          return false;
439
        }
440
      };
441
    };
442
443
    /**
444
      Adds event listeners with jQuery.
445
      @method addEvents
446
      @private
447
    */
448
449
450
    NanoScroll.prototype.addEvents = function() {
451
      var events;
452
      this.removeEvents();
453
      events = this.events;
454
      if (!this.options.disableResize) {
455
        this.win.bind(RESIZE, events[RESIZE]);
456
      }
457
      if (!this.iOSNativeScrolling) {
458
        this.slider.bind(MOUSEDOWN, events[DOWN]);
459
        this.pane.bind(MOUSEDOWN, events[PANEDOWN]).bind("" + MOUSEWHEEL + " " + DOMSCROLL, events[WHEEL]);
460
      }
461
      this.$content.bind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]);
462
    };
463
464
    /**
465
      Removes event listeners with jQuery.
466
      @method removeEvents
467
      @private
468
    */
469
470
471
    NanoScroll.prototype.removeEvents = function() {
472
      var events;
473
      events = this.events;
474
      this.win.unbind(RESIZE, events[RESIZE]);
475
      if (!this.iOSNativeScrolling) {
476
        this.slider.unbind();
477
        this.pane.unbind();
478
      }
479
      this.$content.unbind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]);
480
    };
481
482
    /**
483
      Generates nanoScroller's scrollbar and elements for it.
484
      @method generate
485
      @chainable
486
      @private
487
    */
488
489
490
    NanoScroll.prototype.generate = function() {
491
      var contentClass, cssRule, options, paneClass, sliderClass;
492
      options = this.options;
493
      paneClass = options.paneClass, sliderClass = options.sliderClass, contentClass = options.contentClass;
494
      if (!this.$el.find("" + paneClass).length && !this.$el.find("" + sliderClass).length) {
495
        this.$el.append("<div class=\"" + paneClass + "\"><div class=\"" + sliderClass + "\" /></div>");
496
      }
497
      this.slider = this.$el.find("." + sliderClass);
498
      this.pane = this.$el.find("." + paneClass);
499
      if (BROWSER_SCROLLBAR_WIDTH) {
500
        cssRule = this.$el.css('direction') === 'rtl' ? {
501
           left: -BROWSER_SCROLLBAR_WIDTH
502
        } : {
503
          right: -BROWSER_SCROLLBAR_WIDTH,
504
          bottom: -BROWSER_SCROLLBAR_WIDTH
505
        };
506
        this.$el.addClass('has-scrollbar');
507
      }
508
      if (cssRule != null) {
509
        this.$content.css(cssRule);
510
      }
511
      return this;
512
    };
513
514
    /**
515
      @method restore
516
      @private
517
    */
518
519
520
    NanoScroll.prototype.restore = function() {
521
      this.stopped = false;
522
      this.pane.show();
523
      this.addEvents();
524
    };
525
526
    /**
527
      Resets nanoScroller's scrollbar.
528
      @method reset
529
      @chainable
530
      @example
531
          $(".nano").nanoScroller();
532
    */
533
534
535
    NanoScroll.prototype.reset = function() {
536
      var content, contentHeight, contentStyle, contentStyleOverflowY, paneBottom, paneHeight, paneOuterHeight, paneTop, sliderHeight;
537
      if (this.iOSNativeScrolling) {
538
        this.contentHeight = this.content.scrollHeight;
539
        return;
540
      }
541
      if (!this.$el.find("." + this.options.paneClass).length) {
542
        this.generate().stop();
543
      }
544
      if (this.stopped) {
545
        this.restore();
546
      }
547
      content = this.content;
548
      contentStyle = content.style;
549
      contentStyleOverflowY = contentStyle.overflowY;
550
      if (BROWSER_IS_IE7) {
551
        this.$content.css({
552
          height: this.$content.height()
553
        });
554
      }
555
      contentHeight = content.scrollHeight + BROWSER_SCROLLBAR_WIDTH;
556
      paneHeight = this.pane.outerHeight();
557
      paneTop = parseInt(this.pane.css('top'), 10);
558
      paneBottom = parseInt(this.pane.css('bottom'), 10);
559
      paneOuterHeight = paneHeight + paneTop + paneBottom;
560
      sliderHeight = Math.round(paneOuterHeight / contentHeight * paneOuterHeight);
561
      if (sliderHeight < this.options.sliderMinHeight) {
562
        sliderHeight = this.options.sliderMinHeight;
563
      } else if ((this.options.sliderMaxHeight != null) && sliderHeight > this.options.sliderMaxHeight) {
564
        sliderHeight = this.options.sliderMaxHeight;
565
      }
566
      if (contentStyleOverflowY === SCROLL && contentStyle.overflowX !== SCROLL) {
567
        sliderHeight += BROWSER_SCROLLBAR_WIDTH;
568
      }
569
      this.maxSliderTop = paneOuterHeight - sliderHeight;
570
      this.contentHeight = contentHeight;
571
      this.paneHeight = paneHeight;
572
      this.paneOuterHeight = paneOuterHeight;
573
      this.sliderHeight = sliderHeight;
574
      this.slider.height(sliderHeight);
575
      this.events.scroll();
576
      this.pane.show();
577
      this.isActive = true;
578
      if ((content.scrollHeight === content.clientHeight) || (this.pane.outerHeight(true) >= content.scrollHeight && contentStyleOverflowY !== SCROLL)) {
579
        this.pane.hide();
580
        this.isActive = false;
581
      } else if (this.el.clientHeight === content.scrollHeight && contentStyleOverflowY === SCROLL) {
582
        this.slider.hide();
583
      } else {
584
        this.slider.show();
585
      }
586
      this.pane.css({
587
        opacity: (this.options.alwaysVisible ? 1 : ''),
588
        visibility: (this.options.alwaysVisible ? 'visible' : '')
589
      });
590
      return this;
591
    };
592
593
    /**
594
      @method scroll
595
      @private
596
      @example
597
          $(".nano").nanoScroller({ scroll: 'top' });
598
    */
599
600
601
    NanoScroll.prototype.scroll = function() {
602
      if (!this.isActive) {
603
        return;
604
      }
605
      this.sliderY = Math.max(0, this.sliderY);
606
      this.sliderY = Math.min(this.maxSliderTop, this.sliderY);
607
      this.$content.scrollTop((this.paneHeight - this.contentHeight + BROWSER_SCROLLBAR_WIDTH) * this.sliderY / this.maxSliderTop * -1);
608
      if (!this.iOSNativeScrolling) {
609
        this.slider.css({
610
          top: this.sliderY
611
        });
612
      }
613
      return this;
614
    };
615
616
    /**
617
      Scroll at the bottom with an offset value
618
      @method scrollBottom
619
      @param offsetY {Number}
620
      @chainable
621
      @example
622
          $(".nano").nanoScroller({ scrollBottom: value });
623
    */
624
625
626
    NanoScroll.prototype.scrollBottom = function(offsetY) {
627
      if (!this.isActive) {
628
        return;
629
      }
630
      this.reset();
631
      this.$content.scrollTop(this.contentHeight - this.$content.height() - offsetY).trigger(MOUSEWHEEL);
632
      return this;
633
    };
634
635
    /**
636
      Scroll at the top with an offset value
637
      @method scrollTop
638
      @param offsetY {Number}
639
      @chainable
640
      @example
641
          $(".nano").nanoScroller({ scrollTop: value });
642
    */
643
644
645
    NanoScroll.prototype.scrollTop = function(offsetY) {
646
      if (!this.isActive) {
647
        return;
648
      }
649
      this.reset();
650
      this.$content.scrollTop(+offsetY).trigger(MOUSEWHEEL);
651
      return this;
652
    };
653
654
    /**
655
      Scroll to an element
656
      @method scrollTo
657
      @param node {Node} A node to scroll to.
658
      @chainable
659
      @example
660
          $(".nano").nanoScroller({ scrollTo: $('#a_node') });
661
    */
662
663
664
    NanoScroll.prototype.scrollTo = function(node) {
665
      if (!this.isActive) {
666
        return;
667
      }
668
      this.reset();
669
      this.scrollTop($(node).get(0).offsetTop);
670
      return this;
671
    };
672
673
    /**
674
      To stop the operation.
675
      This option will tell the plugin to disable all event bindings and hide the gadget scrollbar from the UI.
676
      @method stop
677
      @chainable
678
      @example
679
          $(".nano").nanoScroller({ stop: true });
680
    */
681
682
683
    NanoScroll.prototype.stop = function() {
684
      this.stopped = true;
685
      this.removeEvents();
686
      this.pane.hide();
687
      return this;
688
    };
689
690
    /**
691
      To flash the scrollbar gadget for an amount of time defined in plugin settings (defaults to 1,5s).
692
      Useful if you want to show the user (e.g. on pageload) that there is more content waiting for him.
693
      @method flash
694
      @chainable
695
      @example
696
          $(".nano").nanoScroller({ flash: true });
697
    */
698
699
700
    NanoScroll.prototype.flash = function() {
701
      var _this = this;
702
      if (!this.isActive) {
703
        return;
704
      }
705
      this.reset();
706
      this.pane.addClass('flashed');
707
      setTimeout(function() {
708
        _this.pane.removeClass('flashed');
709
      }, this.options.flashDelay);
710
      return this;
711
    };
712
713
    return NanoScroll;
714
715
  })();
716
  $.fn.nanoScroller = function(settings) {
717
    return this.each(function() {
718
      var options, scrollbar;
719
      if (!(scrollbar = this.nanoscroller)) {
720
        options = $.extend({}, defaults, settings);
721
        this.nanoscroller = scrollbar = new NanoScroll(this, options);
722
      }
723
      if (settings && typeof settings === "object") {
724
        $.extend(scrollbar.options, settings);
725
        if (settings.scrollBottom) {
726
          return scrollbar.scrollBottom(settings.scrollBottom);
727
        }
728
        if (settings.scrollTop) {
729
          return scrollbar.scrollTop(settings.scrollTop);
730
        }
731
        if (settings.scrollTo) {
732
          return scrollbar.scrollTo(settings.scrollTo);
733
        }
734
        if (settings.scroll === 'bottom') {
735
          return scrollbar.scrollBottom(0);
736
        }
737
        if (settings.scroll === 'top') {
738
          return scrollbar.scrollTop(0);
739
        }
740
        if (settings.scroll && settings.scroll instanceof $) {
741
          return scrollbar.scrollTo(settings.scroll);
742
        }
743
        if (settings.stop) {
744
          return scrollbar.stop();
745
        }
746
        if (settings.flash) {
747
          return scrollbar.flash();
748
        }
749
      }
750
      return scrollbar.reset();
751
    });
752
  };
753
})(jQuery, window, document);
754