<commit_before>function Scrollify(scrollModule, options){
	'use strict';
	var heights = [],
		names = [],
		elements = [],
		index = 0,
		currentIndex = 0,
		hasLocation = false,
		timeoutId,
		timeoutId2,
		top = getScrollTop(),
		scrollable = false,
		locked = false,
		scrolled = false,
		manualScroll,
		swipeScroll,
		util,
		disabled = false,
		scrollSamples = [],
		scrollTime = new Date().getTime(),
		firstLoad = true,
		destination = 0,
		wheelEvent = 'onwheel' in document ? 'wheel' : document.onmousewheel !== undefined ? 'mousewheel' : 'DOMMouseScroll',
		settings = {
			section: '.section',
			sectionName: 'section-name',
			offset : 0,
			scrollbars: false,
			target:'html,body',
			standardScrollElements: false,
			setHeights: true,
			updateHash: true,
			before:function() {},
			after:function() {},
			afterResize:function() {},
			afterRender:function() {}
		};

	// replace $window.scrollTop()
	function getScrollTop(){
		return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
	}

	function updateHistory(panelName){
		if(history.pushState) {
			try {
				history.replaceState(null, null, panelName);
			} catch (e) {
				if(window.console) {
					console.warn('Scrollify warning: Page must be hosted to manipulate the hash value.');
				}
			}
		} else {
			window.location.hash = panelName;
		}
	}

	function warnHashValue(){
		if(window.location.hash.length && settings.sectionName && window.console) {
			try {
				if(document.querySelectorAll(window.location.hash).length > 0){
					console.warn('Scrollify warning: ID matches hash value - this will cause the page to anchor.');
				}
			} catch (e) {}
		}
	}

	function animateScroll(index,instant,callbacks,toTop) {
		if(currentIndex===index) {
			callbacks = false;
		}
		if(disabled===true) {
			return true;
		}

		if(!names[index]){
			return;
		}

		scrollable = false;
		if(callbacks) {
			settings.before(index,elements);
		}

		// TODO: move this into its own function
		if(settings.updateHash && settings.sectionName && !(firstLoad===true && index===0)) {
			updateHistory(names[index]);
		}
		if(firstLoad) {
			settings.afterRender(index);
			firstLoad = false;
		}

		currentIndex = index;

		if(instant) {
      scrollModule.scrollTo(elements[index], instant).then(() => {
				if(callbacks) {
					settings.after(index,elements);
				}
			});
		} else {
			locked = true;
			warnHashValue();

      scrollModule.scrollTo(elements[index]).then(() => {
				locked = false;
				firstLoad = false;
				if(callbacks) {
					settings.after(index,elements);
				}
			});
		}
	}

	function isAccelerating(samples) {
		function average(num) {
			var sum = 0;

			var lastElements = samples.slice(Math.max(samples.length - num, 1));

      for(var i = 0; i < lastElements.length; i++){
          sum += lastElements[i];
      }

      return Math.ceil(sum/num);
		}

		var avEnd = average(10);
    var avMiddle = average(70);

    if(avEnd >= avMiddle) {
			return true;
		} else {
			return false;
		}
	}

	manualScroll = {
		handleMousedown:function() {
			if(disabled===true) {
				return true;
			}
			scrollable = false;
			scrolled = false;
		},
		handleMouseup:function() {
			if(disabled===true) {
				return true;
			}
			scrollable = true;
			if(scrolled) {
				//instant,callbacks
				manualScroll.calculateNearest(false,true);
			}
		},
		handleScroll:function() {
			if(disabled===true) {
				return true;
			}
			if(timeoutId){
				clearTimeout(timeoutId);
			}

			timeoutId = setTimeout(function(){
				scrolled = true;
				if(scrollable===false) {
					return false;
				}
				scrollable = false;
				//instant,callbacks
				manualScroll.calculateNearest(false,true);
			}, 200);
		},
		calculateNearest:function(instant,callbacks) {
			top = getScrollTop();
			var i =1,
				max = heights.length,
				closest = 0,
				prev = Math.abs(heights[0] - top),
				diff;
			for(;i<max;i++) {
				diff = Math.abs(heights[i] - top);

				if(diff < prev) {
					prev = diff;
					closest = i;
				}
			}
			if((atBottom() && closest>index) || atTop()) {
				index = closest;
				//index, instant, callbacks, toTop
				animateScroll(closest,instant,callbacks,false);
			}
		},
		wheelHandler:function(e) {
			if(disabled===true) {
				return true;
			} else if(settings.standardScrollElements) {

				if(e.target.matches(settings.standardScrollElements) || e.target.closest(settings.standardScrollElements).length) {
					return true;
				}
			}
			e.preventDefault();
			var currentScrollTime = new Date().getTime();

			e = e || window.event;
			var value = e.wheelDelta || -e.deltaY || -e.detail;
			var delta = Math.max(-1, Math.min(1, value));

			if(scrollSamples.length > 149){
				scrollSamples.shift();
			}
			scrollSamples.push(Math.abs(value));

			if((currentScrollTime-scrollTime) > 200){
				scrollSamples = [];
			}
			scrollTime = currentScrollTime;

			if(locked) {
				return false;
			}
			if(delta<0) {
				if(index<heights.length-1) {
					if(atBottom()) {
						if(isAccelerating(scrollSamples)) {
							e.preventDefault();
							index++;
							locked = true;
							//index, instant, callbacks, toTop
							animateScroll(index,false,true, false);
						} else {
							return false;
						}
					}
				}
			} else if(delta>0) {
				if(index>0) {
					if(atTop()) {
						if(isAccelerating(scrollSamples)) {
							e.preventDefault();
							index--;
							locked = true;
							//index, instant, callbacks, toTop
							animateScroll(index,false,true, false);
						} else {
							return false
						}
					}
				}
			}

		},
		keyHandler:function(e) {
			if(disabled===true) {
				return true;
			}
			if(locked===true) {
				return false;
			}
			if(e.keyCode==38 || e.keyCode==33) {
				if(index>0) {
					if(atTop()) {
						e.preventDefault();
						index--;
						//index, instant, callbacks, toTop
						animateScroll(index,false,true,false);
					}
				}
			} else if(e.keyCode==40 || e.keyCode==34) {
				if(index<heights.length-1) {
					if(atBottom()) {
						e.preventDefault();
						index++;
						//index, instant, callbacks, toTop
						animateScroll(index,false,true,false);
					}
				}
			}
		},
		init:function() {
			if(settings.scrollbars) {
				window.addEventListener('mousedown', manualScroll.handleMousedown);
				window.addEventListener('mouseup', manualScroll.handleMouseup);
				window.addEventListener('scroll', manualScroll.handleScroll);
			} else {
				document.querySelector('body').style.overflow = 'hidden';
			}

			window.addEventListener(wheelEvent, manualScroll.wheelHandler);
			window.addEventListener('keydown', manualScroll.keyHandler);
		}
	};

	// TODO: move these into manualScroll
	function atTop() {
		return true;
	}
	function atBottom() {
		return true;
	}

	util = {
		refresh:function(withCallback,scroll) {
			clearTimeout(timeoutId2);
			timeoutId2 = setTimeout(function() {
				//retain position
				sizePanels(true);
				//scroll, firstLoad
				calculatePositions(scroll,false);
				if(withCallback) {
					settings.afterResize();
				}
			},400);
		},
		handleUpdate:function() {
			//callbacks, scroll
			//changed from false,true to false,false
			util.refresh(false,false);
		},
		handleResize:function() {
			//callbacks, scroll
			util.refresh(true,false);
		},
		handleOrientation:function() {
			//callbacks, scroll
			util.refresh(true,true);
		}
	};

	function sizePanels(keepPosition) {
		if(keepPosition) {
			top = getScrollTop();
		}

		var selector = settings.section;

		// TODO: break this out into extra methods
		document.querySelectorAll(selector)
			.forEach(function(val, i){
				if(settings.setHeights){

					val.style.height = '';
					if((val.offsetHeight <= window.innerHeight) || val.style.overflow === 'hidden' ){
						val.style.height = window.innerHeight;
					}
				}
			});

		if(keepPosition) {
			// TODO: make this work (should set scroll top to top);
			getScrollTop(top);
		}
	}
	function calculatePositions(scroll,firstLoad) {
		var selector = settings.section;

		heights = [];
		names = [];
		elements = [];

		document.querySelectorAll(selector)
			.forEach(function(val, i){
				if(i>0) {
					heights[i] = parseInt(val.getBoundingClientRect().top + document.documentElement.scrollTop) + settings.offset;
				} else {
					heights[i] = parseInt(val.getBoundingClientRect().top + document.documentElement.scrollTop);
				}
				if(settings.sectionName && val.getAttribute(settings.sectionName)) {
					names[i] = '#' + val.getAttribute(settings.sectionName).toString().replace(/ /g,'-');
				} else {
						names[i] = '#' + (i + 1);
				}
				elements[i] = val;

				try {
					if(document.getElementById(names[i]) && window.console){
						console.warn('Scrollify warning: Section names can\'t match IDs - this will cause the browser to anchor.');
					}
				} catch (e) {}

				if(window.location.hash===names[i]) {
					index = i;
					hasLocation = true;
				}
			});

		if(true===scroll) {
			//index, instant, callbacks, toTop
			animateScroll(index,false,false,false);
		}
	}

	class Scroller{
		constructor(options){
			this.settings = Object.assign(settings, options);

			//retain position
			sizePanels(false);
			calculatePositions(false,true);

			if(true===hasLocation) {
				//index, instant, callbacks, toTop
				animateScroll(index,false,true,true);
			} else {
				setTimeout(function() {
					//instant,callbacks
					manualScroll.calculateNearest(true,false);
				},200);
			}
			if(heights.length) {
				manualScroll.init();
				window.addEventListener('resize', util.handleResize);
				if (document.addEventListener) {
					window.addEventListener('orientationchange', util.handleOrientation, false);
				}
			}
		}

		_move(panel,instant) {
			var z = names.length;
			for(;z>=0;z--) {
				if(typeof panel === 'string') {
					if (names[z]===panel) {
						index = z;
						//index, instant, callbacks, toTop
						animateScroll(z,instant,true,true);
					}
				} else {
					if(z===panel) {
						index = z;
						//index, instant, callbacks, toTop
						animateScroll(z,instant,true,true);
					}
				}
			}
		};

		move(panel) {
			if(panel===undefined) {
				return false;
			}
			this._move(panel,false);
		};

		instantMove(panel) {
			if(panel===undefined) {
				return false;
			}
			this._move(panel,true);
		};

		next() {
			if(index<names.length) {
				index += 1;
				//index, instant, callbacks, toTop
				animateScroll(index,false,true,true);
			}
		};

		previous() {
			if(index>0) {
				index -= 1;
				//index, instant, callbacks, toTop
				animateScroll(index,false,true,true);
			}
		};

		instantNext() {
			if(index<names.length) {
				index += 1;
				//index, instant, callbacks, toTop
				animateScroll(index,true,true,true);
			}
		};

		instantPrevious() {
			if(index>0) {
				index -= 1;
				//index, instant, callbacks, toTop
				animateScroll(index,true,true,true);
			}
		};

		destroy() {
			if(settings.setHeights) {
				document.querySelectorAll(settings.section).forEach(function(val){
					val.style.height = '';
				});
			}

			window.removeEventListener('resize', util.handleResize);
			// TODO: remove settings.scrollbars
			if(settings.scrollbars) {
				window.removeEventListener('mousedown', manualScroll.handleMousedown);
				window.removeEventListener('mouseup', manualScroll.handleMouseup);
				window.removeEventListener('scroll', manualScroll.handleScroll);
			}

			window.removeEventListener(wheelEvent, manualScroll.wheelHandler);
			window.removeEventListener('keydown', manualScroll.keyHandler);

			heights = [];
			names = [];
			elements = [];
		};

		update() {
			util.handleUpdate();
		};

		current() {
			return elements[index];
		};

		disable() {
			disabled = true;
			document.querySelector('body').style.overflow = '';
		};

		enable() {
			disabled = false;
			if(!settings.scrollbars) {
				document.querySelector('body').style.overflow = 'hidden';
			}
			//instant,callbacks
			manualScroll.calculateNearest(false,false);
		};

		isDisabled() {
			return disabled;
		};

		setOptions(updatedOptions) {
			if(typeof updatedOptions === 'object') {
				settings = Object.assign(settings, updatedOptions);
				util.handleUpdate();
			} else if(window.console) {
				console.warn('Scrollify warning: setOptions expects an object.');
			}
		};

		getOptions(){
			return this.settings;
		};
	}

	return new Scroller(options);
}
<commit_msg>Export Scrollify as defaultmodule<commit_after>function Scrollify(scrollModule, options){
	'use strict';
	var heights = [],
		names = [],
		elements = [],
		index = 0,
		currentIndex = 0,
		hasLocation = false,
		timeoutId,
		timeoutId2,
		top = getScrollTop(),
		scrollable = false,
		locked = false,
		scrolled = false,
		manualScroll,
		swipeScroll,
		util,
		disabled = false,
		scrollSamples = [],
		scrollTime = new Date().getTime(),
		firstLoad = true,
		destination = 0,
		wheelEvent = 'onwheel' in document ? 'wheel' : document.onmousewheel !== undefined ? 'mousewheel' : 'DOMMouseScroll',
		settings = {
			section: '.section',
			sectionName: 'section-name',
			offset : 0,
			scrollbars: false,
			target:'html,body',
			standardScrollElements: false,
			setHeights: true,
			updateHash: true,
			before:function() {},
			after:function() {},
			afterResize:function() {},
			afterRender:function() {}
		};

	// replace $window.scrollTop()
	function getScrollTop(){
		return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
	}

	function updateHistory(panelName){
		if(history.pushState) {
			try {
				history.replaceState(null, null, panelName);
			} catch (e) {
				if(window.console) {
					console.warn('Scrollify warning: Page must be hosted to manipulate the hash value.');
				}
			}
		} else {
			window.location.hash = panelName;
		}
	}

	function warnHashValue(){
		if(window.location.hash.length && settings.sectionName && window.console) {
			try {
				if(document.querySelectorAll(window.location.hash).length > 0){
					console.warn('Scrollify warning: ID matches hash value - this will cause the page to anchor.');
				}
			} catch (e) {}
		}
	}

	function animateScroll(index,instant,callbacks,toTop) {
		if(currentIndex===index) {
			callbacks = false;
		}
		if(disabled===true) {
			return true;
		}

		if(!names[index]){
			return;
		}

		scrollable = false;
		if(callbacks) {
			settings.before(index,elements);
		}

		// TODO: move this into its own function
		if(settings.updateHash && settings.sectionName && !(firstLoad===true && index===0)) {
			updateHistory(names[index]);
		}
		if(firstLoad) {
			settings.afterRender(index);
			firstLoad = false;
		}

		currentIndex = index;

		if(instant) {
      scrollModule.scrollTo(elements[index], instant).then(() => {
				if(callbacks) {
					settings.after(index,elements);
				}
			});
		} else {
			locked = true;
			warnHashValue();

      scrollModule.scrollTo(elements[index]).then(() => {
				locked = false;
				firstLoad = false;
				if(callbacks) {
					settings.after(index,elements);
				}
			});
		}
	}

	function isAccelerating(samples) {
		function average(num) {
			var sum = 0;

			var lastElements = samples.slice(Math.max(samples.length - num, 1));

      for(var i = 0; i < lastElements.length; i++){
          sum += lastElements[i];
      }

      return Math.ceil(sum/num);
		}

		var avEnd = average(10);
    var avMiddle = average(70);

    if(avEnd >= avMiddle) {
			return true;
		} else {
			return false;
		}
	}

	manualScroll = {
		handleMousedown:function() {
			if(disabled===true) {
				return true;
			}
			scrollable = false;
			scrolled = false;
		},
		handleMouseup:function() {
			if(disabled===true) {
				return true;
			}
			scrollable = true;
			if(scrolled) {
				//instant,callbacks
				manualScroll.calculateNearest(false,true);
			}
		},
		handleScroll:function() {
			if(disabled===true) {
				return true;
			}
			if(timeoutId){
				clearTimeout(timeoutId);
			}

			timeoutId = setTimeout(function(){
				scrolled = true;
				if(scrollable===false) {
					return false;
				}
				scrollable = false;
				//instant,callbacks
				manualScroll.calculateNearest(false,true);
			}, 200);
		},
		calculateNearest:function(instant,callbacks) {
			top = getScrollTop();
			var i =1,
				max = heights.length,
				closest = 0,
				prev = Math.abs(heights[0] - top),
				diff;
			for(;i<max;i++) {
				diff = Math.abs(heights[i] - top);

				if(diff < prev) {
					prev = diff;
					closest = i;
				}
			}
			if((atBottom() && closest>index) || atTop()) {
				index = closest;
				//index, instant, callbacks, toTop
				animateScroll(closest,instant,callbacks,false);
			}
		},
		wheelHandler:function(e) {
			if(disabled===true) {
				return true;
			} else if(settings.standardScrollElements) {

				if(e.target.matches(settings.standardScrollElements) || e.target.closest(settings.standardScrollElements).length) {
					return true;
				}
			}
			e.preventDefault();
			var currentScrollTime = new Date().getTime();

			e = e || window.event;
			var value = e.wheelDelta || -e.deltaY || -e.detail;
			var delta = Math.max(-1, Math.min(1, value));

			if(scrollSamples.length > 149){
				scrollSamples.shift();
			}
			scrollSamples.push(Math.abs(value));

			if((currentScrollTime-scrollTime) > 200){
				scrollSamples = [];
			}
			scrollTime = currentScrollTime;

			if(locked) {
				return false;
			}
			if(delta<0) {
				if(index<heights.length-1) {
					if(atBottom()) {
						if(isAccelerating(scrollSamples)) {
							e.preventDefault();
							index++;
							locked = true;
							//index, instant, callbacks, toTop
							animateScroll(index,false,true, false);
						} else {
							return false;
						}
					}
				}
			} else if(delta>0) {
				if(index>0) {
					if(atTop()) {
						if(isAccelerating(scrollSamples)) {
							e.preventDefault();
							index--;
							locked = true;
							//index, instant, callbacks, toTop
							animateScroll(index,false,true, false);
						} else {
							return false
						}
					}
				}
			}

		},
		keyHandler:function(e) {
			if(disabled===true) {
				return true;
			}
			if(locked===true) {
				return false;
			}
			if(e.keyCode==38 || e.keyCode==33) {
				if(index>0) {
					if(atTop()) {
						e.preventDefault();
						index--;
						//index, instant, callbacks, toTop
						animateScroll(index,false,true,false);
					}
				}
			} else if(e.keyCode==40 || e.keyCode==34) {
				if(index<heights.length-1) {
					if(atBottom()) {
						e.preventDefault();
						index++;
						//index, instant, callbacks, toTop
						animateScroll(index,false,true,false);
					}
				}
			}
		},
		init:function() {
			if(settings.scrollbars) {
				window.addEventListener('mousedown', manualScroll.handleMousedown);
				window.addEventListener('mouseup', manualScroll.handleMouseup);
				window.addEventListener('scroll', manualScroll.handleScroll);
			} else {
				document.querySelector('body').style.overflow = 'hidden';
			}

			window.addEventListener(wheelEvent, manualScroll.wheelHandler);
			window.addEventListener('keydown', manualScroll.keyHandler);
		}
	};

	// TODO: move these into manualScroll
	function atTop() {
		return true;
	}
	function atBottom() {
		return true;
	}

	util = {
		refresh:function(withCallback,scroll) {
			clearTimeout(timeoutId2);
			timeoutId2 = setTimeout(function() {
				//retain position
				sizePanels(true);
				//scroll, firstLoad
				calculatePositions(scroll,false);
				if(withCallback) {
					settings.afterResize();
				}
			},400);
		},
		handleUpdate:function() {
			//callbacks, scroll
			//changed from false,true to false,false
			util.refresh(false,false);
		},
		handleResize:function() {
			//callbacks, scroll
			util.refresh(true,false);
		},
		handleOrientation:function() {
			//callbacks, scroll
			util.refresh(true,true);
		}
	};

	function sizePanels(keepPosition) {
		if(keepPosition) {
			top = getScrollTop();
		}

		var selector = settings.section;

		// TODO: break this out into extra methods
		document.querySelectorAll(selector)
			.forEach(function(val, i){
				if(settings.setHeights){

					val.style.height = '';
					if((val.offsetHeight <= window.innerHeight) || val.style.overflow === 'hidden' ){
						val.style.height = window.innerHeight;
					}
				}
			});

		if(keepPosition) {
			// TODO: make this work (should set scroll top to top);
			getScrollTop(top);
		}
	}
	function calculatePositions(scroll,firstLoad) {
		var selector = settings.section;

		heights = [];
		names = [];
		elements = [];

		document.querySelectorAll(selector)
			.forEach(function(val, i){
				if(i>0) {
					heights[i] = parseInt(val.getBoundingClientRect().top + document.documentElement.scrollTop) + settings.offset;
				} else {
					heights[i] = parseInt(val.getBoundingClientRect().top + document.documentElement.scrollTop);
				}
				if(settings.sectionName && val.getAttribute(settings.sectionName)) {
					names[i] = '#' + val.getAttribute(settings.sectionName).toString().replace(/ /g,'-');
				} else {
						names[i] = '#' + (i + 1);
				}
				elements[i] = val;

				try {
					if(document.getElementById(names[i]) && window.console){
						console.warn('Scrollify warning: Section names can\'t match IDs - this will cause the browser to anchor.');
					}
				} catch (e) {}

				if(window.location.hash===names[i]) {
					index = i;
					hasLocation = true;
				}
			});

		if(true===scroll) {
			//index, instant, callbacks, toTop
			animateScroll(index,false,false,false);
		}
	}

	class Scroller{
		constructor(options){
			this.settings = Object.assign(settings, options);

			//retain position
			sizePanels(false);
			calculatePositions(false,true);

			if(true===hasLocation) {
				//index, instant, callbacks, toTop
				animateScroll(index,false,true,true);
			} else {
				setTimeout(function() {
					//instant,callbacks
					manualScroll.calculateNearest(true,false);
				},200);
			}
			if(heights.length) {
				manualScroll.init();
				window.addEventListener('resize', util.handleResize);
				if (document.addEventListener) {
					window.addEventListener('orientationchange', util.handleOrientation, false);
				}
			}
		}

		_move(panel,instant) {
			var z = names.length;
			for(;z>=0;z--) {
				if(typeof panel === 'string') {
					if (names[z]===panel) {
						index = z;
						//index, instant, callbacks, toTop
						animateScroll(z,instant,true,true);
					}
				} else {
					if(z===panel) {
						index = z;
						//index, instant, callbacks, toTop
						animateScroll(z,instant,true,true);
					}
				}
			}
		};

		move(panel) {
			if(panel===undefined) {
				return false;
			}
			this._move(panel,false);
		};

		instantMove(panel) {
			if(panel===undefined) {
				return false;
			}
			this._move(panel,true);
		};

		next() {
			if(index<names.length) {
				index += 1;
				//index, instant, callbacks, toTop
				animateScroll(index,false,true,true);
			}
		};

		previous() {
			if(index>0) {
				index -= 1;
				//index, instant, callbacks, toTop
				animateScroll(index,false,true,true);
			}
		};

		instantNext() {
			if(index<names.length) {
				index += 1;
				//index, instant, callbacks, toTop
				animateScroll(index,true,true,true);
			}
		};

		instantPrevious() {
			if(index>0) {
				index -= 1;
				//index, instant, callbacks, toTop
				animateScroll(index,true,true,true);
			}
		};

		destroy() {
			if(settings.setHeights) {
				document.querySelectorAll(settings.section).forEach(function(val){
					val.style.height = '';
				});
			}

			window.removeEventListener('resize', util.handleResize);
			// TODO: remove settings.scrollbars
			if(settings.scrollbars) {
				window.removeEventListener('mousedown', manualScroll.handleMousedown);
				window.removeEventListener('mouseup', manualScroll.handleMouseup);
				window.removeEventListener('scroll', manualScroll.handleScroll);
			}

			window.removeEventListener(wheelEvent, manualScroll.wheelHandler);
			window.removeEventListener('keydown', manualScroll.keyHandler);

			heights = [];
			names = [];
			elements = [];
		};

		update() {
			util.handleUpdate();
		};

		current() {
			return elements[index];
		};

		disable() {
			disabled = true;
			document.querySelector('body').style.overflow = '';
		};

		enable() {
			disabled = false;
			if(!settings.scrollbars) {
				document.querySelector('body').style.overflow = 'hidden';
			}
			//instant,callbacks
			manualScroll.calculateNearest(false,false);
		};

		isDisabled() {
			return disabled;
		};

		setOptions(updatedOptions) {
			if(typeof updatedOptions === 'object') {
				settings = Object.assign(settings, updatedOptions);
				util.handleUpdate();
			} else if(window.console) {
				console.warn('Scrollify warning: setOptions expects an object.');
			}
		};

		getOptions(){
			return this.settings;
		};
	}

	return new Scroller(options);
}

