{"action":"created","comment":{"url":"https://api.github.com/repos/Workiva/wf-common/pulls/comments/32974483","id":32974483,"diff_hunk":"@@ -309,20 +383,196 @@ define(function(require) {\n         _onMouseWheel: function(event) {\n             // If we haven't started a mouse wheel for a bit, publish a start event.\n             if (!this._wheeling) {\n-                this.onMouseWheelStart.dispatch([{\n-                    distance: { x: 0, y: 0 },\n-                    source: event\n-                }]);\n-                this._wheeling = true;\n+                this._dispatchMouseWheelStart(event);\n             }\n \n             // Normalize the mouse wheel event and publish.\n             var normalizedEvent = normalizeEvent(event);\n+            this._detectUserScroll(normalizedEvent);\n             this.onMouseWheel.dispatch([normalizedEvent]);\n \n             // Debounce mouse wheels so that an end event is published 50ms\n             // after the last mousewheel occured.\n+            this._debouncedDispatchMouseWheelEnd(event);\n+        },\n+\n+        /**\n+         * Dispatches the MouseWheel start event, and handles related controls.\n+         * @param {object} event\n+         * @private\n+         */\n+        _dispatchMouseWheelStart: function(event) {\n+            this.onMouseWheelStart.dispatch([{\n+                distance: { x: 0, y: 0 },\n+                source: event\n+            }]);\n+            this._wheeling = true;\n+        },\n+\n+        /**\n+         * Dispatches the MouseWheel end event, and handles related controls.\n+         * @param {object} event\n+         * @private\n+         */\n+        _dispatchMouseWheelEnd: function(event) {\n+\n+            // Reset the delta array on MouseEnd to prevent false positives between\n+            // natural end and start events.\n+            deltaArray = [[],[]];\n+\n+            this.onMouseWheelEnd.dispatch([{\n+                distance: { x: 0, y: 0 },\n+                source: event\n+            }]);\n+            this._wheeling = false;\n+        },\n+\n+\n+        /**\n+         * Perform a ReScroll dispatch, which currently means dispatch a MouseEnd event \n+         * followed by a MouseStart event, to alert listeners that the user performed a\n+         * new distinct scroll event.  This could easily be refactored into a separate\n+         * observable if desired in the future.\n+         * @param {object} event\n+         * @private\n+         */\n+        _dispatchUserReScroll: function(event) {\n             this._dispatchMouseWheelEnd(event);\n+            this._dispatchMouseWheelStart(event);\n+        },\n+\n+        /**\n+         * Detect when the user performs a new scroll gesture.\n+         * @param {object} normalizedEvent\n+         * @private\n+         */\n+        _detectUserScroll: function(normalizedEvent) {\n+\n+            /*\n+             * This is a helper function which takes in a set of consecutive linear data \n+             * and tries to determine whether it is increasing or decreasing.  It can\n+             * operate on an entire array, or only a specified section of it. When given a\n+             * starting point and a length that extends outside the boundaries of the array,\n+             * the function assumes the data wraps around to the beginning of the array.\n+             * @param {Array.<number>} array The array of data to perform analysis on.\n+             * @param {number} arrayMaxLength [optional] The max length of the array.  If\n+             * not given, assumed to be array.length\n+             * @param {number} startAt [optional] The index in the array where the target \n+             * data begins.  If not specified, assumes this index is 0.\n+             * @param {number} length [optional] The number of data points to examine.  \n+             * If not specified, assumed to be the entire array length.\n+             * @return {number} Returns:\n+                    1 if data appears to be increasing.\n+                   -1 if data appears to be decreasing.\n+                    0 if there is no discernable trend.\n+             */\n+            var calculateDeltaDirection = function(array,startAt,length,arrayMaxLength) {\n+                arrayMaxLength = arrayMaxLength || array.length;","path":"src/MouseAdapter.js","position":202,"original_position":202,"commit_id":"4c874da35b30117e35312e42488d927e45456878","original_commit_id":"4c874da35b30117e35312e42488d927e45456878","user":{"login":"stephenbush-wf","id":7361970,"avatar_url":"https://avatars.githubusercontent.com/u/7361970?v=3","gravatar_id":"","url":"https://api.github.com/users/stephenbush-wf","html_url":"https://github.com/stephenbush-wf","followers_url":"https://api.github.com/users/stephenbush-wf/followers","following_url":"https://api.github.com/users/stephenbush-wf/following{/other_user}","gists_url":"https://api.github.com/users/stephenbush-wf/gists{/gist_id}","starred_url":"https://api.github.com/users/stephenbush-wf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephenbush-wf/subscriptions","organizations_url":"https://api.github.com/users/stephenbush-wf/orgs","repos_url":"https://api.github.com/users/stephenbush-wf/repos","events_url":"https://api.github.com/users/stephenbush-wf/events{/privacy}","received_events_url":"https://api.github.com/users/stephenbush-wf/received_events","type":"User","site_admin":false},"body":"The function already (sort of) guards against out of bounds access by modding the array indexes to ensure they are between 0 and max.  (I say sort of because javascript doesn't *really* have bounds.  It wont error, just returns `undefined`).  Also, sometimes out of bounds is expected.  This function looks at X number of data points, but until we have collected at least X to look at some of them are going to be `undefined`.  It handles these cases implicitly, not adjusting `total` in either direction as `undefined <> anything` is false.  This is much simpler than pre-screening the data and/or adjusting the function call with some variable to avoid out of bounds","created_at":"2015-06-22T19:39:59Z","updated_at":"2015-06-22T19:39:59Z","html_url":"https://github.com/Workiva/wf-common/pull/42#discussion_r32974483","pull_request_url":"https://api.github.com/repos/Workiva/wf-common/pulls/42","_links":{"self":{"href":"https://api.github.com/repos/Workiva/wf-common/pulls/comments/32974483"},"html":{"href":"https://github.com/Workiva/wf-common/pull/42#discussion_r32974483"},"pull_request":{"href":"https://api.github.com/repos/Workiva/wf-common/pulls/42"}}},"pull_request":{"url":"https://api.github.com/repos/Workiva/wf-common/pulls/42","id":37918642,"html_url":"https://github.com/Workiva/wf-common/pull/42","diff_url":"https://github.com/Workiva/wf-common/pull/42.diff","patch_url":"https://github.com/Workiva/wf-common/pull/42.patch","issue_url":"https://api.github.com/repos/Workiva/wf-common/issues/42","number":42,"state":"open","locked":false,"title":"HY-2651 Add Scroll Detection","user":{"login":"stephenbush-wf","id":7361970,"avatar_url":"https://avatars.githubusercontent.com/u/7361970?v=3","gravatar_id":"","url":"https://api.github.com/users/stephenbush-wf","html_url":"https://github.com/stephenbush-wf","followers_url":"https://api.github.com/users/stephenbush-wf/followers","following_url":"https://api.github.com/users/stephenbush-wf/following{/other_user}","gists_url":"https://api.github.com/users/stephenbush-wf/gists{/gist_id}","starred_url":"https://api.github.com/users/stephenbush-wf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephenbush-wf/subscriptions","organizations_url":"https://api.github.com/users/stephenbush-wf/orgs","repos_url":"https://api.github.com/users/stephenbush-wf/repos","events_url":"https://api.github.com/users/stephenbush-wf/events{/privacy}","received_events_url":"https://api.github.com/users/stephenbush-wf/received_events","type":"User","site_admin":false},"body":"JIRA >> https://jira.atl.workiva.net/browse/HY-2651\r\n@timmccall-wf @tomconnell-wf @todbachman-wf Code Review\r\n\r\n## Problem Summary\r\nThe behavior surrounding scroll inertia is often obnoxious at best.  Right now, when beginning a scroll, a MouseWheelStart event is fired.  This is followed by some number of MouseWheel Events (which include synthetic momentum-driven events), independent of how many scrolls are actually performed, or even if the scroll switches directions.  It would be nice if there was a way to detect when a user physically initiates a new scroll gesture.\r\n\r\n## Solution\r\nImplemented a solution in the MouseAdapter based on statistical analysis, looking at the recent history of scroll deltas and looking for patterns that indicate that a user performed a new scroll event.\r\n\r\n## Unit tests\r\nOne changed, none added yet.  I'm trying to decide on a practical way to unit test this functionality.  Right now, the only option i can think of is to feed in a long lists of fake MouseEvents/deltas that should indicate a user scroll and test whether it detected it or not.  But this doesn't seem very elegant.  Additionally, i don't like the idea of such implementation dependent test cases, where slight tweaks and  changes under the abstraction hood can cause little or no functional difference, but can cause tightly fitted unit tests to fail en masse.  If reviewer(s) have any opinions and/or suggestions, they would be appreciated.\r\n\r\n## How to QA/+10\r\n1. bower link this branch of wf-common into wf-uicomponents and run `grunt serve` in wf-uicomponents directory.  This should automatically pop up a window for functionally testing wf-uicomponents.\r\n2. Open the debug console and put a breakpoint at `bower_components/wf-common/src/MouseAdapter.js:320` inside the MouseAdapter constructor, after the observables are set up.  Refresh.\r\n3. While breakpointed, attach a debugging function to the onMouseWheelStart and/or onMouseWheelEnd observables to see when they are fired.  I use `this.onMouseWheelStart(function(event) { console.log(\"Start event!\"); }); this.onMouseWheelEnd(function(event) { console.log(\"End event!\"); });`\r\n4. Thoroughly test the scroll behavior, use your best judgement to determine if events are firing reasonably and consistently with your scroll gestures.  Pay particular attention to scrolling gestures you make between a cold start and debounced end-event, as those are the events this PR is all about.","created_at":"2015-06-17T16:13:27Z","updated_at":"2015-06-22T19:39:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"da7c7f282641dfdc51dc83fc798a6d14f81b9d44","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/Workiva/wf-common/pulls/42/commits","review_comments_url":"https://api.github.com/repos/Workiva/wf-common/pulls/42/comments","review_comment_url":"https://api.github.com/repos/Workiva/wf-common/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Workiva/wf-common/issues/42/comments","statuses_url":"https://api.github.com/repos/Workiva/wf-common/statuses/4c874da35b30117e35312e42488d927e45456878","head":{"label":"Workiva:HY-2651_Add_Scroll_Detection","ref":"HY-2651_Add_Scroll_Detection","sha":"4c874da35b30117e35312e42488d927e45456878","user":{"login":"Workiva","id":1087529,"avatar_url":"https://avatars.githubusercontent.com/u/1087529?v=3","gravatar_id":"","url":"https://api.github.com/users/Workiva","html_url":"https://github.com/Workiva","followers_url":"https://api.github.com/users/Workiva/followers","following_url":"https://api.github.com/users/Workiva/following{/other_user}","gists_url":"https://api.github.com/users/Workiva/gists{/gist_id}","starred_url":"https://api.github.com/users/Workiva/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Workiva/subscriptions","organizations_url":"https://api.github.com/users/Workiva/orgs","repos_url":"https://api.github.com/users/Workiva/repos","events_url":"https://api.github.com/users/Workiva/events{/privacy}","received_events_url":"https://api.github.com/users/Workiva/received_events","type":"Organization","site_admin":false},"repo":{"id":15975941,"name":"wf-common","full_name":"Workiva/wf-common","owner":{"login":"Workiva","id":1087529,"avatar_url":"https://avatars.githubusercontent.com/u/1087529?v=3","gravatar_id":"","url":"https://api.github.com/users/Workiva","html_url":"https://github.com/Workiva","followers_url":"https://api.github.com/users/Workiva/followers","following_url":"https://api.github.com/users/Workiva/following{/other_user}","gists_url":"https://api.github.com/users/Workiva/gists{/gist_id}","starred_url":"https://api.github.com/users/Workiva/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Workiva/subscriptions","organizations_url":"https://api.github.com/users/Workiva/orgs","repos_url":"https://api.github.com/users/Workiva/repos","events_url":"https://api.github.com/users/Workiva/events{/privacy}","received_events_url":"https://api.github.com/users/Workiva/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Workiva/wf-common","description":"","fork":false,"url":"https://api.github.com/repos/Workiva/wf-common","forks_url":"https://api.github.com/repos/Workiva/wf-common/forks","keys_url":"https://api.github.com/repos/Workiva/wf-common/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Workiva/wf-common/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Workiva/wf-common/teams","hooks_url":"https://api.github.com/repos/Workiva/wf-common/hooks","issue_events_url":"https://api.github.com/repos/Workiva/wf-common/issues/events{/number}","events_url":"https://api.github.com/repos/Workiva/wf-common/events","assignees_url":"https://api.github.com/repos/Workiva/wf-common/assignees{/user}","branches_url":"https://api.github.com/repos/Workiva/wf-common/branches{/branch}","tags_url":"https://api.github.com/repos/Workiva/wf-common/tags","blobs_url":"https://api.github.com/repos/Workiva/wf-common/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Workiva/wf-common/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Workiva/wf-common/git/refs{/sha}","trees_url":"https://api.github.com/repos/Workiva/wf-common/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Workiva/wf-common/statuses/{sha}","languages_url":"https://api.github.com/repos/Workiva/wf-common/languages","stargazers_url":"https://api.github.com/repos/Workiva/wf-common/stargazers","contributors_url":"https://api.github.com/repos/Workiva/wf-common/contributors","subscribers_url":"https://api.github.com/repos/Workiva/wf-common/subscribers","subscription_url":"https://api.github.com/repos/Workiva/wf-common/subscription","commits_url":"https://api.github.com/repos/Workiva/wf-common/commits{/sha}","git_commits_url":"https://api.github.com/repos/Workiva/wf-common/git/commits{/sha}","comments_url":"https://api.github.com/repos/Workiva/wf-common/comments{/number}","issue_comment_url":"https://api.github.com/repos/Workiva/wf-common/issues/comments{/number}","contents_url":"https://api.github.com/repos/Workiva/wf-common/contents/{+path}","compare_url":"https://api.github.com/repos/Workiva/wf-common/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Workiva/wf-common/merges","archive_url":"https://api.github.com/repos/Workiva/wf-common/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Workiva/wf-common/downloads","issues_url":"https://api.github.com/repos/Workiva/wf-common/issues{/number}","pulls_url":"https://api.github.com/repos/Workiva/wf-common/pulls{/number}","milestones_url":"https://api.github.com/repos/Workiva/wf-common/milestones{/number}","notifications_url":"https://api.github.com/repos/Workiva/wf-common/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Workiva/wf-common/labels{/name}","releases_url":"https://api.github.com/repos/Workiva/wf-common/releases{/id}","created_at":"2014-01-16T17:53:38Z","updated_at":"2015-05-12T17:05:56Z","pushed_at":"2015-06-17T23:42:53Z","git_url":"git://github.com/Workiva/wf-common.git","ssh_url":"git@github.com:Workiva/wf-common.git","clone_url":"https://github.com/Workiva/wf-common.git","svn_url":"https://github.com/Workiva/wf-common","homepage":null,"size":1390,"stargazers_count":5,"watchers_count":5,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":2,"forks":1,"open_issues":2,"watchers":5,"default_branch":"master"}},"base":{"label":"Workiva:master","ref":"master","sha":"97f4e951216c5be5d8d7aca9cb1c6055630b2c82","user":{"login":"Workiva","id":1087529,"avatar_url":"https://avatars.githubusercontent.com/u/1087529?v=3","gravatar_id":"","url":"https://api.github.com/users/Workiva","html_url":"https://github.com/Workiva","followers_url":"https://api.github.com/users/Workiva/followers","following_url":"https://api.github.com/users/Workiva/following{/other_user}","gists_url":"https://api.github.com/users/Workiva/gists{/gist_id}","starred_url":"https://api.github.com/users/Workiva/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Workiva/subscriptions","organizations_url":"https://api.github.com/users/Workiva/orgs","repos_url":"https://api.github.com/users/Workiva/repos","events_url":"https://api.github.com/users/Workiva/events{/privacy}","received_events_url":"https://api.github.com/users/Workiva/received_events","type":"Organization","site_admin":false},"repo":{"id":15975941,"name":"wf-common","full_name":"Workiva/wf-common","owner":{"login":"Workiva","id":1087529,"avatar_url":"https://avatars.githubusercontent.com/u/1087529?v=3","gravatar_id":"","url":"https://api.github.com/users/Workiva","html_url":"https://github.com/Workiva","followers_url":"https://api.github.com/users/Workiva/followers","following_url":"https://api.github.com/users/Workiva/following{/other_user}","gists_url":"https://api.github.com/users/Workiva/gists{/gist_id}","starred_url":"https://api.github.com/users/Workiva/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Workiva/subscriptions","organizations_url":"https://api.github.com/users/Workiva/orgs","repos_url":"https://api.github.com/users/Workiva/repos","events_url":"https://api.github.com/users/Workiva/events{/privacy}","received_events_url":"https://api.github.com/users/Workiva/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Workiva/wf-common","description":"","fork":false,"url":"https://api.github.com/repos/Workiva/wf-common","forks_url":"https://api.github.com/repos/Workiva/wf-common/forks","keys_url":"https://api.github.com/repos/Workiva/wf-common/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Workiva/wf-common/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Workiva/wf-common/teams","hooks_url":"https://api.github.com/repos/Workiva/wf-common/hooks","issue_events_url":"https://api.github.com/repos/Workiva/wf-common/issues/events{/number}","events_url":"https://api.github.com/repos/Workiva/wf-common/events","assignees_url":"https://api.github.com/repos/Workiva/wf-common/assignees{/user}","branches_url":"https://api.github.com/repos/Workiva/wf-common/branches{/branch}","tags_url":"https://api.github.com/repos/Workiva/wf-common/tags","blobs_url":"https://api.github.com/repos/Workiva/wf-common/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Workiva/wf-common/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Workiva/wf-common/git/refs{/sha}","trees_url":"https://api.github.com/repos/Workiva/wf-common/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Workiva/wf-common/statuses/{sha}","languages_url":"https://api.github.com/repos/Workiva/wf-common/languages","stargazers_url":"https://api.github.com/repos/Workiva/wf-common/stargazers","contributors_url":"https://api.github.com/repos/Workiva/wf-common/contributors","subscribers_url":"https://api.github.com/repos/Workiva/wf-common/subscribers","subscription_url":"https://api.github.com/repos/Workiva/wf-common/subscription","commits_url":"https://api.github.com/repos/Workiva/wf-common/commits{/sha}","git_commits_url":"https://api.github.com/repos/Workiva/wf-common/git/commits{/sha}","comments_url":"https://api.github.com/repos/Workiva/wf-common/comments{/number}","issue_comment_url":"https://api.github.com/repos/Workiva/wf-common/issues/comments{/number}","contents_url":"https://api.github.com/repos/Workiva/wf-common/contents/{+path}","compare_url":"https://api.github.com/repos/Workiva/wf-common/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Workiva/wf-common/merges","archive_url":"https://api.github.com/repos/Workiva/wf-common/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Workiva/wf-common/downloads","issues_url":"https://api.github.com/repos/Workiva/wf-common/issues{/number}","pulls_url":"https://api.github.com/repos/Workiva/wf-common/pulls{/number}","milestones_url":"https://api.github.com/repos/Workiva/wf-common/milestones{/number}","notifications_url":"https://api.github.com/repos/Workiva/wf-common/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Workiva/wf-common/labels{/name}","releases_url":"https://api.github.com/repos/Workiva/wf-common/releases{/id}","created_at":"2014-01-16T17:53:38Z","updated_at":"2015-05-12T17:05:56Z","pushed_at":"2015-06-17T23:42:53Z","git_url":"git://github.com/Workiva/wf-common.git","ssh_url":"git@github.com:Workiva/wf-common.git","clone_url":"https://github.com/Workiva/wf-common.git","svn_url":"https://github.com/Workiva/wf-common","homepage":null,"size":1390,"stargazers_count":5,"watchers_count":5,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":2,"forks":1,"open_issues":2,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Workiva/wf-common/pulls/42"},"html":{"href":"https://github.com/Workiva/wf-common/pull/42"},"issue":{"href":"https://api.github.com/repos/Workiva/wf-common/issues/42"},"comments":{"href":"https://api.github.com/repos/Workiva/wf-common/issues/42/comments"},"review_comments":{"href":"https://api.github.com/repos/Workiva/wf-common/pulls/42/comments"},"review_comment":{"href":"https://api.github.com/repos/Workiva/wf-common/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Workiva/wf-common/pulls/42/commits"},"statuses":{"href":"https://api.github.com/repos/Workiva/wf-common/statuses/4c874da35b30117e35312e42488d927e45456878"}}}}