<commit_before>/**
	_moon.ScrollStrategy_ inherits from
	[enyo.TouchScrollStrategy](#enyo.TouchScrollStrategy). Its main purpose is to
	handle scroller paging for [moon.Scroller](#moon.Scroller) and
	[moon.List](#moon.List).
*/

enyo.kind({
	name: "moon.ScrollStrategy",
	kind: "enyo.TouchScrollStrategy",
	//* @public
	published: {
		/** 
			Defines the ratio of mousewheel "delta" units to pixels scrolled.  Increase this value to increase
			the distance scrolled by the scroll wheel.  Note, mice/trackpads do not emit the same "delta" units
			per "notch" or flick of the scroll wheel/trackpad; that can vary based on intensity and momentum.
		*/
		scrollWheelMultiplier: 2,
		/** 
			Defines the maximum distance scrolled by each scroll wheel event, as a rato of the viewport height/width.
			Setting to larger than 1 is not advised, since a single scroll event could move more than one viewport's
			worth of content (depending on the delta received), skipping content.
		*/
		scrollWheelPageMultiplier: 0.2,
		/** 
			Defines the distance scrolled per tap of the paging button, as a rato of the viewport height/width.
			Setting to larger than 1 is not advised, since a paging button tap will move more than one viewport's
			worth of content, skipping content.
		*/
		paginationPageMultiplier: 0.8,
		/** 
			Defines the ratio of continuous-scrolling delta units to pixels scrolled.
			Increase this value to increase the distance scrolled by holding the pagination buttons.
		*/
		paginationScrollMultiplier: 8
	},
	//* @protected
	handlers: {
		onRequestScrollIntoView : "requestScrollIntoView",
		onenter                 : "enter",
		onleave                 : "leave"
	},
	//* @protected
	tools: [
		{kind: "ScrollMath", onScrollStart: "scrollMathStart", onScroll: "scrollMathScroll", onScrollStop: "scrollMathStop"}
	],
	components: [
		{name: "clientContainer", classes: "moon-scroller-client-wrapper", components: [
			{name: "viewport", classes:"moon-scroller-viewport", components: [
				{name: "client", classes: "enyo-touch-scroller matrix-scroll-client matrix3dsurface"}
			]}
		]},
		{name: "vColumn", classes: "moon-scroller-v-column", components: [
			{name: "pageUpControl", kind: "moon.PagingControl", side: "top", onPaginateScroll: "paginateScroll", onPaginate: "paginate"},
			{name: "vthumbContainer", classes: "moon-scroller-thumb-container moon-scroller-vthumb-container", components: [
				{name: "vthumb", kind: "moon.ScrollThumb", classes: "moon-scroller-vthumb hidden", axis: "v"}
			]},
			{name: "pageDownControl", kind: "moon.PagingControl", side: "bottom", onPaginateScroll: "paginateScroll", onPaginate: "paginate"}
		]},
		{name: "hColumn", classes: "moon-scroller-h-column", components: [
			{name: "pageLeftControl", kind: "moon.PagingControl", side: "left", onPaginateScroll: "paginateScroll", onPaginate: "paginate"},
			{name: "hthumbContainer", classes: "moon-scroller-thumb-container moon-scroller-hthumb-container", components: [
				{name: "hthumb", kind: "moon.ScrollThumb", classes: "moon-scroller-hthumb hidden", axis: "h"}
			]},
			{name: "pageRightControl", kind: "moon.PagingControl", side: "right", onPaginateScroll: "paginateScroll", onPaginate: "paginate"}
		]},
		{kind: "Signals", onSpotlightModeChanged: "spotlightModeChanged", isChrome: true}
	],
	create: function() {
		this.inherited(arguments);
		this.transform = enyo.dom.canTransform();
		this.accel = enyo.dom.canAccelerate();
		this.container.addClass("enyo-touch-strategy-container");
		this.translation = this.accel ? "matrix3d" : "matrix";
		this.showHideScrollColumns(this.container.spotlightPagingControls);
	},
	/**
		Calls super-super-inherited (i.e., skips _TouchScrollStrategy_'s)
		_rendered()_ function to avoid thumb flicker at render time. Then
		shows or hides page controls.
	*/
	rendered: function() {
		enyo.TouchScrollStrategy.prototype.rendered._inherited.apply(this, arguments);
		this.setupBounds();
		this.updateSpotlightPagingControls();
	},
	resizeHandler: function() {
		this.resizing = true;
		this.resetCachedValues();
		this.setupBounds();
		this.resizing = false;
	},
	setupBounds: function() {
		this.calcBoundaries();
		this.syncScrollMath();
		this.enableDisableScrollColumns();
		this.setThumbSizeRatio();
		this.clampScrollPosition();
	},

	//* @public
	//* Gets the left scroll position within the scroller.
	getScrollLeft: function() {
		return this.scrollLeft;
	},
	//* Gets the top scroll position within the scroller.
	getScrollTop: function() {
		return this.scrollTop;
	},
	//* Sets the left scroll position within the scroller.
	setScrollLeft: function(inLeft) {
		var m = this.$.scrollMath;
		m.setScrollX(-inLeft);
		m.stabilize();
	},
	//* Sets the top scroll position within the scroller.
	setScrollTop: function(inTop) {
		var m = this.$.scrollMath;
		m.setScrollY(-inTop);
		m.stabilize();
	},
	//* Scrolls to specific x/y positions within the scroll area.
	scrollTo: function(inX, inY) {
		this.stop();
		if (this.resizing) {
			this.effectScroll(inX, inY);
		} else {
			this._scrollTo(inX, inY);
		}
	},

	//* @protected

	//* Overrides default _maxHeightChanged()_ method from _TouchScrollStrategy_.
	maxHeightChanged: function() {
		// content should cover scroller at a minimum if there's no max-height.
		this.$.client.applyStyle("min-height", this.maxHeight ? null : "100%");
		this.$.client.applyStyle("max-height", this.maxHeight);
		this.$.clientContainer.addRemoveClass("enyo-scrollee-fit", !this.maxHeight);
	},

	// Event handling

	//* Disables dragging.
	shouldDrag: function(inSender, inEvent) { return true; },
	//* On _hold_, stops scrolling.
	hold: function(inSender, inEvent) {
		if (!this.isPageControl(inEvent.originator)) {
			this.inherited(arguments);
		}
	},
	//* On _down_, stops scrolling.
	down: function(inSender, inEvent) {
		if (!this.isPageControl(inEvent.originator) && this.isScrolling() && !this.isOverscrolling()) {
			this.stop();
		}
	},
	//* On _mousewheel_, scrolls a fixed amount.
	mousewheel: function(inSender, inEvent) {
		if (this.useMouseWheel) {
			var isScrolling = this.isScrolling();
			this.scrollBounds = this._getScrollBounds();
			this.setupBounds();

			var x = null,
				y = null,
				showVertical = this.showVertical(),
				showHorizontal = this.showHorizontal(),
				dir = null,
				val = null,
				max = null,
				delta = null
			;

			//* If we don't have to scroll, allow mousewheel event to bubble
			if (!showVertical && !showHorizontal) {
				this.scrollBounds = null;
				return false;
			}

			if (showVertical) {
				dir = inEvent.wheelDeltaY >= 0 ? 1 : -1;
				val = Math.abs(inEvent.wheelDeltaY * this.scrollWheelMultiplier);
				max = this.scrollBounds.clientHeight * this.scrollWheelPageMultiplier;
				delta = Math.min(val, max);
				y = (isScrolling ? this.lastScrollToY : this.scrollTop) + -dir * delta;
			}

			if (showHorizontal) {
				if (inEvent.wheelDeltaX) {
					dir = inEvent.wheelDeltaX >= 0 ? 1 : -1;
					val = Math.abs(inEvent.wheelDeltaX * this.scrollWheelMultiplier);
					max = this.scrollBounds.clientWidth * this.scrollWheelPageMultiplier;
					delta = Math.min(val, max);
					x = (isScrolling ? this.lastScrollToX : this.scrollLeft) + -dir * delta;
				} else if (!showVertical) {
					// only use vertical wheel for horizontal scrolling when no vertical bars shown
					dir = inEvent.wheelDeltaY >= 0 ? 1 : -1;
					val = Math.abs(inEvent.wheelDeltaY * this.scrollWheelMultiplier);
					max = this.scrollBounds.clientWidth * this.scrollWheelPageMultiplier;
					delta = Math.min(val, max);
					x = (isScrolling ? this.lastScrollToX : this.scrollLeft) + -dir * delta;
				}
			}

			this.scrollTo(x, y);
			inEvent.preventDefault();
			this.scrollBounds = null;
			return true;
		}
	},
	//* On _enter_, sets _this.hovering_ to true and shows pagination controls.
	enter: function(inSender, inEvent) {
		if (inEvent.originator == this) {
			this.hovering = true;
			this.setupBounds();
			this.showHidePageControls();
			this.showHideScrollColumns(true);
		}
	},
	//* On _leave_, sets _this.hovering_ to false and hides pagination controls.
	leave: function(inSender, inEvent) {
		if (inEvent.originator == this) {
			this.hovering = false;
			this.showHideScrollColumns(false);
		}
	},
	//* Handles _paginate_ events sent from PagingControl buttons.
	paginate: function(inSender, inEvent) {
		var sb = this.getScrollBounds(),
			scrollYDelta = sb.clientHeight * this.paginationPageMultiplier,
			scrollXDelta = sb.clientWidth * this.paginationPageMultiplier,
			side = inEvent.originator.side,
			x = this.getScrollLeft(),
			y = this.getScrollTop()
		;

		switch (side) {
		case "left":
			x -= scrollXDelta;
			break;
		case "top":
			y -= scrollYDelta;
			break;
		case "right":
			x += scrollXDelta;
			break;
		case "bottom":
			y += scrollYDelta;
			break;
		}

		this._scrollTo(x, y);

		return true;
	},
	//* Handles _paginateScroll_ events sent from PagingControl buttons.
	paginateScroll: function(inSender, inEvent) {
		if (!inEvent || !inEvent.scrollDelta) {
			return;
		}

		var delta = inEvent.scrollDelta * this.paginationScrollMultiplier,
			side = inEvent.originator.side,
			val
		;

		switch (side) {
		case "left":
			val = this.scrollLeft - delta;
			// When we hit the left, bounce and end scrolling
			if (val <= -this.$.scrollMath.leftBoundary) {
				this.setScrollLeft(-this.$.scrollMath.leftBoundary);
				this.$.pageLeftControl.hitBoundary();
			} else {
				this.setScrollLeft(val);<commit_msg>Don’t scroll to out-of-bounds values when paginating. This was occurring when the contents of a scroller were relatively close to the size of the scroller, resulting in a jarringly fast jump and then overscroll correction.<commit_after>/**
	_moon.ScrollStrategy_ inherits from
	[enyo.TouchScrollStrategy](#enyo.TouchScrollStrategy). Its main purpose is to
	handle scroller paging for [moon.Scroller](#moon.Scroller) and
	[moon.List](#moon.List).
*/

enyo.kind({
	name: "moon.ScrollStrategy",
	kind: "enyo.TouchScrollStrategy",
	//* @public
	published: {
		/** 
			Defines the ratio of mousewheel "delta" units to pixels scrolled.  Increase this value to increase
			the distance scrolled by the scroll wheel.  Note, mice/trackpads do not emit the same "delta" units
			per "notch" or flick of the scroll wheel/trackpad; that can vary based on intensity and momentum.
		*/
		scrollWheelMultiplier: 2,
		/** 
			Defines the maximum distance scrolled by each scroll wheel event, as a rato of the viewport height/width.
			Setting to larger than 1 is not advised, since a single scroll event could move more than one viewport's
			worth of content (depending on the delta received), skipping content.
		*/
		scrollWheelPageMultiplier: 0.2,
		/** 
			Defines the distance scrolled per tap of the paging button, as a rato of the viewport height/width.
			Setting to larger than 1 is not advised, since a paging button tap will move more than one viewport's
			worth of content, skipping content.
		*/
		paginationPageMultiplier: 0.8,
		/** 
			Defines the ratio of continuous-scrolling delta units to pixels scrolled.
			Increase this value to increase the distance scrolled by holding the pagination buttons.
		*/
		paginationScrollMultiplier: 8
	},
	//* @protected
	handlers: {
		onRequestScrollIntoView : "requestScrollIntoView",
		onenter                 : "enter",
		onleave                 : "leave"
	},
	//* @protected
	tools: [
		{kind: "ScrollMath", onScrollStart: "scrollMathStart", onScroll: "scrollMathScroll", onScrollStop: "scrollMathStop"}
	],
	components: [
		{name: "clientContainer", classes: "moon-scroller-client-wrapper", components: [
			{name: "viewport", classes:"moon-scroller-viewport", components: [
				{name: "client", classes: "enyo-touch-scroller matrix-scroll-client matrix3dsurface"}
			]}
		]},
		{name: "vColumn", classes: "moon-scroller-v-column", components: [
			{name: "pageUpControl", kind: "moon.PagingControl", side: "top", onPaginateScroll: "paginateScroll", onPaginate: "paginate"},
			{name: "vthumbContainer", classes: "moon-scroller-thumb-container moon-scroller-vthumb-container", components: [
				{name: "vthumb", kind: "moon.ScrollThumb", classes: "moon-scroller-vthumb hidden", axis: "v"}
			]},
			{name: "pageDownControl", kind: "moon.PagingControl", side: "bottom", onPaginateScroll: "paginateScroll", onPaginate: "paginate"}
		]},
		{name: "hColumn", classes: "moon-scroller-h-column", components: [
			{name: "pageLeftControl", kind: "moon.PagingControl", side: "left", onPaginateScroll: "paginateScroll", onPaginate: "paginate"},
			{name: "hthumbContainer", classes: "moon-scroller-thumb-container moon-scroller-hthumb-container", components: [
				{name: "hthumb", kind: "moon.ScrollThumb", classes: "moon-scroller-hthumb hidden", axis: "h"}
			]},
			{name: "pageRightControl", kind: "moon.PagingControl", side: "right", onPaginateScroll: "paginateScroll", onPaginate: "paginate"}
		]},
		{kind: "Signals", onSpotlightModeChanged: "spotlightModeChanged", isChrome: true}
	],
	create: function() {
		this.inherited(arguments);
		this.transform = enyo.dom.canTransform();
		this.accel = enyo.dom.canAccelerate();
		this.container.addClass("enyo-touch-strategy-container");
		this.translation = this.accel ? "matrix3d" : "matrix";
		this.showHideScrollColumns(this.container.spotlightPagingControls);
	},
	/**
		Calls super-super-inherited (i.e., skips _TouchScrollStrategy_'s)
		_rendered()_ function to avoid thumb flicker at render time. Then
		shows or hides page controls.
	*/
	rendered: function() {
		enyo.TouchScrollStrategy.prototype.rendered._inherited.apply(this, arguments);
		this.setupBounds();
		this.updateSpotlightPagingControls();
	},
	resizeHandler: function() {
		this.resizing = true;
		this.resetCachedValues();
		this.setupBounds();
		this.resizing = false;
	},
	setupBounds: function() {
		this.calcBoundaries();
		this.syncScrollMath();
		this.enableDisableScrollColumns();
		this.setThumbSizeRatio();
		this.clampScrollPosition();
	},

	//* @public
	//* Gets the left scroll position within the scroller.
	getScrollLeft: function() {
		return this.scrollLeft;
	},
	//* Gets the top scroll position within the scroller.
	getScrollTop: function() {
		return this.scrollTop;
	},
	//* Sets the left scroll position within the scroller.
	setScrollLeft: function(inLeft) {
		var m = this.$.scrollMath;
		m.setScrollX(-inLeft);
		m.stabilize();
	},
	//* Sets the top scroll position within the scroller.
	setScrollTop: function(inTop) {
		var m = this.$.scrollMath;
		m.setScrollY(-inTop);
		m.stabilize();
	},
	//* Scrolls to specific x/y positions within the scroll area.
	scrollTo: function(inX, inY) {
		this.stop();
		if (this.resizing) {
			this.effectScroll(inX, inY);
		} else {
			this._scrollTo(inX, inY);
		}
	},

	//* @protected

	//* Overrides default _maxHeightChanged()_ method from _TouchScrollStrategy_.
	maxHeightChanged: function() {
		// content should cover scroller at a minimum if there's no max-height.
		this.$.client.applyStyle("min-height", this.maxHeight ? null : "100%");
		this.$.client.applyStyle("max-height", this.maxHeight);
		this.$.clientContainer.addRemoveClass("enyo-scrollee-fit", !this.maxHeight);
	},

	// Event handling

	//* Disables dragging.
	shouldDrag: function(inSender, inEvent) { return true; },
	//* On _hold_, stops scrolling.
	hold: function(inSender, inEvent) {
		if (!this.isPageControl(inEvent.originator)) {
			this.inherited(arguments);
		}
	},
	//* On _down_, stops scrolling.
	down: function(inSender, inEvent) {
		if (!this.isPageControl(inEvent.originator) && this.isScrolling() && !this.isOverscrolling()) {
			this.stop();
		}
	},
	//* On _mousewheel_, scrolls a fixed amount.
	mousewheel: function(inSender, inEvent) {
		if (this.useMouseWheel) {
			var isScrolling = this.isScrolling();
			this.scrollBounds = this._getScrollBounds();
			this.setupBounds();

			var x = null,
				y = null,
				showVertical = this.showVertical(),
				showHorizontal = this.showHorizontal(),
				dir = null,
				val = null,
				max = null,
				delta = null
			;

			//* If we don't have to scroll, allow mousewheel event to bubble
			if (!showVertical && !showHorizontal) {
				this.scrollBounds = null;
				return false;
			}

			if (showVertical) {
				dir = inEvent.wheelDeltaY >= 0 ? 1 : -1;
				val = Math.abs(inEvent.wheelDeltaY * this.scrollWheelMultiplier);
				max = this.scrollBounds.clientHeight * this.scrollWheelPageMultiplier;
				delta = Math.min(val, max);
				y = (isScrolling ? this.lastScrollToY : this.scrollTop) + -dir * delta;
			}

			if (showHorizontal) {
				if (inEvent.wheelDeltaX) {
					dir = inEvent.wheelDeltaX >= 0 ? 1 : -1;
					val = Math.abs(inEvent.wheelDeltaX * this.scrollWheelMultiplier);
					max = this.scrollBounds.clientWidth * this.scrollWheelPageMultiplier;
					delta = Math.min(val, max);
					x = (isScrolling ? this.lastScrollToX : this.scrollLeft) + -dir * delta;
				} else if (!showVertical) {
					// only use vertical wheel for horizontal scrolling when no vertical bars shown
					dir = inEvent.wheelDeltaY >= 0 ? 1 : -1;
					val = Math.abs(inEvent.wheelDeltaY * this.scrollWheelMultiplier);
					max = this.scrollBounds.clientWidth * this.scrollWheelPageMultiplier;
					delta = Math.min(val, max);
					x = (isScrolling ? this.lastScrollToX : this.scrollLeft) + -dir * delta;
				}
			}

			this.scrollTo(x, y);
			inEvent.preventDefault();
			this.scrollBounds = null;
			return true;
		}
	},
	//* On _enter_, sets _this.hovering_ to true and shows pagination controls.
	enter: function(inSender, inEvent) {
		if (inEvent.originator == this) {
			this.hovering = true;
			this.setupBounds();
			this.showHidePageControls();
			this.showHideScrollColumns(true);
		}
	},
	//* On _leave_, sets _this.hovering_ to false and hides pagination controls.
	leave: function(inSender, inEvent) {
		if (inEvent.originator == this) {
			this.hovering = false;
			this.showHideScrollColumns(false);
		}
	},
	//* Handles _paginate_ events sent from PagingControl buttons.
	paginate: function(inSender, inEvent) {
		var sb = this.getScrollBounds(),
			scrollYDelta = sb.clientHeight * this.paginationPageMultiplier,
			scrollXDelta = sb.clientWidth * this.paginationPageMultiplier,
			side = inEvent.originator.side,
			x = this.getScrollLeft(),
			y = this.getScrollTop()
		;

		switch (side) {
		case "left":
			x -= scrollXDelta;
			break;
		case "top":
			y -= scrollYDelta;
			break;
		case "right":
			x += scrollXDelta;
			break;
		case "bottom":
			y += scrollYDelta;
			break;
		}

		x = Math.max(0, Math.min(x, sb.maxLeft));
		y = Math.max(0, Math.min(y, sb.maxTop));

		this._scrollTo(x, y);

		return true;
	},
	//* Handles _paginateScroll_ events sent from PagingControl buttons.
	paginateScroll: function(inSender, inEvent) {
		if (!inEvent || !inEvent.scrollDelta) {
			return;
		}

		var delta = inEvent.scrollDelta * this.paginationScrollMultiplier,
			side = inEvent.originator.side,
			val
		;

		switch (side) {
		case "left":
			val = this.scrollLeft - delta;
			// When we hit the left, bounce and end scrolling
			if (val <= -this.$.scrollMath.leftBoundary) {
				this.setScrollLeft(-this.$.scrollMath.leftBoundary);
				this.$.pageLeftControl.hitBoundary();
			} else {
				this.setScrollLeft(val);
{'fraction_non_alphanumeric': 0.13024752218359192, 'fraction_numerical': 0.0023870063826475015, 'mean_word_length': 3.878987341772152, 'pattern_counts': {'":': 10, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1755376', 'n_tokens_mistral': 7015, 'n_tokens_neox': 6280, 'n_words': 1585}