export default Scrollify;
<|endoftext|>


<commit_before>var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

// 辅助函数
var utils = require('./utils');
var fullPath  = utils.fullPath;
var pickFiles = utils.pickFiles;

// 项目根路径
var ROOT_PATH = fullPath('../');
// 项目源码路径
var SRC_PATH = ROOT_PATH + '/src';
// 产出路径
var DIST_PATH = ROOT_PATH + '/dist';
// node_modules
var NODE_MODULES_PATH =  ROOT_PATH + '/node_modules';

var __DEV__ = process.env.NODE_ENV !== 'production';

// conf
var alias = pickFiles({
  id: /(conf\/[^\/]+).js$/,
  pattern: SRC_PATH + '/conf/*.js'
});

// components
alias = Object.assign(alias, pickFiles({
  id: /(components\/[^\/]+)/,
  pattern: SRC_PATH + '/components/*/index.js'
}));

// reducers
alias = Object.assign(alias, pickFiles({
  id: /(reducers\/[^\/]+).js/,
  pattern: SRC_PATH + '/js/reducers/*'
}));

// actions
alias = Object.assign(alias, pickFiles({
  id: /(actions\/[^\/]+).js/,
  pattern: SRC_PATH + '/js/actions/*'
}));

var config = {
  context: SRC_PATH,
  entry: {
    app: ['./pages/app.js'],
    lib: [
      'react', 'react-dom', 'react-router',
      'redux', 'react-redux', 'redux-thunk'
    ],
  },
  output: {
    path: DIST_PATH,
    filename: 'js/[name].[hash].js'
  },
  module: {},
  resolve: {
    alias: alias
  },
  plugins: [
    new webpack.DefinePlugin({
      // http://stackoverflow.com/questions/30030031/passing-environment-dependent-variables-in-webpack
      "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || 'development')
    }),
    new webpack.optimize.CommonsChunkPlugin('lib', 'js/[name].[hash].js')
  ]
};

