1/* 2 * scrollToTop 3 * https://github.com/amazingsurge/jquery-scrollToTop 4 * 5 * Copyright (c) 2014 amazingsurge 6 * Licensed under the GPL license. 7 */ 8 9(function(window, document, $, undefined) { 10 'use strict'; 11 12 // Constructor 13 var ScrollToTop = function(options) { 14 this.$doc = $('body'); 15 this.options = $.extend(ScrollToTop.defaults, options); 16 17 var namespace = this.options.namespace; 18 19 if (this.options.skin === null) { 20 this.options.skin = 'default'; 21 } 22 23 this.classes = { 24 skin: namespace + '_' + this.options.skin, 25 trigger: namespace, 26 animating: namespace + '_animating', 27 show: namespace + '_show' 28 }; 29 30 this.disabled = false; 31 this.useMobile = false; 32 this.isShow = false; 33 34 var self = this; 35 $.extend(self, { 36 init: function() { 37 self.transition = self.transition(); 38 self.build(); 39 40 41 if (self.options.target) { 42 if (typeof self.options.target === 'number') { 43 self.target = self.options.target; 44 } else if (typeof self.options.target === 'string') { 45 self.target = Math.floor($(self.options.target).offset().top); 46 } 47 } else { 48 self.target = 0; 49 } 50 51 self.$trigger.on('click.scrollToTop', function() { 52 self.$doc.trigger('ScrollToTop::jump'); 53 return false; 54 }); 55 56 // bind events 57 self.$doc.on('ScrollToTop::jump', function() { 58 if (self.disabled) { 59 return; 60 } 61 62 self.checkMobile(); 63 64 var speed, easing; 65 66 if (self.useMobile) { 67 speed = self.options.mobile.speed; 68 easing = self.options.mobile.easing; 69 } else { 70 speed = self.options.speed; 71 easing = self.options.easing; 72 } 73 74 self.$doc.addClass(self.classes.animating); 75 76 77 if (self.transition.supported) { 78 var pos = $(window).scrollTop(); 79 80 self.$doc.css({ 81 'margin-top': -pos + self.target + 'px' 82 }); 83 $(window).scrollTop(self.target); 84 85 self.insertRule('.duration_' + speed + '{' + self.transition.prefix + 'transition-duration: ' + speed + 'ms;}'); 86 87 self.$doc.addClass('easing_' + easing + ' duration_' + speed).css({ 88 'margin-top': '' 89 }).one(self.transition.end, function() { 90 self.$doc.removeClass(self.classes.animating + ' easing_' + easing + ' duration_' + speed); 91 }); 92 } else { 93 $('html, body').stop(true, false).animate({ 94 scrollTop: self.target 95 }, speed, function() { 96 self.$doc.removeClass(self.classes.animating); 97 }); 98 return; 99 } 100 }) 101 .on('ScrollToTop::show', function() { 102 if (self.isShow) { 103 return; 104 } 105 self.isShow = true; 106 107 self.$trigger.addClass(self.classes.show); 108 }) 109 .on('ScrollToTop::hide', function() { 110 if (!self.isShow) { 111 return; 112 } 113 self.isShow = false; 114 self.$trigger.removeClass(self.classes.show); 115 }) 116 .on('ScrollToTop::disable', function() { 117 self.disabled = true; 118 self.$doc.trigger('ScrollToTop::hide'); 119 }) 120 .on('ScrollToTop::enable', function() { 121 self.disabled = false;
122 self.toggle(); 123 }); 124 125 $(window).on('scroll', self._throttle(function() { 126 if (self.disabled) { 127 return; 128 } 129 130 self.toggle(); 131 }, self.options.throttle)); 132 133 if (self.options.mobile) { 134 $(window).on('resize', self._throttle(function() { 135 if (self.disabled) { 136 return; 137 } 138 139 self.checkMobile(); 140 }, self.options.throttle)); 141 } 142 143 self.toggle(); 144 }, 145 checkMobile: function() { 146 var width = $(window).width(); 147 148 if (width < self.options.mobile.width) { 149 self.useMobile = true; 150 } else { 151 self.useMobile = false; 152 } 153 }, 154 build: function() { 155 if (self.options.trigger) { 156 self.$trigger = $(self.options.trigger); 157 } else { 158 self.$trigger = $('<a href="#" class="' + self.classes.trigger + ' ' + self.classes.skin + '">' + self.options.text + '</a>').appendTo($('body')); 159 } 160 161 self.insertRule('.' + self.classes.show + '{' + self.transition.prefix + 'animation-duration: ' + self.options.animationSpeed + 'ms;' + self.transition.prefix + 'animation-name: ' + self.options.namespace + '_' + self.options.animation + ';}'); 162 163 if (self.options.mobile) { 164 self.insertRule('@media (max-width: ' + self.options.mobile.width + 'px){.' + self.classes.show + '{' + self.transition.prefix + 'animation-duration: ' + self.options.mobile.animationSpeed + 'ms !important;' + self.transition.prefix + 'animation-name: ' + self.options.namespace + '_' + self.options.mobile.animation + ' !important;}}'); 165 } 166 }, 167 can: function() { 168 var distance; 169 if (self.useMobile) { 170 distance = self.options.mobile.distance; 171 } else { 172 distance = self.options.distance; 173 } 174 if ($(window).scrollTop() > distance) { 175 return true; 176 } else { 177 return false; 178 } 179 }, 180 toggle: function() { 181 if (self.can()) { 182 self.$doc.trigger('ScrollToTop::show'); 183 } else { 184 self.$doc.trigger('ScrollToTop::hide'); 185 } 186 }, 187 188 transition: function() { 189 var e, 190 end, 191 prefix = '', 192 supported = false, 193 el = document.createElement("fakeelement"), 194 transitions = { 195 "WebkitTransition": "webkitTransitionEnd", 196 "MozTransition": "transitionend", 197 "OTransition": "oTransitionend", 198 "transition": "transitionend" 199 }; 200 for (e in transitions) { 201 if (el.style[e] !== undefined) { 202 end = transitions[e]; 203 supported = true; 204 break; 205 } 206 } 207 if (/(WebKit)/i.test(window.navigator.userAgent)) { 208 prefix = '-webkit-'; 209 } 210 return { 211 prefix: prefix, 212 end: end, 213 supported: supported 214 }; 215 }, 216 insertRule: function(rule) { 217 if (self.rules && self.rules[rule]) { 218 return; 219 } else if (self.rules === undefined) { 220 self.rules = {}; 221 } else { 222 self.rules[rule] = true; 223 } 224 225 if (document.styleSheets && document.styleSheets.length) { 226 document.styleSheets[0].insertRule(rule, 0); 227 } else { 228 var style = document.createElement('style'); 229 style.innerHTML = rule; 230 document.head.appendChild(style); 231 } 232 }, 233 /** 234 * _throttle
235 * @description Borrowed from Underscore.js 236 */ 237 _throttle: function(func, wait) { 238 var _now = Date.now || function() { 239 return new Date().getTime(); 240 }; 241 var context, args, result; 242 var timeout = null; 243 var previous = 0; 244 var later = function() { 245 previous = _now(); 246 timeout = null; 247 result = func.apply(context, args); 248 context = args = null; 249 }; 250 return function() { 251 var now = _now(); 252 var remaining = wait - (now - previous); 253 context = this; 254 args = arguments; 255 if (remaining <= 0) { 256 clearTimeout(timeout); 257 timeout = null; 258 previous = now; 259 result = func.apply(context, args); 260 context = args = null; 261 } else if (!timeout) { 262 timeout = setTimeout(later, remaining); 263 } 264 return result; 265 }; 266 } 267 }); 268 269 this.init(); 270 }; 271 272 // Default options 273 ScrollToTop.defaults = { 274 distance: 200, 275 speed: 1000, 276 easing: 'linear', 277 animation: 'fade', // fade, slide, none 278 animationSpeed: 500, 279 280 mobile: { 281 width: 768, 282 distance: 100, 283 speed: 1000, 284 easing: 'easeInOutElastic', 285 animation: 'slide', 286 animationSpeed: 200 287 }, 288 289 trigger: null, // Set a custom triggering element. Can be an HTML string or jQuery object 290 target: null, // Set a custom target element for scrolling to. Can be element or number 291 text: 'Scroll To Top', // Text for element, can contain HTML 292 293 skin: null, 294 throttle: 250, 295 296 namespace: 'scrollToTop' 297 }; 298 299 ScrollToTop.prototype = { 300 constructor: ScrollToTop, 301 jump: function() { 302 this.$doc.trigger('ScrollToTop::jump'); 303 }, 304 disable: function() { 305 this.$doc.trigger('ScrollToTop::disable'); 306 }, 307 enable: function() { 308 this.$doc.trigger('ScrollToTop::enable'); 309 }, 310 destroy: function() { 311 this.$trigger.remove(); 312 this.$doc.data('ScrollToTop', null); 313 this.$doc.off('ScrollToTop::enable') 314 .off('ScrollToTop::disable') 315 .off('ScrollToTop::jump') 316 .off('ScrollToTop::show') 317 .off('ScrollToTop::hide'); 318 } 319 }; 320 321 $.fn.scrollToTop = function(options) { 322 if (typeof options === 'string') { 323 var method = options; 324 var method_arguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : undefined; 325 326 return this.each(function() { 327 var api = $.data(this, 'scrollToTop'); 328 329 if (api && typeof api[method] === 'function') { 330 api[method].apply(api, method_arguments); 331 } 332 }); 333 } else { 334 return this.each(function() { 335 var api = $.data(this, 'scrollToTop'); 336 if (!api) { 337 api = new ScrollToTop(options); 338 $.data(this, 'scrollToTop', api); 339 } 340 }); 341 } 342 }; 343}(window, document, jQuery));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.