├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .template-lintrc.js ├── .watchmanconfig ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep ├── instance-initializers │ └── ember-router-scroll.js └── services │ └── router-scroll.js ├── app ├── .gitkeep ├── instance-initializers │ └── ember-router-scroll.js └── services │ └── router-scroll.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package-lock.json ├── package.json ├── testem.js ├── tests ├── acceptance │ └── basic-functionality-test.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── application.js │ │ ├── helpers │ │ │ ├── .gitkeep │ │ │ └── not.js │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ ├── .gitkeep │ │ │ ├── index.js │ │ │ ├── target-next-page.js │ │ │ └── target.js │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ ├── next-page.hbs │ │ │ ├── target-next-page.hbs │ │ │ └── target.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ ├── crossdomain.xml │ │ ├── images │ │ ├── death-star.png │ │ ├── stars.png │ │ ├── tentacle-monster.png │ │ └── yellow_submarine.gif │ │ └── robots.txt ├── helpers │ ├── resolver.js │ └── start-app.js ├── index.html ├── integration │ └── .gitkeep ├── test-helper.js └── unit │ ├── .gitkeep │ ├── router-scroll-test.js │ └── services │ └── router-scroll-test.js ├── vendor └── .gitkeep └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .*/ 17 | .eslintcache 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /package.json.ember-try 23 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true, 11 | }, 12 | }, 13 | plugins: ['ember'], 14 | extends: [ 15 | 'eslint:recommended', 16 | 'plugin:ember/recommended', 17 | 'plugin:prettier/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | }, 22 | rules: {}, 23 | overrides: [ 24 | // node files 25 | { 26 | files: [ 27 | './.eslintrc.js', 28 | './.prettierrc.js', 29 | './.template-lintrc.js', 30 | './ember-cli-build.js', 31 | './index.js', 32 | './testem.js', 33 | './blueprints/*/index.js', 34 | './config/**/*.js', 35 | './tests/dummy/config/**/*.js', 36 | ], 37 | parserOptions: { 38 | sourceType: 'script', 39 | }, 40 | env: { 41 | browser: false, 42 | node: true, 43 | }, 44 | plugins: ['node'], 45 | extends: ['plugin:node/recommended'], 46 | }, 47 | { 48 | // Test files: 49 | files: ['tests/**/*-test.{js,ts}'], 50 | extends: ['plugin:qunit/recommended'], 51 | }, 52 | ], 53 | }; 54 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: {} 9 | 10 | jobs: 11 | test: 12 | name: "Tests" 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Install Node 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: 12.x 21 | cache: yarn 22 | - name: Install Dependencies 23 | run: yarn install --frozen-lockfile 24 | - name: Lint 25 | run: yarn lint 26 | - name: Run Tests 27 | run: yarn test:ember 28 | 29 | floating: 30 | name: "Floating Dependencies" 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v2 35 | - uses: actions/setup-node@v2 36 | with: 37 | node-version: 12.x 38 | cache: yarn 39 | - name: Install Dependencies 40 | run: yarn install --no-lockfile 41 | - name: Run Tests 42 | run: yarn test:ember 43 | 44 | try-scenarios: 45 | name: ${{ matrix.try-scenario }} 46 | runs-on: ubuntu-latest 47 | needs: 'test' 48 | 49 | strategy: 50 | fail-fast: false 51 | matrix: 52 | try-scenario: 53 | - ember-lts-3.12 54 | - ember-lts-3.16 55 | - ember-lts-3.20 56 | - ember-lts-3.24 57 | - ember-release 58 | - ember-beta 59 | - ember-canary 60 | - ember-classic 61 | - ember-default-with-jquery 62 | - embroider-safe 63 | - embroider-optimized 64 | 65 | steps: 66 | - uses: actions/checkout@v2 67 | - name: Install Node 68 | uses: actions/setup-node@v2 69 | with: 70 | node-version: 12.x 71 | cache: yarn 72 | - name: Install Dependencies 73 | run: yarn install --frozen-lockfile 74 | - name: Run Tests 75 | run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }} 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /.eslintcache 16 | /connect.lock 17 | /coverage/ 18 | /libpeerconnection.log 19 | /npm-debug.log* 20 | /testem.log 21 | /yarn-error.log 22 | 23 | # ember-try 24 | /.node_modules.ember-try/ 25 | /bower.json.ember-try 26 | /package.json.ember-try 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintcache 14 | /.eslintignore 15 | /.eslintrc.js 16 | /.git/ 17 | /.github/ 18 | /.gitignore 19 | /.prettierignore 20 | /.prettierrc.js 21 | /.template-lintrc.js 22 | /.travis.yml 23 | /.watchmanconfig 24 | /bower.json 25 | /config/ember-try.js 26 | /CODE_OF_CONDUCT.md 27 | /CONTRIBUTING.md 28 | /ember-cli-build.js 29 | /testem.js 30 | /tests/ 31 | /yarn-error.log 32 | /yarn.lock 33 | .gitkeep 34 | 35 | # ember-try 36 | /.node_modules.ember-try/ 37 | /bower.json.ember-try 38 | /package.json.ember-try 39 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .eslintcache 17 | 18 | # ember-try 19 | /.node_modules.ember-try/ 20 | /bower.json.ember-try 21 | /package.json.ember-try 22 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 4.1.2 4 | - Fix automatically scrolling to the element which id matches the hash segment of the url when there's loading substates. 5 | 6 | - Updating to the v3.0 series was due to removing `scrollWhenPainted` as a config option. Also, we fixed some hidden bugs with scheduling when to scroll to your last y position. 7 | 8 | ## v4.1.0 (2021-06-18) 9 | 10 | #### :rocket: Enhancement 11 | * [#283](https://github.com/DockYard/ember-router-scroll/pull/283) Upgrade ember-app-scheduler to the latest ([@SergeAstapov](https://github.com/SergeAstapov)) 12 | 13 | #### Committers: 2 14 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 15 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 16 | 17 | 18 | ## 4.1.0 (2021-06-18) 19 | 20 | #### :rocket: Enhancement 21 | * [#283](https://github.com/DockYard/ember-router-scroll/pull/283) Upgrade ember-app-scheduler to the latest ([@SergeAstapov](https://github.com/SergeAstapov)) 22 | 23 | #### Committers: 2 24 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 25 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 26 | 27 | 28 | ## v4.0.3 (2021-04-06) 29 | 30 | #### :bug: Bug Fix 31 | * [#280](https://github.com/DockYard/ember-router-scroll/pull/280) Run service teardown code in willDestroy() ([@SergeAstapov](https://github.com/SergeAstapov)) 32 | 33 | #### Committers: 2 34 | - Dan Nelson ([@nadnoslen](https://github.com/nadnoslen)) 35 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 36 | 37 | 38 | ## 4.0.3 (2021-04-06) 39 | 40 | #### :bug: Bug Fix 41 | * [#280](https://github.com/DockYard/ember-router-scroll/pull/280) Run service teardown code in willDestroy() ([@SergeAstapov](https://github.com/SergeAstapov)) 42 | 43 | #### Committers: 2 44 | - Dan Nelson ([@nadnoslen](https://github.com/nadnoslen)) 45 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 46 | 47 | 48 | ## v4.0.0 (2021-01-05) 49 | 50 | #### :rocket: Enhancement 51 | * [#270](https://github.com/DockYard/ember-router-scroll/pull/270) [Major]: refactor: use`RouterService` instead of Ember `Router` ([@snewcomer](https://github.com/snewcomer)) 52 | 53 | #### Committers: 1 54 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 55 | 56 | 57 | ## 4.0.0 (2021-01-05) 58 | 59 | #### :rocket: Enhancement 60 | * [#270](https://github.com/DockYard/ember-router-scroll/pull/270) [Major]: refactor: use`RouterService` instead of Ember `Router` ([@snewcomer](https://github.com/snewcomer)) 61 | 62 | #### Committers: 1 63 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 64 | 65 | 66 | ## v3.3.5 (2020-12-24) 67 | 68 | #### :rocket: Enhancement 69 | * [#269](https://github.com/DockYard/ember-router-scroll/pull/269) [Enhancement]: improve idle case with improved scrollTo under high loads ([@snewcomer](https://github.com/snewcomer)) 70 | 71 | #### Committers: 1 72 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 73 | 74 | 75 | ## 3.3.5 (2020-12-24) 76 | 77 | #### :rocket: Enhancement 78 | * [#269](https://github.com/DockYard/ember-router-scroll/pull/269) [Enhancement]: improve idle case with improved scrollTo under high loads ([@snewcomer](https://github.com/snewcomer)) 79 | 80 | #### Committers: 1 81 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 82 | 83 | 84 | ## v3.3.4 (2020-12-24) 85 | 86 | #### :rocket: Enhancement 87 | * [#266](https://github.com/DockYard/ember-router-scroll/pull/266) [Deps]: bump app-scheduler 5.1.1 ([@snewcomer](https://github.com/snewcomer)) 88 | 89 | #### Committers: 2 90 | - Garrick Cheung ([@GCheung55](https://github.com/GCheung55)) 91 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 92 | 93 | 94 | ## 3.3.4 (2020-12-24) 95 | 96 | #### :rocket: Enhancement 97 | * [#266](https://github.com/DockYard/ember-router-scroll/pull/266) [Deps]: bump app-scheduler 5.1.1 ([@snewcomer](https://github.com/snewcomer)) 98 | 99 | #### Committers: 2 100 | - Garrick Cheung ([@GCheung55](https://github.com/GCheung55)) 101 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 102 | 103 | 104 | ## v3.3.3 (2020-08-01) 105 | 106 | #### :rocket: Enhancement 107 | * [#257](https://github.com/DockYard/ember-router-scroll/pull/257) Run service teardown code in willDestroy() ([@SergeAstapov](https://github.com/SergeAstapov)) 108 | 109 | #### Committers: 2 110 | - Bruno Casali ([@brunoocasali](https://github.com/brunoocasali)) 111 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 112 | 113 | 114 | ## 3.3.3 (2020-08-01) 115 | 116 | #### :rocket: Enhancement 117 | * [#257](https://github.com/DockYard/ember-router-scroll/pull/257) Run service teardown code in willDestroy() ([@SergeAstapov](https://github.com/SergeAstapov)) 118 | 119 | #### Committers: 2 120 | - Bruno Casali ([@brunoocasali](https://github.com/brunoocasali)) 121 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 122 | 123 | 124 | ## v3.3.0 (2020-03-03) 125 | 126 | #### :rocket: Enhancement 127 | * [#246](https://github.com/DockYard/ember-router-scroll/pull/246) Add Evented trigger didScroll ([@snewcomer](https://github.com/snewcomer)) 128 | 129 | #### Committers: 1 130 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 131 | 132 | 133 | ## 3.3.0 (2020-03-03) 134 | 135 | #### :rocket: Enhancement 136 | * [#246](https://github.com/DockYard/ember-router-scroll/pull/246) Add Evented trigger didScroll ([@snewcomer](https://github.com/snewcomer)) 137 | 138 | #### Committers: 1 139 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 140 | 141 | 142 | ## v3.2.0 (2020-02-26) 143 | 144 | #### :rocket: Enhancement 145 | * [#244](https://github.com/DockYard/ember-router-scroll/pull/244) [ENHANCEMENT] Bring back scroll recursive ([@snewcomer](https://github.com/snewcomer)) 146 | 147 | #### Committers: 1 148 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 149 | 150 | 151 | ## 3.2.0 (2020-02-26) 152 | 153 | #### :rocket: Enhancement 154 | * [#244](https://github.com/DockYard/ember-router-scroll/pull/244) [ENHANCEMENT] Bring back scroll recursive ([@snewcomer](https://github.com/snewcomer)) 155 | 156 | #### Committers: 1 157 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 158 | 159 | 160 | ## v3.1.3 (2020-02-26) 161 | 162 | #### :bug: Bug Fix 163 | * [#242](https://github.com/DockYard/ember-router-scroll/pull/242) [BUG] update scroll position should only be called once ([@snewcomer](https://github.com/snewcomer)) 164 | 165 | #### Committers: 1 166 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 167 | 168 | 169 | ## 3.1.3 (2020-02-26) 170 | 171 | #### :bug: Bug Fix 172 | * [#242](https://github.com/DockYard/ember-router-scroll/pull/242) [BUG] update scroll position should only be called once ([@snewcomer](https://github.com/snewcomer)) 173 | 174 | #### Committers: 1 175 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 176 | 177 | 178 | ## v3.1.2 (2020-02-25) 179 | 180 | #### :bug: Bug Fix 181 | * [#241](https://github.com/DockYard/ember-router-scroll/pull/241) Prevent multiple calls on routeDidChange ([@snewcomer](https://github.com/snewcomer)) 182 | 183 | #### Committers: 1 184 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 185 | 186 | 187 | ## 3.1.2 (2020-02-25) 188 | 189 | #### :bug: Bug Fix 190 | * [#241](https://github.com/DockYard/ember-router-scroll/pull/241) Prevent multiple calls on routeDidChange ([@snewcomer](https://github.com/snewcomer)) 191 | 192 | #### Committers: 1 193 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 194 | 195 | 196 | ## 3.1.1 (2020-02-25) 197 | 198 | #### :bug: Bug Fix 199 | * [#240](https://github.com/DockYard/ember-router-scroll/pull/240) Missing setter for scrollWhenAfterRender ([@snewcomer](https://github.com/snewcomer)) 200 | 201 | #### Committers: 1 202 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 203 | 204 | 205 | ## v3.1.0 (2020-02-25) 206 | 207 | #### :rocket: Enhancement 208 | * [#239](https://github.com/DockYard/ember-router-scroll/pull/239) Scroll when afterRender ([@snewcomer](https://github.com/snewcomer)) 209 | 210 | #### Committers: 1 211 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 212 | 213 | 214 | ## 3.1.0 (2020-02-25) 215 | 216 | #### :rocket: Enhancement 217 | * [#239](https://github.com/DockYard/ember-router-scroll/pull/239) Scroll when afterRender ([@snewcomer](https://github.com/snewcomer)) 218 | 219 | #### Committers: 1 220 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 221 | 222 | 223 | ## v3.0.0 (2020-02-24) 224 | 225 | #### :rocket: Enhancement 226 | * [#238](https://github.com/DockYard/ember-router-scroll/pull/238) Upgrade ember-app-scheduler to v4.0.0 ([@bobisjan](https://github.com/bobisjan)) 227 | 228 | #### Committers: 1 229 | - Jan Bobisud ([@bobisjan](https://github.com/bobisjan)) 230 | 231 | 232 | ## 3.0.0 (2020-02-24) 233 | 234 | #### :rocket: Enhancement 235 | * [#238](https://github.com/DockYard/ember-router-scroll/pull/238) Upgrade ember-app-scheduler to v4.0.0 ([@bobisjan](https://github.com/bobisjan)) 236 | 237 | #### Committers: 1 238 | - Jan Bobisud ([@bobisjan](https://github.com/bobisjan)) 239 | 240 | 241 | ## v2.0.1 (2020-02-21) 242 | 243 | #### :bug: Bug Fix 244 | * [#237](https://github.com/DockYard/ember-router-scroll/pull/237) Remove recursive scroll check ([@snewcomer](https://github.com/snewcomer)) 245 | 246 | #### Committers: 2 247 | - Florian Pichler ([@pichfl](https://github.com/pichfl)) 248 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 249 | 250 | 251 | ## 2.0.1 (2020-02-21) 252 | 253 | #### :bug: Bug Fix 254 | * [#237](https://github.com/DockYard/ember-router-scroll/pull/237) Remove recursive scroll check ([@snewcomer](https://github.com/snewcomer)) 255 | 256 | #### Committers: 2 257 | - Florian Pichler ([@pichfl](https://github.com/pichfl)) 258 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 259 | 260 | 261 | ## v2.0.0 (2020-01-17) 262 | 263 | #### :boom: Breaking Change 264 | * [#234](https://github.com/DockYard/ember-router-scroll/pull/234) [MAJOR] Class based elements ([@snewcomer](https://github.com/snewcomer)) 265 | 266 | #### :rocket: Enhancement 267 | * [#234](https://github.com/DockYard/ember-router-scroll/pull/234) [MAJOR] Class based elements ([@snewcomer](https://github.com/snewcomer)) 268 | * [#202](https://github.com/DockYard/ember-router-scroll/pull/202) Update to 3.12 ([@snewcomer](https://github.com/snewcomer)) 269 | 270 | #### Committers: 1 271 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 272 | 273 | 274 | ## 2.0.0 (2020-01-17) 275 | 276 | #### :boom: Breaking Change 277 | * [#234](https://github.com/DockYard/ember-router-scroll/pull/234) [MAJOR] Class based elements ([@snewcomer](https://github.com/snewcomer)) 278 | 279 | #### :rocket: Enhancement 280 | * [#234](https://github.com/DockYard/ember-router-scroll/pull/234) [MAJOR] Class based elements ([@snewcomer](https://github.com/snewcomer)) 281 | * [#202](https://github.com/DockYard/ember-router-scroll/pull/202) Update to 3.12 ([@snewcomer](https://github.com/snewcomer)) 282 | 283 | #### Committers: 1 284 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 285 | 286 | 287 | ## v1.3.3 (2019-10-13) 288 | 289 | #### :rocket: Enhancement 290 | * [#225](https://github.com/DockYard/ember-router-scroll/pull/225) Allow recording position with users first page visit ([@snewcomer](https://github.com/snewcomer)) 291 | 292 | #### :bug: Bug Fix 293 | * [#225](https://github.com/DockYard/ember-router-scroll/pull/225) Allow recording position with users first page visit ([@snewcomer](https://github.com/snewcomer)) 294 | 295 | #### Committers: 2 296 | - Mehul Kar ([@mehulkar](https://github.com/mehulkar)) 297 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 298 | 299 | 300 | ## 1.3.3 (2019-10-13) 301 | 302 | #### :rocket: Enhancement 303 | * [#225](https://github.com/DockYard/ember-router-scroll/pull/225) Allow recording position with users first page visit ([@snewcomer](https://github.com/snewcomer)) 304 | 305 | #### :bug: Bug Fix 306 | * [#225](https://github.com/DockYard/ember-router-scroll/pull/225) Allow recording position with users first page visit ([@snewcomer](https://github.com/snewcomer)) 307 | 308 | #### Committers: 2 309 | - Mehul Kar ([@mehulkar](https://github.com/mehulkar)) 310 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 311 | 312 | 313 | ## 1.3.1 (2019-09-12) 314 | 315 | #### :rocket: Enhancement 316 | * [#221](https://github.com/DockYard/ember-router-scroll/pull/221) Sn/1.3.1 ([@snewcomer](https://github.com/snewcomer)) 317 | * [#219](https://github.com/DockYard/ember-router-scroll/pull/219) Dont batch reads and writes in same rAF ([@snewcomer](https://github.com/snewcomer)) 318 | 319 | #### Committers: 1 320 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 321 | 322 | 323 | ## 1.3.0 (2019-09-11) 324 | 325 | #### :rocket: Enhancement 326 | * [#217](https://github.com/DockYard/ember-router-scroll/pull/217) Recursively check document height before scrollTo ([@snewcomer](https://github.com/snewcomer)) 327 | 328 | #### Committers: 1 329 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 330 | 331 | 332 | ## 1.2.1 (2019-07-25) 333 | 334 | #### :rocket: Enhancement 335 | * [#216](https://github.com/DockYard/ember-router-scroll/pull/216) Update version to 1.2.1 ([@snewcomer](https://github.com/snewcomer)) 336 | * [#213](https://github.com/DockYard/ember-router-scroll/pull/213) Allow configure so consuming app can override ([@snewcomer](https://github.com/snewcomer)) 337 | * [#210](https://github.com/DockYard/ember-router-scroll/pull/210) Fix query-params-only transitions ([@CvX](https://github.com/CvX)) 338 | 339 | #### :memo: Documentation 340 | * [#212](https://github.com/DockYard/ember-router-scroll/pull/212) Update Changelog 1.2.0 ([@snewcomer](https://github.com/snewcomer)) 341 | 342 | #### Committers: 2 343 | - Jarek Radosz ([@CvX](https://github.com/CvX)) 344 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 345 | 346 | 347 | ## 1.2.0 (2019-07-24) 348 | 349 | #### :boom: Breaking Change 350 | * [#203](https://github.com/DockYard/ember-router-scroll/pull/203) Deprecate locations/router-scroll for Embers own implementation ([@snewcomer](https://github.com/snewcomer)) 351 | 352 | #### :rocket: Enhancement 353 | * [#204](https://github.com/DockYard/ember-router-scroll/pull/204) Release 1.2.0 ([@snewcomer](https://github.com/snewcomer)) 354 | * [#203](https://github.com/DockYard/ember-router-scroll/pull/203) Deprecate locations/router-scroll for Embers own implementation ([@snewcomer](https://github.com/snewcomer)) 355 | 356 | #### Committers: 4 357 | - Jarek Radosz ([@CvX](https://github.com/CvX)) 358 | - Lou Greenwood ([@lougreenwood](https://github.com/lougreenwood)) 359 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 360 | - Steve ([@st-h](https://github.com/st-h)) 361 | 362 | 363 | ## 1.1.0 (2019-03-11) 364 | 365 | #### :rocket: Enhancement 366 | * [#187](https://github.com/DockYard/ember-router-scroll/pull/187) 1.1.0 minor version bump ([@snewcomer](https://github.com/snewcomer)) 367 | * [#183](https://github.com/DockYard/ember-router-scroll/pull/183) Fix router event deprecations, update to Ember 3.7 ([@rwwagner90](https://github.com/rwwagner90)) 368 | 369 | #### Committers: 2 370 | - Robert Wagner ([@rwwagner90](https://github.com/rwwagner90)) 371 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 372 | 373 | 374 | ## 1.0.2 (2018-12-21) 375 | 376 | #### :rocket: Enhancement 377 | * [#178](https://github.com/DockYard/ember-router-scroll/pull/178) Update to 3.6 + bump ember-app-scheduler to fix deprecations ([@cibernox](https://github.com/cibernox)) 378 | 379 | #### Committers: 2 380 | - Jeff Wainwright ([@yowainwright](https://github.com/yowainwright)) 381 | - Miguel Camba ([@cibernox](https://github.com/cibernox)) 382 | 383 | 384 | ## 1.0.1 (2018-11-05) 385 | 386 | #### :rocket: Enhancement 387 | * [#169](https://github.com/DockYard/ember-router-scroll/pull/169) Avoid scroll on first load ([@snewcomer](https://github.com/snewcomer)) 388 | 389 | #### :bug: Bug Fix 390 | * [#169](https://github.com/DockYard/ember-router-scroll/pull/169) Avoid scroll on first load ([@snewcomer](https://github.com/snewcomer)) 391 | 392 | #### Committers: 1 393 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 394 | 395 | 396 | ## 0.8.0 (2018-09-04) 397 | 398 | #### :rocket: Enhancement 399 | * [#150](https://github.com/DockYard/ember-router-scroll/pull/150) [init] adds initial work for ember-cli-update on greenkeeper PRs ([@yowainwright](https://github.com/yowainwright)) 400 | 401 | #### Committers: 4 402 | - Greg Larrenaga ([@Duder-onomy](https://github.com/Duder-onomy)) 403 | - Jeff Wainwright ([@yowainwright](https://github.com/yowainwright)) 404 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 405 | - William Bautista ([@Willibaur](https://github.com/Willibaur)) 406 | 407 | 408 | ## 0.6.1 (2018-05-08) 409 | 410 | #### :rocket: Enhancement 411 | * [#134](https://github.com/DockYard/ember-router-scroll/pull/134) [update] adds git auto-tag script, nvmrc + fixes ([@yowainwright](https://github.com/yowainwright)) 412 | * [#101](https://github.com/DockYard/ember-router-scroll/pull/101) delayScrollTop config + schedule in render queue ([@snewcomer](https://github.com/snewcomer)) 413 | * [#128](https://github.com/DockYard/ember-router-scroll/pull/128) [init] adds CODE_OF_CONDUCT.md ([@yowainwright](https://github.com/yowainwright)) 414 | * [#114](https://github.com/DockYard/ember-router-scroll/pull/114) [update] removes .travis.yml ([@yowainwright](https://github.com/yowainwright)) 415 | * [#113](https://github.com/DockYard/ember-router-scroll/pull/113) [feature] Feature add CircleCi ([@yowainwright](https://github.com/yowainwright)) 416 | * [#109](https://github.com/DockYard/ember-router-scroll/pull/109) [init] adds initial automation of changelog.md, fixes changelog script ([@yowainwright](https://github.com/yowainwright)) 417 | 418 | #### :bug: Bug Fix 419 | * [#120](https://github.com/DockYard/ember-router-scroll/pull/120) [BugFix] ensure correct intimate API is represented in transition mock ([@rondale-sc](https://github.com/rondale-sc)) 420 | 421 | #### :memo: Documentation 422 | * [#101](https://github.com/DockYard/ember-router-scroll/pull/101) delayScrollTop config + schedule in render queue ([@snewcomer](https://github.com/snewcomer)) 423 | * [#128](https://github.com/DockYard/ember-router-scroll/pull/128) [init] adds CODE_OF_CONDUCT.md ([@yowainwright](https://github.com/yowainwright)) 424 | 425 | #### Committers: 5 426 | - Jeff Wainwright ([@yowainwright](https://github.com/yowainwright)) 427 | - Jonathan ([@rondale-sc](https://github.com/rondale-sc)) 428 | - Josh ([@joshkg](https://github.com/joshkg)) 429 | - Robert Wagner ([@rwwagner90](https://github.com/rwwagner90)) 430 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 431 | 432 | 433 | ## v0.6.0 (2018-04-02) 434 | 435 | #### :rocket: Enhancement 436 | * [#108](https://github.com/DockYard/ember-router-scroll/pull/108) [update] Adds DSC style linting ([@yowainwright](https://github.com/yowainwright)) 437 | 438 | #### Committers: 7 439 | - Jeff Jewiss ([@jeffjewiss](https://github.com/jeffjewiss)) 440 | - Jeff Wainwright ([@yowainwright](https://github.com/yowainwright)) 441 | - Katherine ([@TerminalStar](https://github.com/TerminalStar)) 442 | - Paul Nicholls ([@pauln](https://github.com/pauln)) 443 | - Ryan Toronto ([@ryanto](https://github.com/ryanto)) 444 | - Sam Selikoff ([@samselikoff](https://github.com/samselikoff)) 445 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 446 | 447 | 448 | ## v0.5.0 (2018-02-01) 449 | 450 | #### :memo: Documentation 451 | * [#80](https://github.com/DockYard/ember-router-scroll/pull/80) Add CHANGELOG file ([@Turbo87](https://github.com/Turbo87)) 452 | 453 | #### :house: Internal 454 | * [#78](https://github.com/DockYard/ember-router-scroll/pull/78) Allow tests to run by adding rootURL to asset paths. ([@ppegusii](https://github.com/ppegusii)) 455 | * [#76](https://github.com/DockYard/ember-router-scroll/pull/76) Updates the demo app with the `rootUrl` ([@gowthamrm](https://github.com/gowthamrm)) 456 | * [#75](https://github.com/DockYard/ember-router-scroll/pull/75) Add `ember-cli-github-pages` to deploy demo app ([@gowthamrm](https://github.com/gowthamrm)) 457 | * [#74](https://github.com/DockYard/ember-router-scroll/pull/74) Moves the demo app inside the tests/dummy ([@gowthamrm](https://github.com/gowthamrm)) 458 | 459 | #### Committers: 5 460 | - Brian Gonzalez ([@briangonzalez](https://github.com/briangonzalez)) 461 | - Gowtham Raj ([@gowthamrm](https://github.com/gowthamrm)) 462 | - Robert Webb ([@robert-j-webb](https://github.com/robert-j-webb)) 463 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 464 | - [@ppegusii](https://github.com/ppegusii) 465 | 466 | 467 | ## v0.4.0 (2017-10-08) 468 | 469 | #### :bug: Bug Fix 470 | * [#73](https://github.com/dollarshaveclub/ember-router-scroll/pull/73) Update ember-app-scheduler to 0.2.0 (Closes [#72](https://github.com/dollarshaveclub/ember-router-scroll/issues/72)). ([@YoranBrondsema](https://github.com/YoranBrondsema)) 471 | 472 | #### Committers: 1 473 | - Yoran Brondsema ([YoranBrondsema](https://github.com/YoranBrondsema)) 474 | 475 | 476 | ## v0.3.1 (2017-09-11) 477 | 478 | #### :rocket: Enhancement 479 | * [#67](https://github.com/dollarshaveclub/ember-router-scroll/pull/67) configurable scroll element. ([@ktrhn](https://github.com/ktrhn)) 480 | 481 | #### :memo: Documentation 482 | * [#69](https://github.com/dollarshaveclub/ember-router-scroll/pull/69) Update readme with 'service:scheduler' dependency for unit tests. ([@barryofguilder](https://github.com/barryofguilder)) 483 | 484 | #### :house: Internal 485 | * [#64](https://github.com/dollarshaveclub/ember-router-scroll/pull/64) Update "ember-cli-qunit" to v4.0.1. ([@Turbo87](https://github.com/Turbo87)) 486 | 487 | #### Committers: 3 488 | - Jason Barry ([barryofguilder](https://github.com/barryofguilder)) 489 | - Sebastian Helbig ([ktrhn](https://github.com/ktrhn)) 490 | - Tobias Bieniek ([Turbo87](https://github.com/Turbo87)) 491 | 492 | 493 | ## v0.3.0 (2017-08-02) 494 | 495 | #### :rocket: Enhancement 496 | * [#57](https://github.com/dollarshaveclub/ember-router-scroll/pull/57) Schedule window.scrollTo() to after content paint by using ember-app-scheduler. ([@cclo7](https://github.com/cclo7)) 497 | 498 | #### :bug: Bug Fix 499 | * [#60](https://github.com/dollarshaveclub/ember-router-scroll/pull/60) made ember-app-scheduler a prod dep. ([@briangonzalez](https://github.com/briangonzalez)) 500 | 501 | #### :memo: Documentation 502 | * [#51](https://github.com/dollarshaveclub/ember-router-scroll/pull/51) Updated docs. ([@briangonzalez](https://github.com/briangonzalez)) 503 | 504 | #### :house: Internal 505 | * [#63](https://github.com/dollarshaveclub/ember-router-scroll/pull/63) testem: Use headless Chrome by default. ([@Turbo87](https://github.com/Turbo87)) 506 | * [#62](https://github.com/dollarshaveclub/ember-router-scroll/pull/62) CI: Use yarn instead of npm. ([@Turbo87](https://github.com/Turbo87)) 507 | * [#61](https://github.com/dollarshaveclub/ember-router-scroll/pull/61) Cleanup unused dependencies. ([@Turbo87](https://github.com/Turbo87)) 508 | * [#58](https://github.com/dollarshaveclub/ember-router-scroll/pull/58) Update to Babel 6 and New Module Imports. ([@Turbo87](https://github.com/Turbo87)) 509 | 510 | #### Committers: 3 511 | - Brian Gonzalez ([briangonzalez](https://github.com/briangonzalez)) 512 | - Chiachi Lo ([cclo7](https://github.com/cclo7)) 513 | - Tobias Bieniek ([Turbo87](https://github.com/Turbo87)) 514 | 515 | 516 | ## v0.2.0 (2017-04-21) 517 | 518 | #### :memo: Documentation 519 | * [#44](https://github.com/dollarshaveclub/ember-router-scroll/pull/44) [DOC] Add comma in code snippet. ([@YoranBrondsema](https://github.com/YoranBrondsema)) 520 | 521 | #### Committers: 2 522 | - Brian Gonzalez ([briangonzalez](https://github.com/briangonzalez)) 523 | - Yoran Brondsema ([YoranBrondsema](https://github.com/YoranBrondsema)) 524 | 525 | 526 | ## v0.1.1 (2017-02-01) 527 | 528 | #### :bug: Bug Fix 529 | * [#42](https://github.com/dollarshaveclub/ember-router-scroll/pull/42) Fix ember-getowner-polyfill deprecation. ([@krasnoukhov](https://github.com/krasnoukhov)) 530 | 531 | #### Committers: 2 532 | - Brian Gonzalez ([briangonzalez](https://github.com/briangonzalez)) 533 | - Dmitry Krasnoukhov ([krasnoukhov](https://github.com/krasnoukhov)) 534 | 535 | 536 | ## v0.1.0 (2017-01-24) 537 | 538 | #### :rocket: Enhancement 539 | * [#40](https://github.com/dollarshaveclub/ember-router-scroll/pull/40) Align with Ember's HistoryLocation router. ([@briangonzalez](https://github.com/briangonzalez)) 540 | * [#20](https://github.com/dollarshaveclub/ember-router-scroll/pull/20) Fastboot compatibility. ([@arjansingh](https://github.com/arjansingh)) 541 | 542 | #### :memo: Documentation 543 | * [#41](https://github.com/dollarshaveclub/ember-router-scroll/pull/41) [README] A note about ember rfc and core implementation. ([@briangonzalez](https://github.com/briangonzalez)) 544 | * [#36](https://github.com/dollarshaveclub/ember-router-scroll/pull/36) Remove unwanted whitespace. ([@andrewgordstewart](https://github.com/andrewgordstewart)) 545 | * [#28](https://github.com/dollarshaveclub/ember-router-scroll/pull/28) Add field to config to get live-reload working. ([@bennycwong](https://github.com/bennycwong)) 546 | * [#19](https://github.com/dollarshaveclub/ember-router-scroll/pull/19) Ember install.... ([@arjansingh](https://github.com/arjansingh)) 547 | * [#18](https://github.com/dollarshaveclub/ember-router-scroll/pull/18) update readme. ([@bennycwong](https://github.com/bennycwong)) 548 | 549 | #### :house: Internal 550 | * [#39](https://github.com/dollarshaveclub/ember-router-scroll/pull/39) travis fixes. ([@briangonzalez](https://github.com/briangonzalez)) 551 | 552 | #### Committers: 4 553 | - Arjan Singh ([arjansingh](https://github.com/arjansingh)) 554 | - Benny C. Wong ([bennycwong](https://github.com/bennycwong)) 555 | - Brian Gonzalez ([briangonzalez](https://github.com/briangonzalez)) 556 | - [andrewgordstewart](https://github.com/andrewgordstewart) 557 | 558 | 559 | ## v0.0.4 (2016-08-04) 560 | 561 | #### :bug: Bug Fix 562 | * [#14](https://github.com/dollarshaveclub/ember-router-scroll/pull/14) Enable backbutton for same routes. ([@bcardarella](https://github.com/bcardarella)) 563 | 564 | #### Committers: 1 565 | - Brian Cardarella ([bcardarella](https://github.com/bcardarella)) 566 | 567 | 568 | ## v0.0.3 (2016-08-04) 569 | 570 | #### :rocket: Enhancement 571 | * [#13](https://github.com/dollarshaveclub/ember-router-scroll/pull/13) move to index.js for easier importing. ([@bennycwong](https://github.com/bennycwong)) 572 | 573 | #### :bug: Bug Fix 574 | * [#3](https://github.com/dollarshaveclub/ember-router-scroll/pull/3) Minor fixes. ([@bennycwong](https://github.com/bennycwong)) 575 | 576 | #### :memo: Documentation 577 | * [#10](https://github.com/dollarshaveclub/ember-router-scroll/pull/10) use ember install instead of npm install. ([@bcardarella](https://github.com/bcardarella)) 578 | * [#4](https://github.com/dollarshaveclub/ember-router-scroll/pull/4) Add readme. ([@jacefarm](https://github.com/jacefarm)) 579 | 580 | #### Committers: 3 581 | - Benny C. Wong ([bennycwong](https://github.com/bennycwong)) 582 | - Brian Cardarella ([bcardarella](https://github.com/bcardarella)) 583 | - Jason Farmer ([jacefarm](https://github.com/jacefarm)) 584 | 585 | 586 | ## v0.0.1 (2016-07-21) 587 | 588 | #### :rocket: Enhancement 589 | * [#1](https://github.com/dollarshaveclub/ember-router-scroll/pull/1) ERS creation. ([@jacefarm](https://github.com/jacefarm)) 590 | 591 | #### :house: Internal 592 | * [#2](https://github.com/dollarshaveclub/ember-router-scroll/pull/2) increment version and add eslint. ([@bennycwong](https://github.com/bennycwong)) 593 | 594 | #### Committers: 2 595 | - Benny C. Wong ([bennycwong](https://github.com/bennycwong)) 596 | - Jason Farmer ([jacefarm](https://github.com/jacefarm)) 597 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` 6 | * `cd ember-router-scroll` 7 | * `yarn install` 8 | 9 | ## Linting 10 | 11 | * `yarn lint` 12 | * `yarn lint:fix` 13 | 14 | ## Running tests 15 | 16 | * `ember test` – Runs the test suite on the current Ember version 17 | * `ember test --server` – Runs the test suite in "watch mode" 18 | * `ember try:each` – Runs the test suite against multiple Ember versions 19 | 20 | ## Automation 21 | 22 | * `git checkout ` 23 | * `nvm i` 24 | * `yarn` 25 | * `yarn update` 26 | 27 | ## Running the dummy application 28 | 29 | * `ember serve` 30 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 31 | 32 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Dollar Shave Club 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ember-router-scroll 2 | ============================================================================== 3 | 4 | [![GitHub Actions Build Status](https://github.com/DockYard/ember-router-scroll/workflows/CI/badge.svg)](https://github.com/DockYard/ember-router-scroll/actions/workflows/ci.yml?query=branch%3Amaster) 5 | 6 | > Scroll to page top on transition, like a non-SPA website. An alternative scroll behavior for Ember applications. 7 | 8 | ## Why Use it? 9 | 10 | Ember expects an application to be rendered with nested views. The default behavior is for the scroll position to be 11 | preserved on every transition. However, not all Ember applications use nested views. For these applications, a user 12 | would expect to see the top of the page on most transitions. 13 | 14 | In addition to scrolling to the top of the page on most transitions, a user would expect the scroll position to be 15 | preserved when using the back or forward browser buttons. 16 | 17 | **ember-router-scroll** makes your single page application feel more like a regular website. 18 | 19 | Compatibility 20 | ------------------------------------------------------------------------------ 21 | 22 | * Ember.js v3.12 or above 23 | * Ember CLI v3.12 or above 24 | * Node.js v12 or above 25 | 26 | 27 | Installation 28 | ------------------------------------------------------------------------------ 29 | 30 | ```sh 31 | ember install ember-router-scroll 32 | ``` 33 | 34 | 35 | Usage > 4.x 36 | ------------------------------------------------------------------------------ 37 | 38 | Users do not need to import and extend from `ember-router-scroll` anymore. In order to upgrade, you should remove this import. 39 | 40 | This is what your `router.js` should look like. 41 | 42 | ```js 43 | import EmberRouter from '@ember/routing/router'; 44 | 45 | export default class Router extends EmberRouter { 46 | ... 47 | } 48 | ``` 49 | 50 | Usage < 4.x 51 | ------------------------------------------------------------------------------ 52 | 53 | **1.** Import ember-router-scroll 54 | 55 | Add RouterScroll as an extension to your Router object. This class extends EmberRouter. 56 | 57 | ```javascript 58 | // app/router.js 59 | 60 | import EmberRouterScroll from 'ember-router-scroll'; 61 | 62 | class Router extends EmberRouterScroll { 63 | ... 64 | } 65 | ``` 66 | 67 | In version prior to v2.0, you can import the mixin and use it like so. This is necessary if your application does not support JavaScript classes yet. 68 | 69 | ```javascript 70 | // app/router.js 71 | 72 | import RouterScroll from 'ember-router-scroll'; 73 | 74 | const Router = EmberRouter.extend(RouterScroll, { 75 | ... 76 | }); 77 | ``` 78 | 79 | Remaining optional steps for all versions 2.x - 4.x 80 | ------------------------------------------------------------------------------ 81 | 82 | **2.** Enable `historySupportMiddleware` in your app 83 | 84 | Edit `config/environment.js` and add `historySupportMiddleware: true,` to get live-reload working in nested routes. 85 | (See [Issue #21](https://github.com/DockYard/ember-router-scroll/issues/21)) 86 | 87 | ```javascript 88 | historySupportMiddleware: true, 89 | ``` 90 | 91 | This location type inherits from Ember's `HistoryLocation`. 92 | ``` 93 | 94 | 95 | ### Options 96 | 97 | #### Target Elements 98 | 99 | If you need to scroll to the top of an area that generates a vertical scroll bar, you can specify the id of an element 100 | of the scrollable area. Default is `window` for using the scroll position of the whole viewport. You can pass an options 101 | object in your application's `config/environment.js` file. 102 | 103 | ```javascript 104 | ENV['routerScroll'] = { 105 | scrollElement: '#mainScrollElement' 106 | }; 107 | ``` 108 | 109 | If you want to scroll to a target element on the page, you can specify the id or class of the element on the page. This 110 | is particularly useful if instead of scrolling to the top of the window, you want to scroll to the top of the main 111 | content area (that does not generate a vertical scrollbar). 112 | 113 | ```javascript 114 | ENV['routerScroll'] = { 115 | targetElement: '#main-target-element' // or .main-target-element 116 | }; 117 | ``` 118 | 119 | #### Scroll Timing 120 | 121 | You may want the default "out of the box" behaviour. We schedule scroll immediately after Ember's `render`. This occurs on the tightest schedule between route transition start and end. 122 | 123 | However, you have other options. If you need an extra tick after `render`, set `scrollWhenAfterRender: true`. You also may need to delay scroll functionality until the route is idle (approximately after the first paint completes) using `scrollWhenIdle: true` in your config. `scrollWhenIdle` && `scrollWhenAfterRender` defaults to `false`. 124 | 125 | This config property uses [`ember-app-scheduler`](https://github.com/ember-app-scheduler/ember-app-scheduler), so be sure to follow the instructions in the README. We include the `setupRouter` and `reset`. This all happens after `routeDidChange`. 126 | 127 | ```javascript 128 | ENV['routerScroll'] = { 129 | scrollWhenIdle: true // ember-app-scheduler 130 | }; 131 | ``` 132 | 133 | Or 134 | 135 | ```js 136 | ENV['routerScroll'] = { 137 | scrollWhenAfterRender: true // scheduleOnce('afterRender', ...) 138 | }; 139 | ``` 140 | I would suggest trying all of them out and seeing which works best for your app! 141 | 142 | 143 | ## A working example 144 | 145 | See [demo](https://dollarshaveclub.github.io/router-scroll-demo/) made by [Jon Chua](https://github.com/Chuabacca/). 146 | 147 | 148 | ## A visual demo 149 | 150 | ### Before 151 | 152 | ![before-scroll](https://cloud.githubusercontent.com/assets/4430436/17122972/0a1fe454-5295-11e6-937f-f1f5beab9d6b.gif) 153 | 154 | Notice that the in the full purple page, the user is sent to the **middle** of the page. 155 | 156 | 157 | ### After 158 | 159 | ![after-scroll](https://cloud.githubusercontent.com/assets/4430436/17122970/07c1a3a0-5295-11e6-977f-37eb955d95b1.gif) 160 | 161 | Notice that the in the full purple page, the user is sent to the **top** of the page. 162 | 163 | 164 | ## Issues with nested routes 165 | 166 | ### Before: 167 | 168 | ![before-preserve](https://cloud.githubusercontent.com/assets/4430436/17122971/0a1e34ce-5295-11e6-8d30-9f687dd69dbb.gif) 169 | 170 | Notice the unwanted scroll to top in this case. 171 | 172 | 173 | ### After: 174 | 175 | ![after-preserve](https://cloud.githubusercontent.com/assets/4430436/17122969/07acbb48-5295-11e6-9900-f9ba519affa4.gif) 176 | 177 | Adding a query parameter or controller property fixes this issue. 178 | 179 | 180 | ### preserveScrollPosition with queryParams 181 | 182 | In certain cases, you might want to have certain routes preserve scroll position when coming from a specific location. 183 | For example, inside your application, there is a way to get to a route where the user expects scroll position to be 184 | preserved (such as a tab section). 185 | 186 | **1.** Add query param in controller 187 | 188 | Add `preserveScrollPosition` as a queryParam in the controller for the route that needs to preserve the scroll position. 189 | 190 | Example: 191 | 192 | ```javascript 193 | import Controller from '@ember/controller'; 194 | 195 | export default class MyController extends Controller { 196 | queryParams = [ 197 | 'preserveScrollPosition', 198 | ]; 199 | } 200 | ``` 201 | 202 | **2.** Pass in query param 203 | 204 | Next, in the place where a transition is triggered, pass in `preserveScrollPosition=true`. For example 205 | 206 | ```handlebars 207 | 208 | ``` 209 | 210 | 211 | ### preserveScrollPosition with a controller property 212 | 213 | In other cases, you may have certain routes that always preserve scroll position, or routes where the controller can 214 | decide when to preserve scroll position. For instance, you may have some nested routes that have true nested UI where 215 | preserving scroll position is expected. Or you want a particular route to start off with the default scroll-to-top 216 | behavior but then preserve scroll position when query params change in response to user interaction. Using a controller 217 | property also allows the use of preserveScrollPosition without adding this to the query params. 218 | 219 | **1.** Add query param to controller 220 | 221 | Add `preserveScrollPosition` as a controller property for the route that needs to preserve the scroll position. 222 | In this example we have `preserveScrollPosition` initially set to false so that we get our normal scroll-to-top behavior 223 | when the route loads. Later on, when an action triggers a change to the `filter` query param, we also set 224 | `preserveScrollPosition` to true so that this user interaction does not trigger the scroll-to-top behavior. 225 | 226 | Example: 227 | 228 | ```javascript 229 | import Controller from '@ember/controller'; 230 | import { action } from '@ember/object'; 231 | 232 | export default class MyController extends Controller { 233 | queryParams = ['filter']; 234 | 235 | preserveScrollPosition = false; 236 | 237 | @action 238 | changeFilter(filter) { 239 | this.set('preserveScrollPosition', true); 240 | this.set('filter', filter); 241 | } 242 | } 243 | ``` 244 | 245 | **2.** Reset preserveScrollPosition if necessary 246 | 247 | If your controller is changing the preserveScrollPosition property, you'll probably need to reset 248 | `preserveScrollPosition` back to the default behavior whenever the controller is reset. This is not necessary on routes 249 | where `preserveScrollPosition` is always set to true. 250 | 251 | ```javascript 252 | import Router from '@ember/routing/route'; 253 | 254 | export default class MyRoute extends Route { 255 | resetController(controller) { 256 | controller.set('preserveScrollPosition', false); 257 | } 258 | } 259 | ``` 260 | 261 | 262 | ### preserveScrollPosition via service 263 | 264 | You may need to programatically control `preserveScrollPosition` directly from a component. This can be achieved by toggling the `preserveScrollPosition` property on the `routerScroll` service. 265 | 266 | One common use case for this is when using query-param-based pagination on a page where `preserveScrollPosition` is expected to be false. 267 | 268 | For example, if a route should always scroll to top when loaded, `preserveScrollPosition` would be false. However, a user may then scroll down the page and paginate through some results (where each page is a query param). But because `preserveScrollPosition` is false, the page will scroll back to top on each of these paginations. 269 | 270 | This can be fixed by temporarily setting `preserveScrollPosition` to true on the service in the pagination transition action and then disabling `preserveScrollPosition` after the transition occurs. 271 | 272 | Note: if `preserveScrollPosition` is set to true on the service, it will override any values set on the current route's controller - whether query param or controller property. 273 | 274 | 275 | **1.** Manage preserveScrollPosition via service 276 | 277 | When you need to modify `preserveScrollPosition` on the service for a specific transition, you should always reset the value after the transition occurs, otherwise all future transitions will use the same `preserveScrollPosition` value. 278 | 279 | Example: 280 | 281 | ```javascript 282 | import Component from '@glimmer/component'; 283 | import { inject as service } from '@ember/service'; 284 | import { action } from '@ember/object'; 285 | 286 | export default class MyComponent extends Component { 287 | @service routerScroll; 288 | @service router; 289 | 290 | @action 291 | async goToPaginationPage(pageNumber) { 292 | this.set('routerScroll.preserveScrollPosition', true); 293 | await this.router.transitionTo( 294 | this.router.currentRouteName, 295 | { 296 | queryParams: { page: pageNumber } 297 | } 298 | ); 299 | 300 | // Reset `preserveScrollPosition` after transition so future transitions behave as expected 301 | this.set('routerScroll.preserveScrollPosition', false); 302 | } 303 | } 304 | ``` 305 | 306 | ## Running Tests 307 | 308 | * `npm test` (Runs `ember try:testall` to test your addon against multiple Ember versions) 309 | * `ember test` 310 | * `ember test --serve 311 | 312 | License 313 | ------------------------------------------------------------------------------ 314 | 315 | This project is licensed under the [MIT License](LICENSE.md). 316 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/addon/.gitkeep -------------------------------------------------------------------------------- /addon/instance-initializers/ember-router-scroll.js: -------------------------------------------------------------------------------- 1 | export function initialize(appInstance) { 2 | // Eagerly initialize service 3 | appInstance.lookup('service:router-scroll'); 4 | } 5 | 6 | export default { 7 | initialize, 8 | }; 9 | -------------------------------------------------------------------------------- /addon/services/router-scroll.js: -------------------------------------------------------------------------------- 1 | import Service, { inject as service } from '@ember/service'; 2 | import { set, get, action } from '@ember/object'; 3 | import { typeOf } from '@ember/utils'; 4 | import { assert } from '@ember/debug'; 5 | import { getOwner } from '@ember/application'; 6 | import { scheduleOnce } from '@ember/runloop'; 7 | import { addListener, removeListener, sendEvent } from '@ember/object/events'; 8 | import { setupRouter, whenRouteIdle } from 'ember-app-scheduler'; 9 | 10 | let ATTEMPTS = 0; 11 | const MAX_ATTEMPTS = 100; // rAF runs every 16ms ideally, so 60x a second 12 | 13 | let requestId; 14 | let callbackRequestId; 15 | 16 | /** 17 | * By default, we start checking to see if the document height is >= the last known `y` position 18 | * we want to scroll to. This is important for content heavy pages that might try to scrollTo 19 | * before the content has painted 20 | * 21 | * @method tryScrollRecursively 22 | * @param {Function} fn 23 | * @param {Object} scrollHash 24 | * @param {Element} [element] 25 | * @param {string?} url 26 | * @void 27 | */ 28 | function tryScrollRecursively(fn, scrollHash, element, url) { 29 | let documentHeight; 30 | // read DOM outside of rAF 31 | if (element) { 32 | documentHeight = Math.max( 33 | element.scrollHeight, 34 | element.offsetHeight, 35 | element.clientHeight 36 | ); 37 | } else { 38 | const { body, documentElement: html } = document; 39 | documentHeight = Math.max( 40 | body.scrollHeight, 41 | body.offsetHeight, 42 | html.clientHeight, 43 | html.scrollHeight, 44 | html.offsetHeight 45 | ); 46 | } 47 | 48 | callbackRequestId = window.requestAnimationFrame(() => { 49 | if (url && url.indexOf('#') > -1) { 50 | const hashElement = document.getElementById(url.split('#').pop()); 51 | if (hashElement) { 52 | scrollHash = { x: hashElement.offsetLeft, y: hashElement.offsetTop }; 53 | } 54 | } 55 | // write DOM (scrollTo causes reflow) 56 | if (documentHeight >= scrollHash.y || ATTEMPTS >= MAX_ATTEMPTS) { 57 | ATTEMPTS = 0; 58 | fn.call(null, scrollHash.x, scrollHash.y); 59 | } else { 60 | ATTEMPTS++; 61 | tryScrollRecursively(fn, scrollHash, element, url); 62 | } 63 | }); 64 | } 65 | 66 | // to prevent scheduleOnce calling multiple times, give it the same ref to this function 67 | const CALLBACK = function (transition) { 68 | this.updateScrollPosition(transition); 69 | }; 70 | 71 | class RouterScroll extends Service { 72 | @service router; 73 | 74 | get isFastBoot() { 75 | const fastboot = getOwner(this).lookup('service:fastboot'); 76 | return fastboot ? fastboot.get('isFastBoot') : false; 77 | } 78 | 79 | key; 80 | targetElement; 81 | scrollElement = 'window'; 82 | isFirstLoad = true; 83 | preserveScrollPosition = false; 84 | // ember-app-scheduler properties 85 | scrollWhenIdle = false; 86 | scrollWhenAfterRender = false; 87 | 88 | constructor() { 89 | super(...arguments); 90 | 91 | // https://github.com/ember-app-scheduler/ember-app-scheduler/pull/773 92 | setupRouter(this.router); 93 | } 94 | 95 | // eslint-disable-next-line ember/classic-decorator-hooks 96 | init(...args) { 97 | super.init(...args); 98 | 99 | this._loadConfig(); 100 | set(this, 'scrollMap', { 101 | default: { 102 | x: 0, 103 | y: 0, 104 | }, 105 | }); 106 | 107 | addListener(this.router, 'routeWillChange', this._routeWillChange); 108 | addListener(this.router, 'routeDidChange', this._routeDidChange); 109 | } 110 | 111 | willDestroy() { 112 | removeListener(this.router, 'routeWillChange', this._routeWillChange); 113 | removeListener(this.router, 'routeDidChange', this._routeDidChange); 114 | 115 | if (requestId) { 116 | window.cancelAnimationFrame(requestId); 117 | } 118 | 119 | if (callbackRequestId) { 120 | window.cancelAnimationFrame(callbackRequestId); 121 | } 122 | 123 | super.willDestroy(...arguments); 124 | } 125 | 126 | /** 127 | * Updates the scroll position 128 | * it will be a single transition 129 | * @method updateScrollPosition 130 | * @param {transition|transition[]} transition If before Ember 3.6, this will be an array of transitions, otherwise 131 | */ 132 | updateScrollPosition(transition) { 133 | if (this.isFirstLoad) { 134 | this.unsetFirstLoad(); 135 | } 136 | 137 | let scrollPosition = this.position; 138 | 139 | // If `preserveScrollPosition` was not set on the controller, attempt fallback to `preserveScrollPosition` which was set on the router service. 140 | let preserveScrollPosition = 141 | (transition.router.currentRouteInfos || []).some( 142 | (routeInfo) => routeInfo.route.controller.preserveScrollPosition 143 | ) || this.preserveScrollPosition; 144 | 145 | if (!preserveScrollPosition) { 146 | const { scrollElement, targetElement, currentURL } = this; 147 | 148 | if (targetElement || 'window' === scrollElement) { 149 | tryScrollRecursively(window.scrollTo, scrollPosition, null, currentURL); 150 | } else if ('#' === scrollElement.charAt(0)) { 151 | const element = document.getElementById(scrollElement.substring(1)); 152 | 153 | if (element) { 154 | let fn = (x, y) => { 155 | element.scrollLeft = x; 156 | element.scrollTop = y; 157 | }; 158 | tryScrollRecursively(fn, scrollPosition, element, currentURL); 159 | } 160 | } 161 | } 162 | 163 | sendEvent(this, 'didScroll', transition); 164 | } 165 | 166 | @action 167 | _routeWillChange() { 168 | if (this.isFastBoot) { 169 | return; 170 | } 171 | 172 | this.update(); 173 | } 174 | 175 | @action 176 | _routeDidChange(transition) { 177 | if (this.isFastBoot) { 178 | return; 179 | } 180 | 181 | const scrollWhenIdle = this.scrollWhenIdle; 182 | const scrollWhenAfterRender = this.scrollWhenAfterRender; 183 | 184 | if (!scrollWhenIdle && !scrollWhenAfterRender) { 185 | // out of the option, this happens on the tightest schedule 186 | scheduleOnce('render', this, CALLBACK, transition); 187 | } else if (scrollWhenAfterRender && !scrollWhenIdle) { 188 | // out of the option, this happens on the second tightest schedule 189 | scheduleOnce('afterRender', this, CALLBACK, transition); 190 | } else { 191 | whenRouteIdle().then(() => { 192 | this.updateScrollPosition(transition); 193 | }); 194 | } 195 | } 196 | 197 | unsetFirstLoad() { 198 | set(this, 'isFirstLoad', false); 199 | } 200 | 201 | update() { 202 | if (this.isFastBoot || this.isFirstLoad) { 203 | return; 204 | } 205 | 206 | const scrollElement = this.scrollElement; 207 | const targetElement = this.targetElement; 208 | const scrollMap = this.scrollMap; 209 | const key = this.key; 210 | let x; 211 | let y; 212 | 213 | if (targetElement) { 214 | let element = document.querySelector(targetElement); 215 | if (element) { 216 | x = element.offsetLeft; 217 | y = element.offsetTop; 218 | 219 | // if we are looking to where to transition to next, we need to set the default to the position 220 | // of the targetElement on screen 221 | set(scrollMap, 'default', { 222 | x, 223 | y, 224 | }); 225 | } 226 | } else if ('window' === scrollElement) { 227 | x = window.scrollX; 228 | y = window.scrollY; 229 | } else if ('#' === scrollElement.charAt(0)) { 230 | let element = document.getElementById(scrollElement.substring(1)); 231 | 232 | if (element) { 233 | x = element.scrollLeft; 234 | y = element.scrollTop; 235 | } 236 | } 237 | 238 | // only a `key` present after first load 239 | if (key && 'number' === typeOf(x) && 'number' === typeOf(y)) { 240 | set(scrollMap, key, { 241 | x, 242 | y, 243 | }); 244 | } 245 | } 246 | 247 | _loadConfig() { 248 | const config = getOwner(this).resolveRegistration('config:environment'); 249 | 250 | if (config && config.routerScroll) { 251 | const scrollElement = config.routerScroll.scrollElement; 252 | const targetElement = config.routerScroll.targetElement; 253 | 254 | assert( 255 | 'You defined both scrollElement and targetElement in your config. We currently only support definining one of them', 256 | !(scrollElement && targetElement) 257 | ); 258 | 259 | if ('string' === typeOf(scrollElement)) { 260 | set(this, 'scrollElement', scrollElement); 261 | } 262 | 263 | if ('string' === typeOf(targetElement)) { 264 | set(this, 'targetElement', targetElement); 265 | } 266 | 267 | const { scrollWhenIdle = false, scrollWhenAfterRender = false } = 268 | config.routerScroll; 269 | set(this, 'scrollWhenIdle', scrollWhenIdle); 270 | set(this, 'scrollWhenAfterRender', scrollWhenAfterRender); 271 | } 272 | } 273 | } 274 | 275 | Object.defineProperty(RouterScroll.prototype, 'position', { 276 | configurable: true, 277 | get() { 278 | const scrollMap = this.scrollMap; 279 | const stateUuid = window.history.state?.uuid; 280 | 281 | set(this, 'key', stateUuid); 282 | const key = this.key || '-1'; 283 | 284 | return get(scrollMap, key) || scrollMap.default; 285 | }, 286 | }); 287 | 288 | export default RouterScroll; 289 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/app/.gitkeep -------------------------------------------------------------------------------- /app/instance-initializers/ember-router-scroll.js: -------------------------------------------------------------------------------- 1 | export { 2 | default, 3 | initialize, 4 | } from 'ember-router-scroll/instance-initializers/ember-router-scroll'; 5 | -------------------------------------------------------------------------------- /app/services/router-scroll.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-router-scroll/services/router-scroll'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | useYarn: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-3.12', 12 | npm: { 13 | devDependencies: { 14 | 'ember-source': '~3.12.0', 15 | }, 16 | }, 17 | }, 18 | { 19 | name: 'ember-lts-3.16', 20 | npm: { 21 | devDependencies: { 22 | 'ember-source': '~3.16.0', 23 | }, 24 | }, 25 | }, 26 | { 27 | name: 'ember-lts-3.20', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': '~3.20.5', 31 | }, 32 | }, 33 | }, 34 | { 35 | name: 'ember-lts-3.24', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': '~3.24.3', 39 | }, 40 | }, 41 | }, 42 | { 43 | name: 'ember-release', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': await getChannelURL('release'), 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'ember-beta', 52 | npm: { 53 | devDependencies: { 54 | 'ember-source': await getChannelURL('beta'), 55 | }, 56 | }, 57 | }, 58 | { 59 | name: 'ember-canary', 60 | npm: { 61 | devDependencies: { 62 | 'ember-source': await getChannelURL('canary'), 63 | }, 64 | }, 65 | }, 66 | { 67 | name: 'ember-default-with-jquery', 68 | env: { 69 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 70 | 'jquery-integration': true, 71 | }), 72 | }, 73 | npm: { 74 | devDependencies: { 75 | '@ember/jquery': '^1.1.0', 76 | }, 77 | }, 78 | }, 79 | { 80 | name: 'ember-classic', 81 | env: { 82 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 83 | 'application-template-wrapper': true, 84 | 'default-async-observers': false, 85 | 'template-only-glimmer-components': false, 86 | }), 87 | }, 88 | npm: { 89 | devDependencies: { 90 | 'ember-source': '~3.28.0', 91 | }, 92 | ember: { 93 | edition: 'classic', 94 | }, 95 | }, 96 | }, 97 | embroiderSafe(), 98 | embroiderOptimized(), 99 | ], 100 | }; 101 | }; 102 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (/* environment, appConfig */) { 4 | return {}; 5 | }; 6 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function (defaults) { 6 | let app = new EmberAddon(defaults, { 7 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | const { maybeEmbroider } = require('@embroider/test-setup'); 18 | return maybeEmbroider(app, { 19 | skipBabel: [ 20 | { 21 | package: 'qunit', 22 | }, 23 | ], 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name, 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-router-scroll", 3 | "version": "4.1.2", 4 | "description": "Scroll to top with preserved browser history scroll position", 5 | "keywords": [ 6 | "ember-addon", 7 | "ember-router-scroll", 8 | "popstateEvent", 9 | "browser scroll" 10 | ], 11 | "license": "MIT", 12 | "author": "Jason Farmer and Benny C. Wong ", 13 | "contributors": [ 14 | { 15 | "name": "Brian Cardarella", 16 | "url": "https://github.com/bcardarella" 17 | }, 18 | { 19 | "name": "Brian Gonzalez", 20 | "url": "https://github.com/briangonzalez" 21 | }, 22 | { 23 | "name": "Robert Wagner", 24 | "url": "https://github.com/rwwagner90" 25 | }, 26 | { 27 | "name": "Scott Newcomer", 28 | "url": "https://github.com/snewcomer" 29 | } 30 | ], 31 | "directories": { 32 | "doc": "doc", 33 | "test": "tests" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "https://github.com/dockyard/ember-router-scroll.git" 38 | }, 39 | "bugs": { 40 | "url": "https://github.com/dockyard/ember-router-scroll/issues" 41 | }, 42 | "homepage": "https://github.com/DockYard/ember-router-scroll", 43 | "scripts": { 44 | "build": "ember build --environment=production", 45 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", 46 | "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", 47 | "lint:hbs": "ember-template-lint .", 48 | "lint:hbs:fix": "ember-template-lint . --fix", 49 | "lint:js": "eslint . --cache", 50 | "lint:js:fix": "eslint . --fix", 51 | "start": "ember serve", 52 | "test": "npm-run-all lint test:*", 53 | "test:ember": "ember test", 54 | "test:ember-compatibility": "ember try:each", 55 | "deploy": "ember github-pages:commit --message \"Deploy gh-pages from commit $(git rev-parse HEAD)\"; git push; git checkout -", 56 | "postpublish": "git tag $npm_package_version && git push origin --tags", 57 | "update": "ember update --run-codemods" 58 | }, 59 | "dependencies": { 60 | "ember-app-scheduler": "^5.1.2 || ^6.0.0 || ^7.0.0", 61 | "ember-cli-babel": "^7.26.6", 62 | "ember-compatibility-helpers": "^1.2.5" 63 | }, 64 | "devDependencies": { 65 | "@ember/optional-features": "^2.0.0", 66 | "@ember/test-helpers": "^2.5.0", 67 | "@embroider/test-setup": "^0.47.0", 68 | "@glimmer/component": "^1.0.4", 69 | "@glimmer/tracking": "^1.0.4", 70 | "babel-eslint": "^10.1.0", 71 | "broccoli-asset-rev": "^3.0.0", 72 | "ember-auto-import": "^2.2.3", 73 | "ember-cli": "~3.28.3", 74 | "ember-cli-dependency-checker": "^3.2.0", 75 | "ember-cli-github-pages": "^0.2.0", 76 | "ember-cli-htmlbars": "^6.0.0", 77 | "ember-cli-inject-live-reload": "^2.1.0", 78 | "ember-cli-sri": "^2.1.1", 79 | "ember-cli-terser": "^4.0.2", 80 | "ember-decorators-polyfill": "^1.1.5", 81 | "ember-disable-prototype-extensions": "^1.1.3", 82 | "ember-export-application-global": "^2.0.1", 83 | "ember-load-initializers": "^2.1.2", 84 | "ember-maybe-import-regenerator": "^1.0.0", 85 | "ember-qunit": "^5.1.5", 86 | "ember-resolver": "^8.0.3", 87 | "ember-source": "~3.28.0", 88 | "ember-source-channel-url": "^3.0.0", 89 | "ember-template-lint": "^3.10.0", 90 | "ember-try": "^1.4.0", 91 | "eslint": "^7.32.0", 92 | "eslint-config-prettier": "^8.3.0", 93 | "eslint-plugin-ember": "^10.5.7", 94 | "eslint-plugin-node": "^11.1.0", 95 | "eslint-plugin-prettier": "^4.0.0", 96 | "eslint-plugin-qunit": "^7.0.0", 97 | "loader.js": "^4.7.0", 98 | "npm-run-all": "^4.1.5", 99 | "prettier": "^2.4.1", 100 | "qunit": "^2.17.2", 101 | "qunit-dom": "^2.0.0", 102 | "webpack": "^5.59.1" 103 | }, 104 | "engines": { 105 | "node": "12.* || 14.* || >= 16" 106 | }, 107 | "ember": { 108 | "edition": "octane" 109 | }, 110 | "ember-addon": { 111 | "configPath": "tests/dummy/config", 112 | "demoURL": "https://dollarshaveclub.github.io/ember-router-scroll/" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /tests/acceptance/basic-functionality-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupApplicationTest } from 'ember-qunit'; 3 | import { visit, click, currentURL } from '@ember/test-helpers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | module('Acceptance | basic functionality', function (hooks) { 7 | setupApplicationTest(hooks); 8 | 9 | hooks.beforeEach(function () { 10 | document.getElementById('ember-testing-container').scrollTop = 0; 11 | }); 12 | 13 | hooks.afterEach(function () { 14 | config['routerScroll'] = {}; 15 | }); 16 | 17 | test('The application should work when loading a page and clicking a link', async function (assert) { 18 | config['routerScroll'] = { 19 | scrollElement: '#ember-testing-container', 20 | }; 21 | 22 | await visit('/'); 23 | 24 | // testing specific 25 | let container = document.getElementById('ember-testing-container'); 26 | assert.strictEqual(container.scrollTop, 0); 27 | 28 | document.getElementById('monster').scrollIntoView(false); 29 | assert.ok(container.scrollTop > 0); 30 | 31 | await click('a[href="/next-page"]'); 32 | assert.strictEqual(currentURL(), '/next-page'); 33 | }); 34 | 35 | test('The application should work when loading a page and clicking a link to target an element to scroll to', async function (assert) { 36 | config['routerScroll'] = { 37 | scrollElement: '#target-main', 38 | }; 39 | 40 | await visit('/target'); 41 | 42 | // testing specific 43 | let container = document.getElementById('ember-testing-container'); 44 | assert.strictEqual(container.scrollTop, 0); 45 | 46 | document.getElementById('monster').scrollIntoView(false); 47 | assert.ok(container.scrollTop > 0); 48 | 49 | await click('a[href="/target-next-page"]'); 50 | assert.strictEqual(currentURL(), '/target-next-page'); 51 | }); 52 | 53 | test('The application should work when just changing query params', async function (assert) { 54 | config['routerScroll'] = { 55 | scrollElement: '#ember-testing-container', 56 | }; 57 | 58 | await visit('/'); 59 | 60 | let container = document.getElementById('ember-testing-container'); 61 | assert.strictEqual(container.scrollTop, 0); 62 | 63 | document.getElementById('monster').scrollIntoView(false); 64 | assert.ok(container.scrollTop > 0); 65 | 66 | await click('#change-size'); 67 | assert.ok(container.scrollTop > 0); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default class extends Controller { 4 | queryParams = ['small', 'preserveScrollPosition']; 5 | small = false; 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/not.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | 3 | export default class extends Helper { 4 | compute([value]) { 5 | return !value; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () { 10 | this.route('next-page'); 11 | this.route('target'); 12 | this.route('target-next-page'); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/routes/index.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class extends Route {} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/target-next-page.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class extends Route {} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/target.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class extends Route {} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=VT323); 2 | 3 | body { 4 | margin: 0; 5 | } 6 | 7 | #death-star { 8 | position:absolute; 9 | margin: 1550px auto 0 60%; 10 | width:75px; 11 | } 12 | 13 | #monster { 14 | bottom:0; 15 | position: absolute; 16 | margin: 0 auto 0 60%; 17 | width: 175px; 18 | } 19 | 20 | #ocean { 21 | background: linear-gradient(to top, black, #000055, #00DCFF); 22 | position: absolute; 23 | width:100%; 24 | height:3960px; 25 | } 26 | 27 | #space { 28 | background: linear-gradient(to top, #00DCFF, #000055, #000022, black); 29 | position: absolute; 30 | width:100%; 31 | height:3960px; 32 | } 33 | 34 | #stars { 35 | background: url(../images/stars.png) repeat; 36 | position: absolute; 37 | width:100%; 38 | height:3600px; 39 | } 40 | 41 | #submarine { 42 | display:block; 43 | position:fixed; 44 | top:50%; 45 | right:33%; 46 | width:150px; 47 | } 48 | 49 | div { 50 | color:white; 51 | font-family:vt323; 52 | } 53 | 54 | #intro { 55 | font-size: 32px; 56 | margin: 12% 10% -200px 10%; 57 | } 58 | 59 | #change-size { 60 | bottom: 0; 61 | color: white; 62 | padding: 16px; 63 | position: fixed; 64 | right: 0; 65 | } 66 | 67 | .small { 68 | transform: scale(0.5); 69 | } 70 | 71 | #nav { 72 | height: 44px; 73 | background-color: white; 74 | color: black; 75 | } 76 | 77 | .depth { 78 | font-size: 26px; 79 | } 80 | 81 | .flip { 82 | -moz-transform: scaleX(-1); 83 | -o-transform: scaleX(-1); 84 | -webkit-transform: scaleX(-1); 85 | transform: scaleX(-1); 86 | filter: FlipH; 87 | -ms-filter: "FlipH"; 88 | left:33%; 89 | } 90 | 91 | .small { 92 | color: #dddddd; 93 | font-size: 22px; 94 | } 95 | 96 | .left { 97 | margin: 300px 0 0 10%; 98 | } 99 | .right { 100 | margin: 300px 0 0 75%; 101 | } 102 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 | Click on the submarine to launch! 4 |
5 | 6 | 7 | Click here to change the size of the submarine 8 | 9 | 10 | 11 | Submarine 12 | 13 | 14 |
15 | — 500 fathoms 16 |
17 |
18 | — 1000 fathoms 19 |
20 |
21 | — 1500 fathoms 22 |
23 |
24 | — 2000 fathoms 25 |
26 |
27 | — 2500 fathoms 28 |
29 |
30 | — 3000 fathoms 31 |
32 |
33 | — 3500 fathoms 34 |
35 |
36 | — 4000 fathoms 37 |
38 |
39 | — 4500 fathoms 40 |
41 |
42 | — 5000 fathoms 43 |
44 | Monster 45 |
46 | {{outlet}} 47 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/next-page.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Orbital velocity achieved!
5 | Ember-router-scroll has taken you to the top of the page!
6 |
7 | 8 | Clicking the submarine will take you to the top of the previous page.
9 | Using the back button will take you to your previous scroll position. 10 |
11 |
12 | 13 | Submarine 14 | Death Star 15 |
16 | — Orbit -500 km 17 |
18 |
19 | — Orbit -1000 km 20 |
21 |
22 | — Orbit -1500 km 23 |
24 |
25 | — Orbit -2000 km 26 |
27 |
28 | — Orbit -2500 km 29 |
30 |
31 | — Orbit -3000 km 32 |
33 |
34 | — Orbit -3500 km 35 |
36 |
37 | — Orbit -4000 km 38 |
39 |
40 | — Orbit -4500 km 41 |
42 |
43 | — Orbit -5000 km 44 |
45 |
46 |
47 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/target-next-page.hbs: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 |
7 | Orbital velocity achieved!
8 | Ember-router-scroll has taken you to the top of the page!
9 |
10 | 11 | Clicking the submarine will take you to the top of the previous page.
12 | Using the back button will take you to your previous scroll position. 13 |
14 |
15 | Death Star 16 |
17 | — Orbit -500 km 18 |
19 |
20 | — Orbit -1000 km 21 |
22 |
23 | — Orbit -1500 km 24 |
25 |
26 | — Orbit -2000 km 27 |
28 |
29 | — Orbit -2500 km 30 |
31 |
32 | — Orbit -3000 km 33 |
34 |
35 | — Orbit -3500 km 36 |
37 |
38 | — Orbit -4000 km 39 |
40 |
41 | — Orbit -4500 km 42 |
43 |
44 | — Orbit -5000 km 45 |
46 |
47 |
48 | Submarine 49 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/target.hbs: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 |
7 | Click on the submarine to launch! 8 |
9 |
10 | — 500 fathoms 11 |
12 |
13 | — 1000 fathoms 14 |
15 |
16 | — 1500 fathoms 17 |
18 |
19 | — 2000 fathoms 20 |
21 |
22 | — 2500 fathoms 23 |
24 |
25 | — 3000 fathoms 26 |
27 |
28 | — 3500 fathoms 29 |
30 |
31 | — 4000 fathoms 32 |
33 |
34 | — 4500 fathoms 35 |
36 |
37 | — 5000 fathoms 38 |
39 | Monster 40 |
41 |
42 | Submarine 43 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "3.28.3", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--yarn", 15 | "--no-welcome" 16 | ] 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | historySupportMiddleware: true, 10 | EmberENV: { 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. 'with-controller': true 14 | }, 15 | EXTEND_PROTOTYPES: { 16 | // Prevent Ember Data from overriding Date.parse. 17 | Date: false, 18 | }, 19 | }, 20 | 21 | // routerScroll: { 22 | // targetElement: '#target-main' 23 | // }, 24 | 25 | APP: { 26 | // Here you can pass flags/options to your application instance 27 | // when it is created 28 | }, 29 | }; 30 | 31 | if (environment === 'development') { 32 | // ENV.APP.LOG_RESOLVER = true; 33 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 34 | // ENV.APP.LOG_TRANSITIONS = true; 35 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 36 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 37 | } 38 | 39 | if (environment === 'test') { 40 | // Testem prefers this... 41 | ENV.locationType = 'none'; 42 | 43 | // keep test console output quieter 44 | ENV.APP.LOG_ACTIVE_GENERATION = false; 45 | ENV.APP.LOG_VIEW_LOOKUPS = false; 46 | 47 | ENV.APP.rootElement = '#ember-testing'; 48 | ENV.APP.autoboot = false; 49 | } 50 | 51 | if (environment === 'production') { 52 | ENV.rootURL = '/ember-router-scroll/'; 53 | } 54 | 55 | return ENV; 56 | }; 57 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | // Ember's browser support policy is changing, and IE11 support will end in 10 | // v4.0 onwards. 11 | // 12 | // See https://deprecations.emberjs.com/v3.x#toc_3-0-browser-support-policy 13 | // 14 | // If you need IE11 support on a version of Ember that still offers support 15 | // for it, uncomment the code block below. 16 | // 17 | // const isCI = Boolean(process.env.CI); 18 | // const isProduction = process.env.EMBER_ENV === 'production'; 19 | // 20 | // if (isCI || isProduction) { 21 | // browsers.push('ie 11'); 22 | // } 23 | 24 | module.exports = { 25 | browsers, 26 | }; 27 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/images/death-star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/public/images/death-star.png -------------------------------------------------------------------------------- /tests/dummy/public/images/stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/public/images/stars.png -------------------------------------------------------------------------------- /tests/dummy/public/images/tentacle-monster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/public/images/tentacle-monster.png -------------------------------------------------------------------------------- /tests/dummy/public/images/yellow_submarine.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/dummy/public/images/yellow_submarine.gif -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix, 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | import { merge } from '@ember/polyfills'; 3 | import Application from '../../app'; 4 | import config from '../../config/environment'; 5 | 6 | export default function startApp(attrs) { 7 | let application; 8 | 9 | let attributes = merge({}, config.APP); 10 | attributes = merge(attributes, attrs); // use defaults, but you can override; 11 | 12 | run(() => { 13 | application = Application.create(attributes); 14 | application.setupForTesting(); 15 | application.injectTestHelpers(); 16 | }); 17 | 18 | return application; 19 | } 20 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ember Router Scroll 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{content-for "body-footer"}} 38 | {{content-for "test-body-footer"}} 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | 8 | setApplication(Application.create(config.APP)); 9 | 10 | setup(QUnit.assert); 11 | 12 | start(); 13 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/router-scroll-test.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | import { module, test } from 'qunit'; 3 | import { setupTest } from 'ember-qunit'; 4 | import { settled } from '@ember/test-helpers'; 5 | 6 | let scrollTo, subject; 7 | 8 | module('router-scroll', function (hooks) { 9 | setupTest(hooks); 10 | 11 | hooks.beforeEach(function () { 12 | scrollTo = window.scrollTo; 13 | }); 14 | 15 | hooks.afterEach(function () { 16 | window.scrollTo = scrollTo; 17 | }); 18 | 19 | function getTransitionsMock(URL, isPreserveScroll) { 20 | Object.defineProperty(subject, 'currentURL', { 21 | value: URL || 'Hello/World', 22 | }); 23 | 24 | const transition = { 25 | handler: { 26 | controller: { 27 | preserveScrollPosition: isPreserveScroll || false, 28 | }, 29 | }, 30 | router: { 31 | currentRouteInfos: [ 32 | { 33 | route: { 34 | controller: { 35 | preserveScrollPosition: isPreserveScroll || false, 36 | }, 37 | }, 38 | }, 39 | ], 40 | }, 41 | }; 42 | 43 | return transition; 44 | } 45 | 46 | test('when the application is FastBooted', async function (assert) { 47 | assert.expect(1); 48 | const done = assert.async(); 49 | 50 | this.owner.register( 51 | 'service:fastboot', 52 | class extends EmberObject { 53 | isFastBoot = true; 54 | } 55 | ); 56 | const routerScrollService = this.owner.lookup('service:router-scroll'); 57 | routerScrollService.updateScrollPosition = () => { 58 | assert.notOk(true, 'it should not call updateScrollPosition.'); 59 | done(); 60 | }; 61 | 62 | subject = this.owner.lookup('service:router'); 63 | subject.trigger('routeDidChange'); 64 | 65 | assert.ok(true, 'it should not call updateScrollPosition.'); 66 | await settled(); 67 | done(); 68 | }); 69 | 70 | test('when the application is not FastBooted', function (assert) { 71 | assert.expect(1); 72 | const done = assert.async(); 73 | 74 | this.owner.register( 75 | 'service:fastboot', 76 | class extends EmberObject { 77 | isFastBoot = false; 78 | } 79 | ); 80 | const routerScrollService = this.owner.lookup('service:router-scroll'); 81 | routerScrollService.scrollWhenIdle = false; 82 | routerScrollService.updateScrollPosition = () => { 83 | assert.ok(true, 'it should call updateScrollPosition.'); 84 | done(); 85 | }; 86 | 87 | subject = this.owner.lookup('service:router'); 88 | subject.trigger('routeDidChange'); 89 | }); 90 | 91 | test('when the application is not FastBooted with targetElement', function (assert) { 92 | assert.expect(1); 93 | const done = assert.async(); 94 | 95 | this.owner.register( 96 | 'service:fastboot', 97 | class extends EmberObject { 98 | isFastBoot = false; 99 | } 100 | ); 101 | const routerScrollService = this.owner.lookup('service:router-scroll'); 102 | routerScrollService.targetElement = '#myElement'; 103 | routerScrollService.updateScrollPosition = () => { 104 | assert.ok(true, 'it should call updateScrollPosition.'); 105 | done(); 106 | }; 107 | 108 | subject = this.owner.lookup('service:router'); 109 | subject.trigger('routeDidChange'); 110 | }); 111 | 112 | test('when the application is not FastBooted with scrollWhenIdle', function (assert) { 113 | assert.expect(1); 114 | const done = assert.async(); 115 | 116 | this.owner.register( 117 | 'service:fastboot', 118 | class extends EmberObject { 119 | isFastBoot = false; 120 | } 121 | ); 122 | const routerScrollService = this.owner.lookup('service:router-scroll'); 123 | routerScrollService.scrollWhenIdle = true; 124 | routerScrollService.updateScrollPosition = () => { 125 | assert.ok(true, 'it should call updateScrollPosition.'); 126 | done(); 127 | }; 128 | 129 | subject = this.owner.lookup('service:router'); 130 | subject.trigger('routeDidChange'); 131 | }); 132 | 133 | test('when the application is not FastBooted with scrollWhenAfterRender', function (assert) { 134 | assert.expect(1); 135 | const done = assert.async(); 136 | 137 | this.owner.register( 138 | 'service:fastboot', 139 | class extends EmberObject { 140 | isFastBoot = false; 141 | } 142 | ); 143 | const routerScrollService = this.owner.lookup('service:router-scroll'); 144 | routerScrollService.scrollWhenAfterRender = true; 145 | routerScrollService.updateScrollPosition = () => { 146 | assert.ok(true, 'it should call updateScrollPosition.'); 147 | done(); 148 | }; 149 | 150 | subject = this.owner.lookup('service:router'); 151 | subject.trigger('routeDidChange'); 152 | }); 153 | 154 | test('Update Scroll Position: Position is preserved', async function (assert) { 155 | assert.expect(0); 156 | 157 | window.scrollTo = () => { 158 | assert.ok(false, 'Scroll To should not be called'); 159 | }; 160 | 161 | this.owner.register( 162 | 'service:fastboot', 163 | class extends EmberObject { 164 | isFastBoot = false; 165 | } 166 | ); 167 | const routerScrollService = this.owner.lookup('service:router-scroll'); 168 | Object.defineProperty(routerScrollService, 'position', { 169 | get position() { 170 | return { x: 0, y: 0 }; 171 | }, 172 | }); 173 | routerScrollService.scrollElement = 'window'; 174 | 175 | subject = this.owner.lookup('service:router'); 176 | subject.trigger('routeDidChange', getTransitionsMock('Hello/World', true)); 177 | await settled(); 178 | }); 179 | 180 | test('Update Scroll Position: Can preserve position using routerService', async function (assert) { 181 | assert.expect(0); 182 | 183 | window.scrollTo = () => { 184 | assert.ok(false, 'Scroll To should not be called'); 185 | }; 186 | 187 | const routerScrollService = this.owner.lookup('service:router-scroll'); 188 | routerScrollService.preserveScrollPosition = true; 189 | 190 | subject = this.owner.lookup('service:router'); 191 | subject.trigger('routeDidChange', getTransitionsMock('Hello/World', true)); 192 | await settled(); 193 | }); 194 | 195 | test('Update Scroll Position: URL is an anchor', async function (assert) { 196 | assert.expect(2); 197 | const done = assert.async(); 198 | 199 | const elem = document.createElement('div'); 200 | elem.id = 'World'; 201 | document.body.insertBefore(elem, null); 202 | window.scrollTo = (x, y) => { 203 | assert.strictEqual( 204 | x, 205 | elem.offsetLeft, 206 | `Scroll to called with correct horizontal offset x: ${x} === ${elem.offsetLeft}` 207 | ); 208 | assert.strictEqual( 209 | y, 210 | elem.offsetTop, 211 | `Scroll to called with correct vertical offset y: ${y} === ${elem.offsetTop}` 212 | ); 213 | done(); 214 | }; 215 | 216 | this.owner.register( 217 | 'service:fastboot', 218 | class extends EmberObject { 219 | isFastBoot = false; 220 | } 221 | ); 222 | const routerScrollService = this.owner.lookup('service:router-scroll'); 223 | routerScrollService.currentURL = '#World'; 224 | Object.defineProperty(routerScrollService, 'position', { 225 | get() { 226 | return { x: 0, y: 0 }; 227 | }, 228 | }); 229 | routerScrollService.scrollElement = 'window'; 230 | 231 | subject = this.owner.lookup('service:router'); 232 | subject.trigger( 233 | 'routeDidChange', 234 | getTransitionsMock('Hello/#World', false) 235 | ); 236 | }); 237 | 238 | test('Update Scroll Position: URL has nothing after an anchor', function (assert) { 239 | assert.expect(2); 240 | const done = assert.async(); 241 | 242 | window.scrollTo = (x, y) => { 243 | assert.strictEqual( 244 | x, 245 | 1, 246 | 'Scroll to called with correct horizontal offset' 247 | ); 248 | assert.strictEqual(y, 2, 'Scroll to called with correct vertical offset'); 249 | done(); 250 | }; 251 | 252 | this.owner.register( 253 | 'service:fastboot', 254 | class extends EmberObject { 255 | isFastBoot = false; 256 | } 257 | ); 258 | const routerScrollService = this.owner.lookup('service:router-scroll'); 259 | Object.defineProperty(routerScrollService, 'position', { 260 | value: { x: 1, y: 2 }, 261 | }); 262 | routerScrollService.scrollElement = 'window'; 263 | 264 | subject = this.owner.lookup('service:router'); 265 | subject.trigger('routeDidChange', getTransitionsMock('Hello/#')); 266 | }); 267 | 268 | test('Update Scroll Position: URL has nonexistent element after anchor', function (assert) { 269 | assert.expect(2); 270 | const done = assert.async(); 271 | 272 | const elem = document.createElement('div'); 273 | elem.id = 'World'; 274 | document.body.insertBefore(elem, null); 275 | window.scrollTo = (x, y) => { 276 | assert.strictEqual( 277 | x, 278 | 1, 279 | 'Scroll to called with correct horizontal offset' 280 | ); 281 | assert.strictEqual(y, 2, 'Scroll to called with correct vertical offset'); 282 | done(); 283 | }; 284 | 285 | this.owner.register( 286 | 'service:fastboot', 287 | class extends EmberObject { 288 | isFastBoot = false; 289 | } 290 | ); 291 | const routerScrollService = this.owner.lookup('service:router-scroll'); 292 | Object.defineProperty(routerScrollService, 'position', { 293 | value: { x: 1, y: 2 }, 294 | }); 295 | routerScrollService.scrollElement = 'window'; 296 | 297 | subject = this.owner.lookup('service:router'); 298 | subject.trigger('routeDidChange', getTransitionsMock('Hello/#Bar')); 299 | }); 300 | 301 | test('Update Scroll Position: Scroll Position is set by service', function (assert) { 302 | assert.expect(2); 303 | const done = assert.async(); 304 | 305 | window.scrollTo = (x, y) => { 306 | assert.strictEqual( 307 | x, 308 | 1, 309 | 'Scroll to called with correct horizontal offset' 310 | ); 311 | assert.strictEqual( 312 | y, 313 | 20, 314 | 'Scroll to called with correct vertical offset' 315 | ); 316 | done(); 317 | }; 318 | 319 | this.owner.register( 320 | 'service:fastboot', 321 | class extends EmberObject { 322 | isFastBoot = false; 323 | } 324 | ); 325 | const routerScrollService = this.owner.lookup('service:router-scroll'); 326 | Object.defineProperty(routerScrollService, 'position', { 327 | value: { x: 1, y: 20 }, 328 | }); 329 | routerScrollService.scrollElement = 'window'; 330 | 331 | subject = this.owner.lookup('service:router'); 332 | subject.trigger('routeDidChange', getTransitionsMock('Hello/World')); 333 | }); 334 | }); 335 | -------------------------------------------------------------------------------- /tests/unit/services/router-scroll-test.js: -------------------------------------------------------------------------------- 1 | import EmberObject, { set } from '@ember/object'; 2 | import { module, test } from 'qunit'; 3 | import { setupTest } from 'ember-qunit'; 4 | 5 | const defaultScrollMap = { 6 | default: { 7 | x: 0, 8 | y: 0, 9 | }, 10 | }; 11 | 12 | module('service:router-scroll', function (hooks) { 13 | setupTest(hooks); 14 | 15 | test('it inits `scrollMap` and `key`', function (assert) { 16 | const service = this.owner.lookup('service:router-scroll'); 17 | assert.deepEqual(service.scrollMap, defaultScrollMap); 18 | assert.deepEqual(service.key, undefined); 19 | }); 20 | 21 | test('it inits `scrollMap` and `key` with scrollElement other than window', function (assert) { 22 | const service = this.owner 23 | .factoryFor('service:router-scroll') 24 | .create({ scrollElement: '#other-elem' }); 25 | assert.deepEqual(service.scrollMap, defaultScrollMap); 26 | assert.deepEqual(service.key, undefined); 27 | }); 28 | 29 | test('updating will not set `scrollMap` to the current scroll position if `key` is not yet set', function (assert) { 30 | const service = this.owner.lookup('service:router-scroll'); 31 | 32 | service.update(); 33 | assert.deepEqual(service.scrollMap, defaultScrollMap); 34 | }); 35 | 36 | test('updating will set `scrollMap` to the current scroll position', function (assert) { 37 | const service = this.owner 38 | .factoryFor('service:router-scroll') 39 | .create({ isFirstLoad: false }); 40 | 41 | const expected = { x: window.scrollX, y: window.scrollY }; 42 | set(service, 'key', '123'); 43 | service.update(); 44 | assert.deepEqual(service.scrollMap, { 45 | 123: expected, 46 | default: { x: 0, y: 0 }, 47 | }); 48 | }); 49 | 50 | test('updating will not set `scrollMap` if scrollElement is defined and element is not found', function (assert) { 51 | const service = this.owner 52 | .factoryFor('service:router-scroll') 53 | .create({ scrollElement: '#other-elem' }); 54 | 55 | service.update(); 56 | const expected = { x: 0, y: 0 }; 57 | assert.deepEqual(service.position, expected); 58 | assert.deepEqual(service.scrollMap, defaultScrollMap); 59 | }); 60 | 61 | test('updating will not set `scrollMap` if targetElement is defined and element is not found', function (assert) { 62 | const service = this.owner 63 | .factoryFor('service:router-scroll') 64 | .create({ targetElement: '#other-elem' }); 65 | 66 | service.update(); 67 | const expected = { x: 0, y: 0 }; 68 | assert.deepEqual(service.position, expected); 69 | assert.deepEqual(service.scrollMap, defaultScrollMap); 70 | }); 71 | 72 | test('updating will not set `scrollMap` if scrollElement is defined and in fastboot', function (assert) { 73 | const otherElem = document.createElement('div'); 74 | otherElem.setAttribute('id', 'other-elem'); 75 | const testing = document.querySelector('#ember-testing'); 76 | testing.appendChild(otherElem); 77 | this.owner.register( 78 | 'service:fastboot', 79 | class extends EmberObject { 80 | isFastBoot = true; 81 | } 82 | ); 83 | const service = this.owner 84 | .factoryFor('service:router-scroll') 85 | .create({ scrollElement: '#other-elem' }); 86 | window.history.replaceState({ uuid: '123' }, null); 87 | 88 | let expected = { x: 0, y: 0 }; 89 | assert.deepEqual(service.position, expected, 'position is defaulted'); 90 | service.update(); 91 | assert.deepEqual( 92 | service.scrollMap, 93 | defaultScrollMap, 94 | 'does not set scrollMap b/c in fastboot' 95 | ); 96 | }); 97 | 98 | test('updating will not set `scrollMap` if targetElement is defined and in fastboot', function (assert) { 99 | const otherElem = document.createElement('div'); 100 | otherElem.setAttribute('id', 'other-elem'); 101 | const testing = document.querySelector('#ember-testing'); 102 | testing.appendChild(otherElem); 103 | this.owner.register( 104 | 'service:fastboot', 105 | class extends EmberObject { 106 | isFastBoot = true; 107 | } 108 | ); 109 | const service = this.owner 110 | .factoryFor('service:router-scroll') 111 | .create({ targetElement: '#other-elem' }); 112 | window.history.replaceState({ uuid: '123' }, null); 113 | 114 | let expected = { x: 0, y: 0 }; 115 | assert.deepEqual(service.position, expected, 'position is defaulted'); 116 | service.update(); 117 | assert.deepEqual( 118 | service.scrollMap, 119 | defaultScrollMap, 120 | 'does not set scrollMap b/c in fastboot' 121 | ); 122 | }); 123 | 124 | test('updating will set `scrollMap` if scrollElement is defined', function (assert) { 125 | const otherElem = document.createElement('div'); 126 | otherElem.setAttribute('id', 'other-elem'); 127 | const testing = document.querySelector('#ember-testing'); 128 | testing.appendChild(otherElem); 129 | const service = this.owner 130 | .factoryFor('service:router-scroll') 131 | .create({ isFirstLoad: false, scrollElement: '#other-elem' }); 132 | window.history.replaceState({ uuid: '123' }, null); 133 | 134 | let expected = { x: 0, y: 0 }; 135 | assert.deepEqual(service.position, expected, 'position is defaulted'); 136 | service.update(); 137 | assert.deepEqual( 138 | service.scrollMap, 139 | { 140 | 123: expected, 141 | default: { x: 0, y: 0 }, 142 | }, 143 | 'sets scrollMap' 144 | ); 145 | }); 146 | 147 | test('updating will set default `scrollMap` if targetElement is defined', function (assert) { 148 | const otherElem = document.createElement('div'); 149 | otherElem.setAttribute('id', 'other-elem'); 150 | otherElem.style.position = 'relative'; 151 | otherElem.style.top = '100px'; 152 | const testing = document.querySelector('#ember-testing'); 153 | testing.appendChild(otherElem); 154 | const service = this.owner 155 | .factoryFor('service:router-scroll') 156 | .create({ isFirstLoad: false, targetElement: '#other-elem' }); 157 | window.history.replaceState({ uuid: '123' }, null); 158 | 159 | let expected = { x: 0, y: 0 }; 160 | assert.deepEqual(service.position, expected, 'position is defaulted'); 161 | service.update(); 162 | assert.deepEqual( 163 | service.scrollMap, 164 | { 123: { x: 0, y: 100 }, default: { x: 0, y: 100 } }, 165 | 'sets scrollMap' 166 | ); 167 | }); 168 | 169 | test('computing the position for an existing state uuid return the coords', function (assert) { 170 | const service = this.owner.lookup('service:router-scroll'); 171 | window.history.replaceState({ uuid: '123' }, null); 172 | 173 | const expected = { x: 1, y: 1 }; 174 | set(service, 'scrollMap.123', expected); 175 | assert.deepEqual(service.position, expected); 176 | }); 177 | 178 | test('computing the position for a state without a cached scroll position returns default', function (assert) { 179 | const service = this.owner.lookup('service:router-scroll'); 180 | const state = window.history.state; 181 | window.history.replaceState({ uuid: '123' }, null); 182 | 183 | const expected = { x: 0, y: 0 }; 184 | assert.deepEqual(service.position, expected); 185 | window.history.replaceState(state, null); 186 | }); 187 | 188 | test('computing the position for a non-existant state returns default', function (assert) { 189 | const service = this.owner.lookup('service:router-scroll'); 190 | 191 | const expected = { x: 0, y: 0 }; 192 | assert.deepEqual(service.position, expected); 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DockYard/ember-router-scroll/2f17728fd82d7d486888df9a09fdc4aae233f294/vendor/.gitkeep --------------------------------------------------------------------------------