// loaders
var CACHE_PATH = ROOT_PATH + '/cache';
config.module.loaders = [];

// 使用 babel 编译 jsx、es6
config.module.loaders.push({
  test: /\.js$/,
  exclude: /node_modules/,
  include: SRC_PATH,
  // 这里使用 loaders ，因为后面还需要添加 loader
  loaders: ['babel?cacheDirectory=' + CACHE_PATH]
});

// 编译 sass
if (__DEV__) {
  config.module.loaders.push({
    test: /\.(scss|css)$/,
    loaders: ['style', 'css', 'sass', 'postcss']
  });
} else {
  config.module.loaders.push({
    test: /\.(scss|css)$/,
    loader: ExtractTextPlugin.extract('style-', 'css!sass')
  });
  config.plugins.push(
    new ExtractTextPlugin('css/[name].[hash].css')
  );
}

// css autoprefix
var precss = require('precss');
var autoprefixer = require('autoprefixer');
config.postcss = function() {
  return [precss, autoprefixer];
}

// 图片路径处理，压缩
config.module.loaders.push({
  test: /\.(?:jpg|gif|png|svg)$/,
  loaders: [
    'url?limit=8000&name=img/[hash].[ext]',
    'image-webpack'
  ]
});

// html 页面
var HtmlwebpackPlugin = require('html-webpack-plugin');
config.plugins.push(
  new HtmlwebpackPlugin({
    filename: 'index.html',
    chunks: ['app', 'lib'],
    template: SRC_PATH + '/pages/app.html',
    minify: __DEV__ ? false : {
      collapseWhitespace: true,
      collapseInlineTagWhitespace: true,
      removeRedundantAttributes: true,
      removeEmptyAttributes: true,
      removeScriptTypeAttributes: true,
      removeStyleLinkTypeAttributes: true,
      removeComments: true
    }
  })
);

module.exports = config;

<commit_msg>fixbug<commit_after>var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

// 辅助函数
var utils = require('./utils');
var fullPath  = utils.fullPath;
var pickFiles = utils.pickFiles;

// 项目根路径
var ROOT_PATH = fullPath('../');
// 项目源码路径
var SRC_PATH = ROOT_PATH + '/src';
// 产出路径
var DIST_PATH = ROOT_PATH + '/dist';
// node_modules
var NODE_MODULES_PATH =  ROOT_PATH + '/node_modules';

var __DEV__ = process.env.NODE_ENV !== 'production';

// conf
var alias = pickFiles({
  id: /(conf\/[^\/]+).js$/,
  pattern: SRC_PATH + '/conf/*.js'
});

// components
alias = Object.assign(alias, pickFiles({
  id: /(components\/[^\/]+)/,
  pattern: SRC_PATH + '/components/*/index.js'
}));

// reducers
alias = Object.assign(alias, pickFiles({
  id: /(reducers\/[^\/]+).js/,
  pattern: SRC_PATH + '/js/reducers/*'
}));

// actions
alias = Object.assign(alias, pickFiles({
  id: /(actions\/[^\/]+).js/,
  pattern: SRC_PATH + '/js/actions/*'
}));

var config = {
  context: SRC_PATH,
  entry: {
    app: ['./pages/app.js'],
    lib: [
      'react', 'react-dom', 'react-router',
      'redux', 'react-redux', 'redux-thunk'
    ],
  },
  output: {
    path: DIST_PATH,
    filename: 'js/[name].[hash].js'
  },
  module: {},
  resolve: {
    alias: alias
  },
  plugins: [
    new webpack.DefinePlugin({
      // http://stackoverflow.com/questions/30030031/passing-environment-dependent-variables-in-webpack
      "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || 'development')
    }),
    new webpack.optimize.CommonsChunkPlugin('lib', 'js/[name].[hash].js')
  ]
};

// loaders
var CACHE_PATH = ROOT_PATH + '/cache';
config.module.loaders = [];

// 使用 babel 编译 jsx、es6
config.module.loaders.push({
  test: /\.js$/,
  exclude: /node_modules/,
  include: SRC_PATH,
  // 这里使用 loaders ，因为后面还需要添加 loader
  loaders: ['babel?cacheDirectory=' + CACHE_PATH]
});

// 编译 sass
if (__DEV__) {
  config.module.loaders.push({
    test: /\.(scss|css)$/,
    loaders: ['style', 'css', 'sass', 'postcss']
  });
} else {
  config.module.loaders.push({
    test: /\.(scss|css)$/,
    loader: ExtractTextPlugin.extract('style', 'css!sass!postcss')
  });
  config.plugins.push(
    new ExtractTextPlugin('css/[name].[hash].css')
  );
}

// css autoprefix
var precss = require('precss');
var autoprefixer = require('autoprefixer');
config.postcss = function() {
  return [precss, autoprefixer];
}

// 图片路径处理，压缩
config.module.loaders.push({
  test: /\.(?:jpg|gif|png|svg)$/,
  loaders: [
    'url?limit=8000&name=img/[hash].[ext]',
    'image-webpack'
  ]
});

// html 页面
var HtmlwebpackPlugin = require('html-webpack-plugin');
config.plugins.push(
  new HtmlwebpackPlugin({
    filename: 'index.html',
    chunks: ['app', 'lib'],
    template: SRC_PATH + '/pages/app.html',
    minify: __DEV__ ? false : {
      collapseWhitespace: true,
      collapseInlineTagWhitespace: true,
      removeRedundantAttributes: true,
      removeEmptyAttributes: true,
      removeScriptTypeAttributes: true,
      removeStyleLinkTypeAttributes: true,
      removeComments: true
    }
  })
);

module.exports = config;

<|endoftext|>


<commit_before>import { combineReducers } from 'redux'
import auth from 'reducers/auth'
import { routeReducer } from 'react-router-redux'
import {reducer as formReducer} from 'redux-form'
import orderReducer from 'reducers/order'

const rootReducer = combineReducers({
    auth,
    routing: routeReducer,
    form: formReducer,
    order: orderReducer
})

export default rootReducer
<commit_msg>added the shape of the state<commit_after>import { combineReducers } from 'redux'
import auth from 'reducers/auth'
import { routeReducer } from 'react-router-redux'
import {reducer as formReducer} from 'redux-form'
import orderReducer from 'reducers/order'

const rootReducer = combineReducers({
    auth,
    routing: routeReducer,
    form: formReducer,
    order: orderReducer
})

export default rootReducer

/*
Shape of the state

{
    auth: {
        isFetching: false,
        isAuthenticated: false
    },
    routing: {...},
    form: {
        shipping: {
            asyncValidating: false,
            error: undefined,
            initialized: false,
            submitting: false,
            submitFailed: false,
            firstName: {
                visited: true,
                value: "John",
                touched: true
            },
            lastName: {..},
            email: {..}
            ...other form fields
        }
    },
    order: {
        initialized: true
        consumer: {
            firstName: '',
            lastName: '',
            email: ''
            ... other "shipping" form submitted values
        }
        shipping: {
            account: 'FedEx',
            accountNum: '748379',
            comments: ''
        }
    }
}
*/
<|endoftext|>


<commit_before>sap.ui.define(function () {

	"use strict";
	return {
		name: "Demo Apps and Tutorials in sap.tnt",
		defaults: {
			qunit: {
				version: 2
			}
		},

		tests: {
			// sap.tnt demo apps
			"sap/tnt/demokit/toolpageapp/webapp/test/integration/opaTests": {
				group: "Shop Administration Tool",
				page: "test-resources/sap/tnt/demokit/toolpageapp/webapp/test/integration/opaTests.qunit.html"
			},
			"sap/tnt/demokit/toolpageapp/webapp/test/unit/unitTests": {
				group: "Shop Administration Tool",
				page: "test-resources/sap/tnt/demokit/toolpageapp/webapp/test/integration/opaTests.qunit.html"
			}
		}
	};
});
<commit_msg>[INTERNAL][FIX] corrected path to toolpageapp demo app unit test<commit_after>sap.ui.define(function () {

	"use strict";
	return {
		name: "Demo Apps and Tutorials in sap.tnt",
		defaults: {
			qunit: {
				version: 2
			}
		},

		tests: {
			// sap.tnt demo apps
			"sap/tnt/demokit/toolpageapp/webapp/test/integration/opaTests": {
				group: "Shop Administration Tool",
				page: "test-resources/sap/tnt/demokit/toolpageapp/webapp/test/integration/opaTests.qunit.html"
			},
			"sap/tnt/demokit/toolpageapp/webapp/test/unit/unitTests": {
				group: "Shop Administration Tool",
				page: "test-resources/sap/tnt/demokit/toolpageapp/webapp/test/unit/unitTests.qunit.html"
			}
		}
	};
});
<|endoftext|>


<commit_before>// @flow
import * as expressions from "./reducers/expressions";
import * as sources from "./reducers/sources";
import * as pause from "./reducers/pause";
import * as debuggee from "./reducers/debuggee";
import * as breakpoints from "./reducers/breakpoints";
import * as pendingBreakpoints from "./reducers/pending-breakpoints";
import * as eventListeners from "./reducers/event-listeners";
import * as ui from "./reducers/ui";
import * as ast from "./reducers/ast";
import * as coverage from "./reducers/coverage";
import * as projectTextSearch from "./reducers/project-text-search";
import * as sourceSearch from "./reducers/source-search";
import * as sourceTree from "./reducers/source-tree";

import getBreakpointAtLocation from "./selectors/breakpointAtLocation";
import getInScopeLines from "./selectors/linesInScope";
import getVisibleBreakpoints from "./selectors/visibleBreakpoints";
import isSelectedFrameVisible from "./selectors/isSelectedFrameVisible";

/**
 * @param object - location
 */

module.exports = Object.assign(
  {},
  expressions,
  sources,
  pause,
  debuggee,
  breakpoints,
  pendingBreakpoints,
  eventListeners,
  ui,
  ast,
  coverage,
  projectTextSearch,
  sourceSearch,
  sourceTree,
  {
    getBreakpointAtLocation,
    getInScopeLines,
    getVisibleBreakpoints,
    isSelectedFrameVisible
  }
);
<commit_msg>Remove object.assign from selectors.js close #4267 (#4268)<commit_after>// @flow
import * as expressions from "./reducers/expressions";
import * as sources from "./reducers/sources";
import * as pause from "./reducers/pause";
import * as debuggee from "./reducers/debuggee";
import * as breakpoints from "./reducers/breakpoints";
import * as pendingBreakpoints from "./reducers/pending-breakpoints";
import * as eventListeners from "./reducers/event-listeners";
import * as ui from "./reducers/ui";
import * as ast from "./reducers/ast";
import * as coverage from "./reducers/coverage";
import * as projectTextSearch from "./reducers/project-text-search";
import * as sourceSearch from "./reducers/source-search";
import * as sourceTree from "./reducers/source-tree";

import getBreakpointAtLocation from "./selectors/breakpointAtLocation";
import getInScopeLines from "./selectors/linesInScope";
import getVisibleBreakpoints from "./selectors/visibleBreakpoints";
import isSelectedFrameVisible from "./selectors/isSelectedFrameVisible";

/**
 * @param object - location
 */

module.exports = {
  ...expressions,
  ...sources,
  ...pause,
  ...debuggee,
  ...breakpoints,
  ...pendingBreakpoints,
  ...eventListeners,
  ...ui,
  ...ast,
  ...coverage,
  ...projectTextSearch,
  ...sourceSearch,
  ...sourceTree,
  getBreakpointAtLocation,
  getInScopeLines,
  getVisibleBreakpoints,
  isSelectedFrameVisible
};
<|endoftext|>


<commit_before>var sqlite3 = require('sqlite3'),
    rs = require('recursive-search'),
    fs = require('fs'),
    path = require('path'),
    async = require('async'),
    classes = require('./classes.js');

function importDirectory(db, directory, callback) {
  console.error("Importing.");
  console.error("  - finding trace files...");
  var files = rs.recursiveSearchSync(/(.gpx|.csv)$/, directory).map(function(f, i) { return [i, f]; });

  console.error("  - adding " + files.length + " files to database...");

  db.run("CREATE TABLE traces (id INTEGER PRIMARY KEY, file TEXT)");
  db.run("CREATE TABLE matchings (id INTEGER, subIdx INTEGER, cls INTEGER, data TEXT, PRIMARY KEY (id, subIdx))");

  db.run("BEGIN TRANSACTION");
  var traceStatement = db.prepare("INSERT INTO traces(id, file) VALUES (?, ?)");
  files.forEach(function(file) { traceStatement.run(file[0], file[1]); });
  traceStatement.finalize();
  db.run("END TRANSACTION");

  db.run("BEGIN TRANSACTION");
  var matchingStatement = db.prepare("INSERT INTO matchings(id, subIdx, cls) VALUES (?, ?, ?)");
  files.forEach(function(file) { matchingStatement.run(file[0], 0, 0); });
  matchingStatement.finalize();
  db.run("END TRANSACTION");

  console.error("done.");
}

function loadDB(directory) {
  var targetFilename = path.join(directory, 'classification_db.sqlite'),
      db = new sqlite3.Database(targetFilename);

  // run in sequential mode to avoid callback hell
  db.serialize();

  if (!fs.existsSync(targetFilename)) {
    importDirectory(db, directory);
  }

  return db;
}

module.exports = loadDB;
<commit_msg>Check if we really need recursive directory listing<commit_after>var sqlite3 = require('sqlite3'),
    rs = require('recursive-search'),
    fs = require('fs'),
    path = require('path'),
    async = require('async'),
    classes = require('./classes.js');

function importDirectory(db, directory, callback) {
  console.error("Importing.");
  console.error("  - finding trace files...");
  var exp = /(.gpx|.csv)$/;
  var files = fs.readdirSync(directory).filter(function (f) { return exp.exec(f); }).map(function(f) { return path.join(directory, f); });
  // check if we need to do it recursively: WARNING does not scale for some reason.
  if (files.filter(function(f) { return fs.statSync(f).isDirectory();}).length > 0)
  {
    files = rs.recursiveSearchSync(exp, directory);
  }

  files = files.map(function(f, i) { return [i, f]; });

  console.error("  - adding " + files.length + " files to database...");

  db.run("CREATE TABLE traces (id INTEGER PRIMARY KEY, file TEXT)");
  db.run("CREATE TABLE matchings (id INTEGER, subIdx INTEGER, cls INTEGER, data TEXT, PRIMARY KEY (id, subIdx))");

  db.run("BEGIN TRANSACTION");
  var traceStatement = db.prepare("INSERT INTO traces(id, file) VALUES (?, ?)");
  files.forEach(function(file) { traceStatement.run(file[0], file[1]); });
  traceStatement.finalize();
  db.run("END TRANSACTION");

  db.run("BEGIN TRANSACTION");
  var matchingStatement = db.prepare("INSERT INTO matchings(id, subIdx, cls) VALUES (?, ?, ?)");
  files.forEach(function(file) { matchingStatement.run(file[0], 0, 0); });
  matchingStatement.finalize();
  db.run("END TRANSACTION");

  console.error("done.");
}

function loadDB(directory) {
  var targetFilename = path.join(directory, 'classification_db.sqlite'),
      db = new sqlite3.Database(targetFilename);

  // run in sequential mode to avoid callback hell
  db.serialize();

  if (!fs.existsSync(targetFilename)) {
    importDirectory(db, directory);
  }

  return db;
}

module.exports = loadDB;
<|endoftext|>


<commit_before>import React from 'react';

import LoadingSpinner from '../utility/loading-spinner';
import PostItem from './post-item';

class PostList extends React.Component {
  shouldComponentUpdate(nextProps) {
    return this.props.postsReady !== nextProps.postsReady;
  }

  render() {
    const { postsReady, posts } = this.props;

    const postList = (

        {posts.map((post, i) => <PostItem key={i} post={post} />)}

    );

    return postsReady ? postList : <LoadingSpinner />;
  }
}

PostList.propTypes = {
  postsReady: React.PropTypes.bool.isRequired,
  posts: React.PropTypes.array.isRequired,
};

export default PostList;
<commit_msg>Implement first draft of infinite scrolling for feeds<commit_after>import React from 'react';

import LoadingSpinner from '../utility/loading-spinner';
import PostItem from './post-item';

class PostList extends React.Component {
  componentDidMount() {
    const component = this;

    $(window).on('scroll', function () {
      const { limit, postsReady } = component.props;

      if (postsReady) {
        const $window = $(this);
        const $getMorePosts = $('#get-more-posts');

        const scrollBottom = $window.scrollTop() + $window.height();
        const getMorePostsPosition = $getMorePosts.position().top;
        const distanceToBottom = getMorePostsPosition - scrollBottom;

        if (distanceToBottom <= 50) {
          const currentLimit = limit.get();

          limit.set(currentLimit + 10);
        }
      }
    });
  }

  shouldComponentUpdate(nextProps) {
    return this.props.postsReady !== nextProps.postsReady;
  }

  componentWillUpdate(nextProps) {
    const { limit, postsReady, posts } = nextProps;

    if (postsReady && limit.get() > posts.length) {
      $(window).off('scroll');
    }
  }

  componentWillUnmount() {
    $(window).off('scroll');

    this.props.limit.set(10);
  }

  render() {
    const { postsReady, posts } = this.props;

    return (

        {posts.map((post, i) => <PostItem key={i} post={post} />)}

          {postsReady ? null : <LoadingSpinner />}

    );
  }
}

PostList.propTypes = {
  limit: React.PropTypes.object.isRequired,
  postsReady: React.PropTypes.bool.isRequired,
  posts: React.PropTypes.array.isRequired,
};

export default PostList;
<|endoftext|>


<commit_before>// $("td.instructor").each(function() {
    // var professorName = $(this).text();
    // if (professorName != ""
        // && rmpData.hasOwnProperty(professorName)) {
        // data = rmpData[professorName];
        // $(this).append(" (" + data.a + ")");
    // }
// });

var SERVER_URL = "http://statm.github.io/";

var localVersion = localStorage["data_ver"];
var remoteVersion = $.ajax({url: SERVER_URL + "version", async: false}).responseText;

console.log("local=" + ", remote=" + remoteVersion);

if (localVersion === undefined || parseInt(remoteVersion) > parseInt(localVersion)) {
    updateData();
}

function updateData() {
    $.ajax({
        url: "http://statm.github.io/professor-ratings/data.json",
        success: function(result) {
            localStorage["data"] = JSON.stringify(result);
            console.log(JSON.parse(localStorage["data"]));
        }
    });
}<commit_msg>Chrome: content script refactored<commit_after>var SERVER_URL = "http://statm.github.io/professor-ratings/";

var localVersion = localStorage["version"];
var remoteVersion = $.ajax({url: SERVER_URL + "version", async: false}).responseText;

console.log("local=" + localVersion + ", remote=" + remoteVersion);

if (localVersion === undefined || parseInt(remoteVersion) > parseInt(localVersion)) {
    console.log("update required");
    updateData();
}

var rmpData = JSON.parse(localStorage["data"]);

$("td.instructor").each(function() {
    var professorName = $(this).text();
    if (professorName != ""
        && rmpData.hasOwnProperty(professorName)) {
        data = rmpData[professorName];
        $(this).append(" (" + data.a + ")");
    }
});

function updateData() {
    $.ajax({
        url: SERVER_URL + data.json",
        async: false,
        success: function(result) {
            localStorage["data"] = JSON.stringify(result);
            localStorage["version"] = remoteVersion;
            console.log("update done");
        }
    });
}<|endoftext|>


<commit_before>import scrollbarWidth from 'scrollbarwidth';
import debounce from 'lodash.debounce';
import ResizeObserver from 'resize-observer-polyfill';

import './simplebar.css';

export default class SimpleBar {
    constructor(element, options) {
        this.el = element;
        this.track;
        this.scrollbar;
        this.flashTimeout;
        this.contentEl;
        this.scrollContentEl;
        this.dragOffset         = { x: 0, y: 0 };
        this.isVisible          = { x: true, y: true };
        this.scrollOffsetAttr   = { x: 'scrollLeft', y: 'scrollTop' };
        this.sizeAttr           = { x: 'offsetWidth', y: 'offsetHeight' };
        this.scrollSizeAttr     = { x: 'scrollWidth', y: 'scrollHeight' };
        this.offsetAttr         = { x: 'left', y: 'top' };
        this.observer;
        this.currentAxis;
        this.options = Object.assign({}, SimpleBar.defaultOptions, options);
        this.classNames = this.options.classNames;
        this.scrollbarWidth = scrollbarWidth();
        this.offsetSize = 15;
        // If scrollbar is a floating scrollbar, disable the plugin
        this.enabled = this.scrollbarWidth !== 0 || this.options.forceEnabled;

        this.flashScrollbar = this.flashScrollbar.bind(this);
        this.onScrollY = this.onScrollY.bind(this);
        this.onScrollX = this.onScrollX.bind(this);
        this.onDrag = this.onDrag.bind(this);
        this.drag = this.drag.bind(this);
        this.onEndDrag = this.onEndDrag.bind(this);
        this.onMouseEnter = this.onMouseEnter.bind(this);

        this.recalculate = debounce(this.recalculate, 100, { leading: true, trailing: false });

        this.init();
    }

    static get defaultOptions() {
        return {
            wrapContent: true,
            autoHide: true,
            forceEnabled: false,
            classNames: {
                content: 'simplebar-content',
                scrollContent: 'simplebar-scroll-content',
                scrollbar: 'simplebar-scrollbar',
                track: 'simplebar-track'
            },
            scrollbarMinSize: 10
        }
    }

    static get htmlAttributes() {
        return {
            autoHide: 'data-simplebar-autohide',
            forceEnabled: 'data-simplebar-force-enabled',
            scrollbarMinSize: 'data-simplebar-scrollbar-min-size'
        }
    }

    static initHtmlApi() {
        this.initDOMLoadedElements = this.initDOMLoadedElements.bind(this);

        // MutationObserver is IE11+
        if (typeof MutationObserver !== 'undefined') {
            // Mutation observer to observe dynamically added elements
            const globalObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    Array.from(mutation.addedNodes).forEach(addedNode => {
                        if (addedNode.nodeType === 1) {
                            if (addedNode.SimpleBar) return;

                            if (addedNode.hasAttribute('data-simplebar')) {
                                new SimpleBar(addedNode, SimpleBar.getElOptions(addedNode));
                            } else {
                                Array.from(addedNode.querySelectorAll('[data-simplebar]')).forEach(el => {
                                    new SimpleBar(el, SimpleBar.getElOptions(el));
                                });
                            }
                        }
                    });

                    Array.from(mutation.removedNodes).forEach(removedNode => {
                        if (removedNode.nodeType === 1) {
                            if (removedNode.hasAttribute('data-simplebar')) {
                                removedNode.SimpleBar && removedNode.SimpleBar.unMount();
                            } else {
                                Array.from(removedNode.querySelectorAll('[data-simplebar]')).forEach(el => {
                                    el.SimpleBar && el.SimpleBar.unMount();
                                });
                            }
                        }
                    });
                });
            });

            globalObserver.observe(document, { childList: true, subtree: true });
        }

        // Taken from jQuery `ready` function
        // Instantiate elements already present on the page
        if (document.readyState === 'complete' ||
                (document.readyState !== 'loading' && !document.documentElement.doScroll)) {
            // Handle it asynchronously to allow scripts the opportunity to delay init
            window.setTimeout(this.initDOMLoadedElements.bind(this));
        } else {
            document.addEventListener('DOMContentLoaded', this.initDOMLoadedElements);
            window.addEventListener('load', this.initDOMLoadedElements);
        }
    }

    // Helper function to retrieve options from element attributes
    static getElOptions(el) {
        const options = Object.keys(SimpleBar.htmlAttributes).reduce((acc, obj) => {
            const attribute = SimpleBar.htmlAttributes[obj];
            if (el.hasAttribute(attribute)) {
                acc[obj] = JSON.parse(el.getAttribute(attribute) || true);
            }
            return acc;
        }, {});

        return options;
    }

    static removeObserver() {
        this.observer && this.observer.disconnect();
    }

    static initDOMLoadedElements() {
        document.removeEventListener('DOMContentLoaded', this.initDOMLoadedElements);
        window.removeEventListener('load', this.initDOMLoadedElements);

        Array.from(document.querySelectorAll('[data-simplebar]')).forEach(el => {
            if (!el.SimpleBar)
                new SimpleBar(el, SimpleBar.getElOptions(el));
        });
    }

    init() {
        // Save a reference to the instance, so we know this DOM node has already been instancied
        this.el.SimpleBar = this;

        if (!this.enabled) {
            return;
        }

        this.initDOM();

        this.scrollContentEl = this.el.querySelector(`.${this.classNames.scrollContent}`);
        this.contentEl = this.el.querySelector(`.${this.classNames.content}`);

        this.trackX = this.el.querySelector(`.${this.classNames.track}.horizontal`);
        this.trackY = this.el.querySelector(`.${this.classNames.track}.vertical`);

        this.scrollbarX = this.trackX.querySelector(`.${this.classNames.scrollbar}`);
        this.scrollbarY = this.trackY.querySelector(`.${this.classNames.scrollbar}`);

        this.scrollContentEl.style.marginRight = `-${this.offsetSize}px`;
        this.scrollContentEl.style.paddingRight = `${this.offsetSize}px`;
        this.scrollContentEl.style.marginBottom = `-${this.offsetSize}px`;
        this.contentEl.style.paddingBottom = `${this.offsetSize}px`;

        if (this.enabled && this.scrollbarWidth !== 0) {
            this.scrollContentEl.style.marginBottom = `-${this.offsetSize*2}px`;
            this.contentEl.style.marginRight = `-${this.offsetSize}px`;
        }

        // Calculate content size
        this.recalculate();

        if (!this.options.autoHide) {
            this.showScrollbar('x');
            this.showScrollbar('y');
        }

        this.initListeners();
    }

    initDOM() {
        // Prepare DOM
        if (this.options.wrapContent) {
            this.scrollContentEl = document.createElement('div');
            this.contentEl = document.createElement('div');

            this.scrollContentEl.classList.add(this.classNames.scrollContent);
            this.contentEl.classList.add(this.classNames.content);

            while (this.el.firstChild)
                this.contentEl.appendChild(this.el.firstChild)

            this.scrollContentEl.appendChild(this.contentEl);
            this.el.appendChild(this.scrollContentEl);
        }

        const track = document.createElement('div');
        const scrollbar = document.createElement('div');

        track.classList.add(this.classNames.track);
        scrollbar.classList.add(this.classNames.scrollbar);

        track.appendChild(scrollbar);

        this.trackX = track.cloneNode(true);
        this.trackX.classList.add('horizontal');

        this.trackY = track.cloneNode(true);
        this.trackY.classList.add('vertical');

        this.el.insertBefore(this.trackX, this.el.firstChild);
        this.el.insertBefore(this.trackY, this.el.firstChild);
        this.el.setAttribute('data-simplebar', 'init');
    }

    initListeners() {
        // Event listeners
        if (this.options.autoHide) {
            this.el.addEventListener('mouseenter', this.onMouseEnter);
        }

        this.scrollbarX.addEventListener('mousedown', (e) => this.onDrag(e, 'x'));
        this.scrollbarY.addEventListener('mousedown', (e) => this.onDrag(e, 'y'));

        this.scrollContentEl.addEventListener('scroll', this.onScrollY);
        this.contentEl.addEventListener('scroll', this.onScrollX);

        // MutationObserver is IE11+
        if (typeof MutationObserver !== 'undefined') {
            // create an observer instance
            this.mutationObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    if (this.isChildNode(mutation.target) || mutation.addedNodes.length) {
                        this.recalculate();
                    }
                });
            });

            // pass in the target node, as well as the observer options
            this.mutationObserver.observe(this.el, { attributes: true, childList: true, characterData: true, subtree: true });
        }

        this.resizeObserver = new ResizeObserver(this.recalculate.bind(this));

        this.resizeObserver.observe(this.el);
    }

    removeListeners() {
        if (!this.enabled) {
            return;
        }

        // Event listeners
        if (this.options.autoHide) {
            this.el.removeEventListener('mouseenter', this.onMouseEnter);
        }

        this.scrollbarX.removeEventListener('mousedown', (e) => this.onDrag(e, 'x'));
        this.scrollbarY.removeEventListener('mousedown', (e) => this.onDrag(e, 'y'));

        this.scrollContentEl.removeEventListener('scroll', this.onScrollY);
        this.contentEl.removeEventListener('scroll', this.onScrollX);

        this.observer && this.observer.disconnect();
    }

    /**
     * on scrollbar handle drag
     */
    onDrag(e, axis = 'y') {
        // Preventing the event's default action stops text being
        // selectable during the drag.
        e.preventDefault()

        let scrollbar = axis === 'y' ? this.scrollbarY : this.scrollbarX;
        // Measure how far the user's mouse is from the top of the scrollbar drag handle.
        let eventOffset = axis === 'y' ? e.pageY : e.pageX;

        this.dragOffset[axis] = eventOffset - scrollbar.getBoundingClientRect()[this.offsetAttr[axis]];
        this.currentAxis = axis;

        document.addEventListener('mousemove', this.drag);
        document.addEventListener('mouseup', this.onEndDrag);
    }

    /**
     * Drag scrollbar handle
     */
    drag(e) {
        e.preventDefault();

        let eventOffset = this.currentAxis === 'y' ? e.pageY : e.pageX;
        let track = this.currentAxis === 'y' ? this.trackY : this.trackX;

        // Calculate how far the user's mouse is from the top/left of the scrollbar (minus the dragOffset).
        let dragPos = eventOffset - track.getBoundingClientRect()[this.offsetAttr[this.currentAxis]] - this.dragOffset[this.currentAxis];

        // Convert the mouse position into a percentage of the scrollbar height/width.
        let dragPerc = dragPos / track[this.sizeAttr[this.currentAxis]];

        // Scroll the content by the same percentage.
        let scrollPos = dragPerc * this.contentEl[this.scrollSizeAttr[this.currentAxis]];

        this.scrollContentEl[this.scrollOffsetAttr[this.currentAxis]] = scrollPos;
    }

    /**
     * End scroll handle drag
     */
    onEndDrag() {
        document.removeEventListener('mousemove', this.drag);
        document.removeEventListener('mouseup', this.onEndDrag);
    }

    /**
     * Resize scrollbar
     */
    resizeScrollbar(axis = 'y') {
        let track;
        let scrollbar;
        let scrollOffset;
        let contentSize;

        if (axis === 'x') {
            track = this.trackX;
            scrollbar = this.scrollbarX;
            scrollOffset = this.contentEl[this.scrollOffsetAttr[axis]]; // Either scrollTop() or scrollLeft().
            contentSize = this.contentEl[this.scrollSizeAttr[axis]];
        } else { // 'y'
            track = this.trackY;
            scrollbar = this.scrollbarY;
            scrollOffset = this.scrollContentEl[this.scrollOffsetAttr[axis]]; // Either scrollTop() or scrollLeft().
            contentSize = this.contentEl[this.scrollSizeAttr[axis]] - 15;
        }

        let scrollbarSize   = track[this.sizeAttr[axis]];
        let scrollbarRatio  = scrollbarSize / contentSize;
        let scrollPourcent  = scrollOffset / (contentSize - scrollbarSize);
            // Calculate new height/position of drag handle.
            // Offset of 2px allows for a small top/bottom or left/right margin around handle.
        let handleSize      = Math.max(Math.floor(scrollbarRatio * (scrollbarSize - 2)) - 2, this.options.scrollbarMinSize);
        let handleOffset    = (scrollbarSize - 4 - handleSize) * scrollPourcent + 2;

        // Set isVisible to false if scrollbar is not necessary (content is shorter than wrapper)
        this.isVisible[axis] = scrollbarSize < contentSize

        if (this.isVisible[axis]) {
            track.style.visibility = 'visible';

            if (axis === 'x') {
                scrollbar.style.left = `${handleOffset}px`;
                scrollbar.style.width = `${handleSize}px`;
            } else {
                scrollbar.style.top = `${handleOffset}px`;
                scrollbar.style.height = `${handleSize}px`;
            }
        } else {
            track.style.visibility = 'hidden';
        }
    }

    /**
     * On scroll event handling
     */
    onScrollX() {
        this.flashScrollbar('x');
    }

    onScrollY() {
        this.flashScrollbar('y');
    }

    /**
     * On mouseenter event handling
     */
    onMouseEnter() {
        this.flashScrollbar('x');
        this.flashScrollbar('y');
    }

    /**
     * Flash scrollbar visibility
     */
    flashScrollbar(axis = 'y') {
        this.resizeScrollbar(axis);
        this.showScrollbar(axis);
    }

    /**
     * Show scrollbar
     */
    showScrollbar(axis = 'y') {
        if (!this.isVisible[axis]) {
            return
        }

        if (axis === 'x') {
            this.scrollbarX.classList.add('visible');
        } else {
            this.scrollbarY.classList.add('visible');
        }

        if (!this.options.autoHide) {
            return
        }
        if(typeof this.flashTimeout === 'number') {
            window.clearTimeout(this.flashTimeout);
        }

        this.flashTimeout = window.setTimeout(this.hideScrollbar.bind(this), 1000);
    }

    /**
     * Hide Scrollbar
     */
    hideScrollbar() {
        this.scrollbarX.classList.remove('visible');
        this.scrollbarY.classList.remove('visible');

        if(typeof this.flashTimeout === 'number') {
            window.clearTimeout(this.flashTimeout);
        }
    }

    /**
     * Recalculate scrollbar
     */
    recalculate() {
        if (!this.enabled) return;

        this.resizeScrollbar('x');
        this.resizeScrollbar('y');
    }

    /**
     * Getter for original scrolling element
     */
    getScrollElement() {
        return this.scrollContentEl;
    }

    /**
     * Getter for content element
     */
    getContentElement() {
        return this.contentEl;
    }

    /**
     * UnMount mutation observer and delete SimpleBar instance from DOM element
     */
    unMount() {
        this.removeListeners();
        this.el.SimpleBar = null;
    }

    /**
     * Recursively walks up the parent nodes looking for this.el
     */
    isChildNode(el) {
        if (el === null) return false;
        if (el === this.el) return true;

        return this.isChildNode(el.parentNode);
    }
}

/**
 * HTML API
 */
SimpleBar.initHtmlApi();
<commit_msg>round the values of scrollbar position to improve performances on Firefox<commit_after>import scrollbarWidth from 'scrollbarwidth';
import debounce from 'lodash.debounce';
import ResizeObserver from 'resize-observer-polyfill';

import './simplebar.css';

export default class SimpleBar {
    constructor(element, options) {
        this.el = element;
        this.track;
        this.scrollbar;
        this.flashTimeout;
        this.contentEl;
        this.scrollContentEl;
        this.dragOffset         = { x: 0, y: 0 };
        this.isVisible          = { x: true, y: true };
        this.scrollOffsetAttr   = { x: 'scrollLeft', y: 'scrollTop' };
        this.sizeAttr           = { x: 'offsetWidth', y: 'offsetHeight' };
        this.scrollSizeAttr     = { x: 'scrollWidth', y: 'scrollHeight' };
        this.offsetAttr         = { x: 'left', y: 'top' };
        this.observer;
        this.currentAxis;
        this.options = Object.assign({}, SimpleBar.defaultOptions, options);
        this.classNames = this.options.classNames;
        this.scrollbarWidth = scrollbarWidth();
        this.offsetSize = 15;
        // If scrollbar is a floating scrollbar, disable the plugin
        this.enabled = this.scrollbarWidth !== 0 || this.options.forceEnabled;

        this.flashScrollbar = this.flashScrollbar.bind(this);
        this.onScrollY = this.onScrollY.bind(this);
        this.onScrollX = this.onScrollX.bind(this);
        this.onDrag = this.onDrag.bind(this);
        this.drag = this.drag.bind(this);
        this.onEndDrag = this.onEndDrag.bind(this);
        this.onMouseEnter = this.onMouseEnter.bind(this);

        this.recalculate = debounce(this.recalculate, 100, { leading: true, trailing: false });

        this.init();
    }

    static get defaultOptions() {
        return {
            wrapContent: true,
            autoHide: true,
            forceEnabled: false,
            classNames: {
                content: 'simplebar-content',
                scrollContent: 'simplebar-scroll-content',
                scrollbar: 'simplebar-scrollbar',
                track: 'simplebar-track'
            },
            scrollbarMinSize: 10
        }
    }

    static get htmlAttributes() {
        return {
            autoHide: 'data-simplebar-autohide',
            forceEnabled: 'data-simplebar-force-enabled',
            scrollbarMinSize: 'data-simplebar-scrollbar-min-size'
        }
    }

    static initHtmlApi() {
        this.initDOMLoadedElements = this.initDOMLoadedElements.bind(this);

        // MutationObserver is IE11+
        if (typeof MutationObserver !== 'undefined') {
            // Mutation observer to observe dynamically added elements
            const globalObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    Array.from(mutation.addedNodes).forEach(addedNode => {
                        if (addedNode.nodeType === 1) {
                            if (addedNode.SimpleBar) return;

                            if (addedNode.hasAttribute('data-simplebar')) {
                                new SimpleBar(addedNode, SimpleBar.getElOptions(addedNode));
                            } else {
                                Array.from(addedNode.querySelectorAll('[data-simplebar]')).forEach(el => {
                                    new SimpleBar(el, SimpleBar.getElOptions(el));
                                });
                            }
                        }
                    });

                    Array.from(mutation.removedNodes).forEach(removedNode => {
                        if (removedNode.nodeType === 1) {
                            if (removedNode.hasAttribute('data-simplebar')) {
                                removedNode.SimpleBar && removedNode.SimpleBar.unMount();
                            } else {
                                Array.from(removedNode.querySelectorAll('[data-simplebar]')).forEach(el => {
                                    el.SimpleBar && el.SimpleBar.unMount();
                                });
                            }
                        }
                    });
                });
            });

            globalObserver.observe(document, { childList: true, subtree: true });
        }

        // Taken from jQuery `ready` function
        // Instantiate elements already present on the page
        if (document.readyState === 'complete' ||
                (document.readyState !== 'loading' && !document.documentElement.doScroll)) {
            // Handle it asynchronously to allow scripts the opportunity to delay init
            window.setTimeout(this.initDOMLoadedElements.bind(this));
        } else {
            document.addEventListener('DOMContentLoaded', this.initDOMLoadedElements);
            window.addEventListener('load', this.initDOMLoadedElements);
        }
    }

    // Helper function to retrieve options from element attributes
    static getElOptions(el) {
        const options = Object.keys(SimpleBar.htmlAttributes).reduce((acc, obj) => {
            const attribute = SimpleBar.htmlAttributes[obj];
            if (el.hasAttribute(attribute)) {
                acc[obj] = JSON.parse(el.getAttribute(attribute) || true);
            }
            return acc;
        }, {});

        return options;
    }

    static removeObserver() {
        this.observer && this.observer.disconnect();
    }

    static initDOMLoadedElements() {
        document.removeEventListener('DOMContentLoaded', this.initDOMLoadedElements);
        window.removeEventListener('load', this.initDOMLoadedElements);

        Array.from(document.querySelectorAll('[data-simplebar]')).forEach(el => {
            if (!el.SimpleBar)
                new SimpleBar(el, SimpleBar.getElOptions(el));
        });
    }

    init() {
        // Save a reference to the instance, so we know this DOM node has already been instancied
        this.el.SimpleBar = this;

        if (!this.enabled) {
            return;
        }

        this.initDOM();

        this.scrollContentEl = this.el.querySelector(`.${this.classNames.scrollContent}`);
        this.contentEl = this.el.querySelector(`.${this.classNames.content}`);

        this.trackX = this.el.querySelector(`.${this.classNames.track}.horizontal`);
        this.trackY = this.el.querySelector(`.${this.classNames.track}.vertical`);

        this.scrollbarX = this.trackX.querySelector(`.${this.classNames.scrollbar}`);
        this.scrollbarY = this.trackY.querySelector(`.${this.classNames.scrollbar}`);

        this.scrollContentEl.style.marginRight = `-${this.offsetSize}px`;
        this.scrollContentEl.style.paddingRight = `${this.offsetSize}px`;
        this.scrollContentEl.style.marginBottom = `-${this.offsetSize}px`;
        this.contentEl.style.paddingBottom = `${this.offsetSize}px`;

        if (this.enabled && this.scrollbarWidth !== 0) {
            this.scrollContentEl.style.marginBottom = `-${this.offsetSize*2}px`;
            this.contentEl.style.marginRight = `-${this.offsetSize}px`;
        }

        // Calculate content size
        this.recalculate();

        if (!this.options.autoHide) {
            this.showScrollbar('x');
            this.showScrollbar('y');
        }

        this.initListeners();
    }

    initDOM() {
        // Prepare DOM
        if (this.options.wrapContent) {
            this.scrollContentEl = document.createElement('div');
            this.contentEl = document.createElement('div');

            this.scrollContentEl.classList.add(this.classNames.scrollContent);
            this.contentEl.classList.add(this.classNames.content);

            while (this.el.firstChild)
                this.contentEl.appendChild(this.el.firstChild)

            this.scrollContentEl.appendChild(this.contentEl);
            this.el.appendChild(this.scrollContentEl);
        }

        const track = document.createElement('div');
        const scrollbar = document.createElement('div');

        track.classList.add(this.classNames.track);
        scrollbar.classList.add(this.classNames.scrollbar);

        track.appendChild(scrollbar);

        this.trackX = track.cloneNode(true);
        this.trackX.classList.add('horizontal');

        this.trackY = track.cloneNode(true);
        this.trackY.classList.add('vertical');

        this.el.insertBefore(this.trackX, this.el.firstChild);
        this.el.insertBefore(this.trackY, this.el.firstChild);
        this.el.setAttribute('data-simplebar', 'init');
    }

    initListeners() {
        // Event listeners
        if (this.options.autoHide) {
            this.el.addEventListener('mouseenter', this.onMouseEnter);
        }

        this.scrollbarX.addEventListener('mousedown', (e) => this.onDrag(e, 'x'));
        this.scrollbarY.addEventListener('mousedown', (e) => this.onDrag(e, 'y'));

        this.scrollContentEl.addEventListener('scroll', this.onScrollY);
        this.contentEl.addEventListener('scroll', this.onScrollX);

        // MutationObserver is IE11+
        if (typeof MutationObserver !== 'undefined') {
            // create an observer instance
            this.mutationObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    if (this.isChildNode(mutation.target) || mutation.addedNodes.length) {
                        this.recalculate();
                    }
                });
            });

            // pass in the target node, as well as the observer options
            this.mutationObserver.observe(this.el, { attributes: true, childList: true, characterData: true, subtree: true });
        }

        this.resizeObserver = new ResizeObserver(this.recalculate.bind(this));

        this.resizeObserver.observe(this.el);
    }

    removeListeners() {
        if (!this.enabled) {
            return;
        }

        // Event listeners
        if (this.options.autoHide) {
            this.el.removeEventListener('mouseenter', this.onMouseEnter);
        }

        this.scrollbarX.removeEventListener('mousedown', (e) => this.onDrag(e, 'x'));
        this.scrollbarY.removeEventListener('mousedown', (e) => this.onDrag(e, 'y'));

        this.scrollContentEl.removeEventListener('scroll', this.onScrollY);
        this.contentEl.removeEventListener('scroll', this.onScrollX);

        this.observer && this.observer.disconnect();
    }

    /**
     * on scrollbar handle drag
     */
    onDrag(e, axis = 'y') {
        // Preventing the event's default action stops text being
        // selectable during the drag.
        e.preventDefault()

        let scrollbar = axis === 'y' ? this.scrollbarY : this.scrollbarX;
        // Measure how far the user's mouse is from the top of the scrollbar drag handle.
        let eventOffset = axis === 'y' ? e.pageY : e.pageX;

        this.dragOffset[axis] = eventOffset - scrollbar.getBoundingClientRect()[this.offsetAttr[axis]];
        this.currentAxis = axis;

        document.addEventListener('mousemove', this.drag);
        document.addEventListener('mouseup', this.onEndDrag);
    }

    /**
     * Drag scrollbar handle
     */
    drag(e) {
        e.preventDefault();

        let eventOffset = this.currentAxis === 'y' ? e.pageY : e.pageX;
        let track = this.currentAxis === 'y' ? this.trackY : this.trackX;

        // Calculate how far the user's mouse is from the top/left of the scrollbar (minus the dragOffset).
        let dragPos = eventOffset - track.getBoundingClientRect()[this.offsetAttr[this.currentAxis]] - this.dragOffset[this.currentAxis];

        // Convert the mouse position into a percentage of the scrollbar height/width.
        let dragPerc = dragPos / track[this.sizeAttr[this.currentAxis]];

        // Scroll the content by the same percentage.
        let scrollPos = dragPerc * this.contentEl[this.scrollSizeAttr[this.currentAxis]];

        this.scrollContentEl[this.scrollOffsetAttr[this.currentAxis]] = scrollPos;
    }

    /**
     * End scroll handle drag
     */
    onEndDrag() {
        document.removeEventListener('mousemove', this.drag);
        document.removeEventListener('mouseup', this.onEndDrag);
    }

    /**
     * Resize scrollbar
     */
    resizeScrollbar(axis = 'y') {
        let track;
        let scrollbar;
        let scrollOffset;
        let contentSize;

        if (axis === 'x') {
            track = this.trackX;
            scrollbar = this.scrollbarX;
            scrollOffset = this.contentEl[this.scrollOffsetAttr[axis]]; // Either scrollTop() or scrollLeft().
            contentSize = this.contentEl[this.scrollSizeAttr[axis]];
        } else { // 'y'
            track = this.trackY;
            scrollbar = this.scrollbarY;
            scrollOffset = this.scrollContentEl[this.scrollOffsetAttr[axis]]; // Either scrollTop() or scrollLeft().
            contentSize = this.contentEl[this.scrollSizeAttr[axis]] - 15;
        }

        let scrollbarSize   = track[this.sizeAttr[axis]];
        let scrollbarRatio  = scrollbarSize / contentSize;
        let scrollPourcent  = scrollOffset / (contentSize - scrollbarSize);
            // Calculate new height/position of drag handle.
            // Offset of 2px allows for a small top/bottom or left/right margin around handle.
        let handleSize      = Math.max(Math.floor(scrollbarRatio * (scrollbarSize - 2)) - 2, this.options.scrollbarMinSize);
        let handleOffset    = (scrollbarSize - 4 - handleSize) * scrollPourcent + 2;

        // Set isVisible to false if scrollbar is not necessary (content is shorter than wrapper)
        this.isVisible[axis] = scrollbarSize < contentSize

        if (this.isVisible[axis]) {
            track.style.visibility = 'visible';

            if (axis === 'x') {
                scrollbar.style.left = `${~~handleOffset}px`;
                scrollbar.style.width = `${~~handleSize}px`;
            } else {
                scrollbar.style.top = `${~~handleOffset}px`;
                scrollbar.style.height = `${~~handleSize}px`;
            }
        } else {
            track.style.visibility = 'hidden';
        }
    }

    /**
     * On scroll event handling
     */
    onScrollX() {
        this.flashScrollbar('x');
    }

    onScrollY() {
        this.flashScrollbar('y');
    }

    /**
     * On mouseenter event handling
     */
    onMouseEnter() {
        this.flashScrollbar('x');
        this.flashScrollbar('y');
    }

    /**
     * Flash scrollbar visibility
     */
    flashScrollbar(axis = 'y') {
        this.resizeScrollbar(axis);
        this.showScrollbar(axis);
    }

    /**
     * Show scrollbar
     */
    showScrollbar(axis = 'y') {
        if (!this.isVisible[axis]) {
            return
        }

        if (axis === 'x') {
            this.scrollbarX.classList.add('visible');
        } else {
            this.scrollbarY.classList.add('visible');
        }

        if (!this.options.autoHide) {
            return
        }
        if(typeof this.flashTimeout === 'number') {
            window.clearTimeout(this.flashTimeout);
        }

        this.flashTimeout = window.setTimeout(this.hideScrollbar.bind(this), 1000);
    }

    /**
     * Hide Scrollbar
     */
    hideScrollbar() {
        this.scrollbarX.classList.remove('visible');
        this.scrollbarY.classList.remove('visible');

        if(typeof this.flashTimeout === 'number') {
            window.clearTimeout(this.flashTimeout);
        }
    }

    /**
     * Recalculate scrollbar
     */
    recalculate() {
        if (!this.enabled) return;

        this.resizeScrollbar('x');
        this.resizeScrollbar('y');
    }

    /**
     * Getter for original scrolling element
     */
    getScrollElement() {
        return this.scrollContentEl;
    }

    /**
     * Getter for content element
     */
    getContentElement() {
        return this.contentEl;
    }

    /**
     * UnMount mutation observer and delete SimpleBar instance from DOM element
     */
    unMount() {
        this.removeListeners();
        this.el.SimpleBar = null;
    }

    /**
     * Recursively walks up the parent nodes looking for this.el
     */
    isChildNode(el) {
        if (el === null) return false;
        if (el === this.el) return true;

        return this.isChildNode(el.parentNode);
    }
}

/**
 * HTML API
 */
SimpleBar.initHtmlApi();
<|endoftext|>


<commit_before>var stream = require('../stream');
var PluginError = require('gulp-util').PluginError;
var _ = require('lodash');

module.exports = function (opts) {
  opts = opts || {};

  // @type false|[]paths - paths to files that failed jshint
  var fails = false;

  // @type false|[]files - files that need to be passed downstream on flush
  var buffer = opts.buffer !== false ? [] : false;

  // @type object - count of jshint error, warning and info messages
  var messages = {error: 0, warning: 0, info: 0};

  return stream(
    function through(file) {
      // count error, warning and info messages
      if (file.jshint && file.jshint.results) {
        messages = _.reduce(file.jshint.results, function(result, err) {
          return {
            error: result.error + Number(err.error.code[0] === 'E'),
            warning: result.warning + Number(err.error.code[0] === 'W'),
            info: result.info + Number(err.error.code[0] === 'I')
          };
        }, messages);
      }

      // check for failure
      if (file.jshint && !file.jshint.success && !file.jshint.ignored) {
        (fails = fails || []).push(file.path);
      }

      // buffer or pass downstream
      (buffer || this).push(file);
    }, function flush() {
      var failOnWarning = !opts.ignoreWarning && messages.warning;
      var failOnInfo = !opts.ignoreInfo && messages.info;
      if (fails && (failOnWarning || failOnInfo)) {
        this.emit('error', new PluginError('gulp-jshint', {
          message: 'JSHint failed for: ' + fails.join(', '),
          showStack: false
        }));
      }

      if (buffer) {
        // send the buffered files downstream
        buffer.forEach(function (file) {
          this.push(file);
        }, this);
      }
    }
  );
};
<commit_msg>Fix fail reporter JSHint 2.8.0 made this bug apparent.<commit_after>var stream = require('../stream');
var PluginError = require('gulp-util').PluginError;
var _ = require('lodash');

module.exports = function (opts) {
  opts = opts || {};

  // @type false|[]paths - paths to files that failed jshint
  var fails = false;

  // @type false|[]files - files that need to be passed downstream on flush
  var buffer = opts.buffer !== false ? [] : false;

  // @type object - count of jshint error, warning and info messages
  var messages = {error: 0, warning: 0, info: 0};

  return stream(
    function through(file) {
      // count error, warning and info messages
      if (file.jshint && file.jshint.results) {
        messages = _.reduce(file.jshint.results, function(result, err) {
          return {
            error: result.error + Number(err.error.code[0] === 'E'),
            warning: result.warning + Number(err.error.code[0] === 'W'),
            info: result.info + Number(err.error.code[0] === 'I')
          };
        }, messages);
      }

      // check for failure
      if (file.jshint && !file.jshint.success && !file.jshint.ignored) {
        (fails = fails || []).push(file.path);
      }

      // buffer or pass downstream
      (buffer || this).push(file);
    }, function flush() {
      var failOnWarning = !opts.ignoreWarning && messages.warning;
      var failOnInfo = !opts.ignoreInfo && messages.info;
      if (fails && (messages.error || failOnWarning || failOnInfo)) {
        this.emit('error', new PluginError('gulp-jshint', {
          message: 'JSHint failed for: ' + fails.join(', '),
          showStack: false
        }));
      }

      if (buffer) {
        // send the buffered files downstream
        buffer.forEach(function (file) {
          this.push(file);
        }, this);
      }
    }
  );
};
<|endoftext|>