├── .babelrc ├── .circleci └── config.yml ├── .editorconfig ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── examples ├── modal │ ├── .gitignore │ ├── README.md │ ├── bundle.js │ ├── index.html │ ├── main.js │ ├── now.json │ ├── package.json │ └── yarn.lock └── vanilla │ ├── .gitignore │ ├── README.md │ ├── bundle.js │ ├── index.html │ ├── main.js │ ├── now.json │ ├── package.json │ └── yarn.lock ├── flow-typed └── globals.js ├── images └── logo.png ├── lib ├── bodyScrollLock.es6.js ├── bodyScrollLock.esm.js ├── bodyScrollLock.js └── bodyScrollLock.min.js ├── package.json ├── src └── bodyScrollLock.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-flow-strip-types"], 3 | "env": { 4 | "umd": { 5 | "presets": [ 6 | "flow", 7 | [ 8 | "env", 9 | { 10 | "modules": "umd" 11 | } 12 | ], 13 | "stage-0" 14 | ] 15 | }, 16 | "module": { 17 | "presets": [ 18 | "flow", 19 | [ 20 | "env", 21 | { 22 | "modules": false 23 | } 24 | ], 25 | "stage-0" 26 | ] 27 | }, 28 | "es6": {} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:7.10 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | - run: yarn test 38 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "plugin:flowtype/recommended", "prettier"], 3 | "rules": { 4 | "flowtype/define-flow-type": 2, 5 | "arrow-parens": 0, 6 | "no-param-reassign": 0, 7 | "max-len": 0, 8 | "no-confusing-arrow": 0 9 | }, 10 | "parser": "babel-eslint", 11 | "plugins": ["flowtype"], 12 | "globals": { 13 | "window": true, 14 | "document": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | 4 | [include] 5 | .*/src 6 | 7 | [libs] 8 | .*/flow-typed 9 | 10 | [options] 11 | module.system.node.resolve_dirname=node_modules 12 | 13 | module.file_ext=.js 14 | 15 | suppress_comment=\\(.\\|\n\\)*\\$FlowIgnoreLine 16 | 17 | [lints] 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea/* 3 | yarn-error.log 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example/ 2 | .circleci/ 3 | test/ 4 | .babelrc 5 | .flowconfig 6 | .npmignore 7 | yarn-error.log 8 | yarn.lock 9 | .idea/ 10 | CONTRIBUTING.md 11 | .eslintrc 12 | .editorconfig 13 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | lib 2 | examples/vanilla/bundle.js 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "printWidth": 120 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Will Po 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Body scroll lock...just works with everything ;-)

2 | 3 | ## Why BSL? 4 | 5 | Enables body scroll locking (for iOS Mobile and Tablet, Android, desktop Safari/Chrome/Firefox) without breaking scrolling of a target element (eg. modal/lightbox/flyouts/nav-menus). 6 | 7 | _Features:_ 8 | 9 | - disables body scroll WITHOUT disabling scroll of a target element 10 | - works on iOS mobile/tablet (!!) 11 | - works on Android 12 | - works on Safari desktop 13 | - works on Chrome/Firefox 14 | - works with vanilla JS and frameworks such as React / Angular / VueJS 15 | - supports nested target elements (eg. a modal that appears on top of a flyout) 16 | - can reserve scrollbar width 17 | - `-webkit-overflow-scrolling: touch` still works 18 | 19 | _Aren't the alternative approaches sufficient?_ 20 | 21 | - the approach `document.body.ontouchmove = (e) => { e.preventDefault(); return false; };` locks the 22 | body scroll, but ALSO locks the scroll of a target element (eg. modal). 23 | - the approach `overflow: hidden` on the body or html elements doesn't work for all browsers 24 | - the `position: fixed` approach causes the body scroll to reset 25 | - some approaches break inertia/momentum/rubber-band scrolling on iOS 26 | 27 | _LIGHT Package Size:_ 28 | 29 | [![minzip size](https://badgen.net/bundlephobia/minzip/body-scroll-lock?color=orange)](https://badgen.net/bundlephobia/minzip/body-scroll-lock?color=orange) 30 | 31 | ## Install 32 | 33 | $ yarn add body-scroll-lock 34 | 35 | or 36 | 37 | $ npm install body-scroll-lock 38 | 39 | You can also load via a `` tag (refer to the lib folder). 40 | 41 | ## Usage examples 42 | 43 | ##### Common JS 44 | 45 | ```javascript 46 | // 1. Import the functions 47 | const bodyScrollLock = require('body-scroll-lock'); 48 | const disableBodyScroll = bodyScrollLock.disableBodyScroll; 49 | const enableBodyScroll = bodyScrollLock.enableBodyScroll; 50 | 51 | // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 52 | // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 53 | // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 54 | const targetElement = document.querySelector('#someElementId'); 55 | 56 | // 3. ...in some event handler after showing the target element...disable body scroll 57 | disableBodyScroll(targetElement); 58 | 59 | // 4. ...in some event handler after hiding the target element... 60 | enableBodyScroll(targetElement); 61 | ``` 62 | 63 | ##### React/ES6 64 | 65 | ```javascript 66 | // 1. Import the functions 67 | import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'; 68 | 69 | class SomeComponent extends React.Component { 70 | targetElement = null; 71 | 72 | componentDidMount() { 73 | // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 74 | // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 75 | // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 76 | this.targetElement = document.querySelector('#targetElementId'); 77 | } 78 | 79 | showTargetElement = () => { 80 | // ... some logic to show target element 81 | 82 | // 3. Disable body scroll 83 | disableBodyScroll(this.targetElement); 84 | }; 85 | 86 | hideTargetElement = () => { 87 | // ... some logic to hide target element 88 | 89 | // 4. Re-enable body scroll 90 | enableBodyScroll(this.targetElement); 91 | }; 92 | 93 | componentWillUnmount() { 94 | // 5. Useful if we have called disableBodyScroll for multiple target elements, 95 | // and we just want a kill-switch to undo all that. 96 | // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor 97 | // clicks a link which takes him/her to a different page within the app. 98 | clearAllBodyScrollLocks(); 99 | } 100 | 101 | render() { 102 | return
some JSX to go here
; 103 | } 104 | } 105 | ``` 106 | 107 | ##### React/ES6 with Refs 108 | 109 | ```javascript 110 | // 1. Import the functions 111 | import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'; 112 | 113 | class SomeComponent extends React.Component { 114 | // 2. Initialise your ref and targetElement here 115 | targetRef = React.createRef(); 116 | targetElement = null; 117 | 118 | componentDidMount() { 119 | // 3. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 120 | // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 121 | // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 122 | this.targetElement = this.targetRef.current; 123 | } 124 | 125 | showTargetElement = () => { 126 | // ... some logic to show target element 127 | 128 | // 4. Disable body scroll 129 | disableBodyScroll(this.targetElement); 130 | }; 131 | 132 | hideTargetElement = () => { 133 | // ... some logic to hide target element 134 | 135 | // 5. Re-enable body scroll 136 | enableBodyScroll(this.targetElement); 137 | }; 138 | 139 | componentWillUnmount() { 140 | // 5. Useful if we have called disableBodyScroll for multiple target elements, 141 | // and we just want a kill-switch to undo all that. 142 | // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor 143 | // clicks a link which takes him/her to a different page within the app. 144 | clearAllBodyScrollLocks(); 145 | } 146 | 147 | render() { 148 | return ( 149 | // 6. Pass your ref with the reference to the targetElement to SomeOtherComponent 150 | some JSX to go here 151 | ); 152 | } 153 | } 154 | 155 | // 7. SomeOtherComponent needs to be a Class component to receive the ref (unless Hooks - https://reactjs.org/docs/hooks-faq.html#can-i-make-a-ref-to-a-function-component - are used). 156 | class SomeOtherComponent extends React.Component { 157 | componentDidMount() { 158 | // Your logic on mount goes here 159 | } 160 | 161 | // 8. BSL will be applied to div below in SomeOtherComponent and persist scrolling for the container 162 | render() { 163 | return
some JSX to go here
; 164 | } 165 | } 166 | ``` 167 | 168 | 169 | ##### Angular 170 | 171 | ```javascript 172 | import { Component, ElementRef, OnDestroy, ViewChild } from "@angular/core"; 173 | 174 | // 1. Import the functions 175 | import { 176 | disableBodyScroll, 177 | enableBodyScroll, 178 | clearAllBodyScrollLocks 179 | } from "body-scroll-lock"; 180 | 181 | @Component({ 182 | selector: "app-scroll-block", 183 | templateUrl: "./scroll-block.component.html", 184 | styleUrls: ["./scroll-block.component.css"] 185 | }) 186 | export class SomeComponent implements OnDestroy { 187 | // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 188 | // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 189 | // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 190 | @ViewChild("scrollTarget") scrollTarget: ElementRef; 191 | 192 | showTargetElement() { 193 | // ... some logic to show target element 194 | 195 | // 3. Disable body scroll 196 | disableBodyScroll(this.scrollTarget.nativeElement); 197 | } 198 | 199 | hideTargetElement() { 200 | // ... some logic to hide target element 201 | 202 | // 4. Re-enable body scroll 203 | enableBodyScroll(this.scrollTarget.nativeElement); 204 | } 205 | 206 | ngOnDestroy() { 207 | // 5. Useful if we have called disableBodyScroll for multiple target elements, 208 | // and we just want a kill-switch to undo all that. 209 | // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor 210 | // clicks a link which takes him/her to a different page within the app. 211 | clearAllBodyScrollLocks(); 212 | } 213 | } 214 | 215 | ``` 216 | 217 | ##### Vanilla JS 218 | 219 | In the html: 220 | 221 | ```html 222 | 223 | 224 | 225 | ``` 226 | 227 | Then in the javascript: 228 | 229 | ```javascript 230 | // 1. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 231 | // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 232 | // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 233 | const targetElement = document.querySelector('#someElementId'); 234 | 235 | // 2. ...in some event handler after showing the target element...disable body scroll 236 | bodyScrollLock.disableBodyScroll(targetElement); 237 | 238 | // 3. ...in some event handler after hiding the target element... 239 | bodyScrollLock.enableBodyScroll(targetElement); 240 | 241 | // 4. Useful if we have called disableBodyScroll for multiple target elements, 242 | // and we just want a kill-switch to undo all that. 243 | bodyScrollLock.clearAllBodyScrollLocks(); 244 | ``` 245 | 246 | ## Demo 247 | 248 | Check out the demo, powered by Vercel. 249 | 250 | * https://bodyscrolllock.vercel.app for a basic example 251 | * https://bodyscrolllock-modal.vercel.app for an example with a modal. 252 | 253 | ## Functions 254 | 255 | | Function | Arguments | Return | Description | 256 | | :------------------------ | :------------------------------------------------------------- | :----: | :----------------------------------------------------------- | 257 | | `disableBodyScroll` | `targetElement: HTMLElement`
`options: BodyScrollOptions` | `void` | Disables body scroll while enabling scroll on target element | 258 | | `enableBodyScroll` | `targetElement: HTMLElement` | `void` | Enables body scroll and removing listeners on target element | 259 | | `clearAllBodyScrollLocks` | `null` | `void` | Clears all scroll locks | 260 | 261 | ## Options 262 | 263 | ### reserveScrollBarGap 264 | 265 | **optional, default:** false 266 | 267 | If the overflow property of the body is set to hidden, the body widens by the width of the scrollbar. This produces an 268 | unpleasant flickering effect, especially on websites with centered content. If the `reserveScrollBarGap` option is set, 269 | this gap is filled by a `padding-right` on the body element. If `disableBodyScroll` is called for the last target element, 270 | or `clearAllBodyScrollLocks` is called, the `padding-right` is automatically reset to the previous value. 271 | 272 | ```js 273 | import { disableBodyScroll } from 'body-scroll-lock'; 274 | import type { BodyScrollOptions } from 'body-scroll-lock'; 275 | 276 | const options: BodyScrollOptions = { 277 | reserveScrollBarGap: true, 278 | }; 279 | 280 | disableBodyScroll(targetElement, options); 281 | ``` 282 | 283 | ### allowTouchMove 284 | 285 | **optional, default:** undefined 286 | 287 | To disable scrolling on iOS, `disableBodyScroll` prevents `touchmove` events. 288 | However, there are cases where you have called `disableBodyScroll` on an 289 | element, but its children still require `touchmove` events to function. 290 | 291 | See below for 2 use cases: 292 | 293 | ##### Simple 294 | 295 | ```javascript 296 | disableBodyScroll(container, { 297 | allowTouchMove: el => el.tagName === 'TEXTAREA', 298 | }); 299 | ``` 300 | 301 | ##### More Complex 302 | 303 | Javascript: 304 | 305 | ```javascript 306 | disableBodyScroll(container, { 307 | allowTouchMove: el => { 308 | while (el && el !== document.body) { 309 | if (el.getAttribute('body-scroll-lock-ignore') !== null) { 310 | return true; 311 | } 312 | 313 | el = el.parentElement; 314 | } 315 | }, 316 | }); 317 | ``` 318 | 319 | Html: 320 | 321 | ```html 322 |
323 |
324 | ... 325 |
326 |
327 | ``` 328 | 329 | ## References 330 | 331 | https://medium.com/jsdownunder/locking-body-scroll-for-all-devices-22def9615177 332 | https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi 333 | 334 | ## Changelog 335 | 336 | Refer to the [releases](https://github.com/willmcpo/body-scroll-lock/releases) page. 337 | -------------------------------------------------------------------------------- /examples/modal/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /examples/modal/README.md: -------------------------------------------------------------------------------- 1 | ### Instructions 2 | 3 | 1. Run `yarn compile` 4 | 2. Open `index.html` 5 | -------------------------------------------------------------------------------- /examples/modal/bundle.js: -------------------------------------------------------------------------------- 1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 1); 84 | 85 | 86 | var locks = []; 87 | var documentListenerAdded = false; 88 | var initialClientY = -1; 89 | var previousBodyOverflowSetting = void 0; 90 | var previousBodyPosition = void 0; 91 | var previousBodyPaddingRight = void 0; 92 | 93 | // returns true if `el` should be allowed to receive touchmove events. 94 | var allowTouchMove = function allowTouchMove(el) { 95 | return locks.some(function (lock) { 96 | if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { 97 | return true; 98 | } 99 | 100 | return false; 101 | }); 102 | }; 103 | 104 | var preventDefault = function preventDefault(rawEvent) { 105 | var e = rawEvent || window.event; 106 | 107 | // For the case whereby consumers adds a touchmove event listener to document. 108 | // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false }) 109 | // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then 110 | // the touchmove event on document will break. 111 | if (allowTouchMove(e.target)) { 112 | return true; 113 | } 114 | 115 | // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom). 116 | if (e.touches.length > 1) return true; 117 | 118 | if (e.preventDefault) e.preventDefault(); 119 | 120 | return false; 121 | }; 122 | 123 | var setOverflowHidden = function setOverflowHidden(options) { 124 | // If previousBodyPaddingRight is already set, don't set it again. 125 | if (previousBodyPaddingRight === undefined) { 126 | var _reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; 127 | var scrollBarGap = window.innerWidth - document.documentElement.clientWidth; 128 | 129 | if (_reserveScrollBarGap && scrollBarGap > 0) { 130 | var computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10); 131 | previousBodyPaddingRight = document.body.style.paddingRight; 132 | document.body.style.paddingRight = computedBodyPaddingRight + scrollBarGap + 'px'; 133 | } 134 | } 135 | 136 | // If previousBodyOverflowSetting is already set, don't set it again. 137 | if (previousBodyOverflowSetting === undefined) { 138 | previousBodyOverflowSetting = document.body.style.overflow; 139 | document.body.style.overflow = 'hidden'; 140 | } 141 | }; 142 | 143 | var restoreOverflowSetting = function restoreOverflowSetting() { 144 | if (previousBodyPaddingRight !== undefined) { 145 | document.body.style.paddingRight = previousBodyPaddingRight; 146 | 147 | // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it 148 | // can be set again. 149 | previousBodyPaddingRight = undefined; 150 | } 151 | 152 | if (previousBodyOverflowSetting !== undefined) { 153 | document.body.style.overflow = previousBodyOverflowSetting; 154 | 155 | // Restore previousBodyOverflowSetting to undefined 156 | // so setOverflowHidden knows it can be set again. 157 | previousBodyOverflowSetting = undefined; 158 | } 159 | }; 160 | 161 | var setPositionFixed = function setPositionFixed() { 162 | return window.requestAnimationFrame(function () { 163 | // If previousBodyPosition is already set, don't set it again. 164 | if (previousBodyPosition === undefined) { 165 | previousBodyPosition = { 166 | position: document.body.style.position, 167 | top: document.body.style.top, 168 | left: document.body.style.left 169 | }; 170 | 171 | // Update the dom inside an animation frame 172 | var _window = window, 173 | scrollY = _window.scrollY, 174 | scrollX = _window.scrollX, 175 | innerHeight = _window.innerHeight; 176 | 177 | document.body.style.position = 'fixed'; 178 | document.body.style.top = -scrollY; 179 | document.body.style.left = -scrollX; 180 | 181 | setTimeout(function () { 182 | return window.requestAnimationFrame(function () { 183 | // Attempt to check if the bottom bar appeared due to the position change 184 | var bottomBarHeight = innerHeight - window.innerHeight; 185 | // console.log({ 186 | // bottomBarHeight, 187 | // innerHeight, 188 | // innerHeightNew: window.innerHeight, 189 | // scrollY 190 | // }) 191 | if (bottomBarHeight && scrollY >= innerHeight) { 192 | // Move the content further up so that the bottom bar doesn't hide it 193 | document.body.style.top = -(scrollY + bottomBarHeight); 194 | } 195 | }); 196 | }, 300); 197 | } 198 | }); 199 | }; 200 | 201 | var restorePositionSetting = function restorePositionSetting() { 202 | if (previousBodyPosition !== undefined) { 203 | // Convert the position from "px" to Int 204 | var y = -parseInt(document.body.style.top, 10); 205 | var x = -parseInt(document.body.style.left, 10); 206 | 207 | // Restore styles 208 | document.body.style.position = previousBodyPosition.position; 209 | document.body.style.top = previousBodyPosition.top; 210 | document.body.style.left = previousBodyPosition.left; 211 | 212 | // Restore scroll 213 | window.scrollTo(x, y); 214 | 215 | previousBodyPosition = undefined; 216 | } 217 | }; 218 | 219 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions 220 | var isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled(targetElement) { 221 | return targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false; 222 | }; 223 | 224 | var handleScroll = function handleScroll(event, targetElement) { 225 | var clientY = event.targetTouches[0].clientY - initialClientY; 226 | 227 | if (allowTouchMove(event.target)) { 228 | return false; 229 | } 230 | 231 | if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { 232 | // element is at the top of its scroll. 233 | return preventDefault(event); 234 | } 235 | 236 | if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { 237 | // element is at the bottom of its scroll. 238 | return preventDefault(event); 239 | } 240 | 241 | event.stopPropagation(); 242 | return true; 243 | }; 244 | 245 | var disableBodyScroll = exports.disableBodyScroll = function disableBodyScroll(targetElement, options) { 246 | // targetElement must be provided 247 | if (!targetElement) { 248 | // eslint-disable-next-line no-console 249 | console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.'); 250 | return; 251 | } 252 | 253 | // disableBodyScroll must not have been called on this targetElement before 254 | if (locks.some(function (lock) { 255 | return lock.targetElement === targetElement; 256 | })) { 257 | return; 258 | } 259 | 260 | var lock = { 261 | targetElement: targetElement, 262 | options: options || {} 263 | }; 264 | 265 | locks = [].concat(_toConsumableArray(locks), [lock]); 266 | 267 | if (isIosDevice) { 268 | setPositionFixed(); 269 | } else { 270 | setOverflowHidden(options); 271 | } 272 | 273 | if (isIosDevice) { 274 | targetElement.ontouchstart = function (event) { 275 | if (event.targetTouches.length === 1) { 276 | // detect single touch. 277 | initialClientY = event.targetTouches[0].clientY; 278 | } 279 | }; 280 | targetElement.ontouchmove = function (event) { 281 | if (event.targetTouches.length === 1) { 282 | // detect single touch. 283 | handleScroll(event, targetElement); 284 | } 285 | }; 286 | 287 | if (!documentListenerAdded) { 288 | document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 289 | documentListenerAdded = true; 290 | } 291 | } 292 | }; 293 | 294 | var clearAllBodyScrollLocks = exports.clearAllBodyScrollLocks = function clearAllBodyScrollLocks() { 295 | if (isIosDevice) { 296 | // Clear all locks ontouchstart/ontouchmove handlers, and the references. 297 | locks.forEach(function (lock) { 298 | lock.targetElement.ontouchstart = null; 299 | lock.targetElement.ontouchmove = null; 300 | }); 301 | 302 | if (documentListenerAdded) { 303 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 304 | documentListenerAdded = false; 305 | } 306 | 307 | // Reset initial clientY. 308 | initialClientY = -1; 309 | } 310 | 311 | if (isIosDevice) { 312 | restorePositionSetting(); 313 | } else { 314 | restoreOverflowSetting(); 315 | } 316 | 317 | locks = []; 318 | }; 319 | 320 | var enableBodyScroll = exports.enableBodyScroll = function enableBodyScroll(targetElement) { 321 | if (!targetElement) { 322 | // eslint-disable-next-line no-console 323 | console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.'); 324 | return; 325 | } 326 | 327 | locks = locks.filter(function (lock) { 328 | return lock.targetElement !== targetElement; 329 | }); 330 | 331 | if (isIosDevice) { 332 | targetElement.ontouchstart = null; 333 | targetElement.ontouchmove = null; 334 | 335 | if (documentListenerAdded && locks.length === 0) { 336 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 337 | documentListenerAdded = false; 338 | } 339 | } 340 | 341 | if (isIosDevice) { 342 | restorePositionSetting(); 343 | } else { 344 | restoreOverflowSetting(); 345 | } 346 | }; 347 | }); 348 | 349 | 350 | },{}]},{},[1]); 351 | -------------------------------------------------------------------------------- /examples/modal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 88 | 89 | 90 |

Body Scroll Lock - Modal Demo

91 | 92 |
93 | 96 |
97 | 98 | 183 | 184 |
185 |

Main Body Content

186 | — Scroll Unlocked 187 |

188 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras blandit mattis dui quis mollis. Suspendisse 189 | posuere justo at fermentum commodo. Duis sed vulputate ex. Pellentesque tortor odio, pretium id tortor id, 190 | sagittis efficitur orci. Maecenas nulla odio, laoreet sed mi sit amet, auctor dictum felis. Fusce condimentum 191 | aliquam erat. Pellentesque quis ultricies ligula, nec congue tellus. Pellentesque habitant morbi tristique 192 | senectus et netus et malesuada fames ac turpis egestas. Pellentesque ac erat convallis, dapibus tellus sed, 193 | eleifend ipsum. Donec porta ligula id risus laoreet, at tempus neque consectetur. Aliquam malesuada pharetra 194 | nisi sed hendrerit. Integer molestie turpis a fringilla molestie. Nulla vehicula mattis mi. Morbi ut pretium 195 | libero. Curabitur rutrum aliquam nulla quis luctus. Curabitur porta felis vel ex maximus condimentum. Nunc 196 | tristique congue tempor. Fusce quis quam interdum, eleifend mi at, congue diam. Vestibulum ante ipsum primis in 197 | faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam vitae tellus tempor, pellentesque ante vitae, 198 | aliquam felis. Phasellus arcu arcu, vestibulum ut vulputate facilisis, tristique at sapien. Nunc pharetra 199 | vulputate nisl, nec vestibulum mi commodo vitae. Orci varius natoque penatibus et magnis dis parturient montes, 200 | nascetur ridiculus mus. Sed quam diam, mollis a odio quis, pretium pretium sapien. Integer ac leo sagittis, 201 | eleifend ex quis, semper sem. Etiam sed euismod ex, at mollis elit. Aliquam luctus, mauris id sollicitudin 202 | pharetra, mi eros aliquam nibh, sit amet bibendum ipsum mi nec sapien. Vestibulum sit amet enim eget velit 203 | hendrerit vestibulum. Vestibulum tempor imperdiet interdum. Aliquam tempus, ex feugiat finibus convallis, urna 204 | ex bibendum sapien, vel euismod turpis sapien ut tellus. Etiam a velit hendrerit tellus auctor ultrices ut 205 | facilisis elit. Proin varius feugiat tempus. Sed consectetur convallis velit, quis commodo enim lobortis in. 206 | Nunc eget ex quam. Duis rhoncus bibendum lacus, non rutrum ex molestie vel. In facilisis condimentum dapibus. 207 | Vestibulum ut turpis sed sem blandit tincidunt. Pellentesque vitae fermentum nisi, quis scelerisque orci. Ut 208 | iaculis ac neque vitae hendrerit. Aenean commodo erat et consequat dapibus. Integer convallis pellentesque 209 | fermentum. Integer sed congue eros, eget tincidunt odio. Pellentesque semper mattis elit, vitae condimentum nisi 210 | faucibus in. Sed pharetra leo eget justo scelerisque, ut mollis magna varius. Duis tincidunt eros id mollis 211 | scelerisque. Suspendisse eu vulputate massa, sit amet tincidunt sem. Donec id lacinia enim, vitae sollicitudin 212 | leo. Sed vitae mauris consequat, interdum massa eu, mollis dui. Ut suscipit semper semper. Praesent tincidunt 213 | malesuada metus nec tempus. Etiam ligula est, consequat vel quam ut, sollicitudin sollicitudin justo. Proin 214 | lorem sapien, hendrerit sollicitudin dictum vel, suscipit egestas augue. Sed auctor erat nisl, sed viverra elit 215 | hendrerit et. Quisque arcu dolor, pretium sit amet quam ac, iaculis dictum quam. Vestibulum porta, orci at 216 | ullamcorper imperdiet, ex augue blandit nunc, vitae bibendum massa turpis ut turpis. Morbi et nulla tellus. 217 | Proin placerat, diam eu lacinia vestibulum, eros risus varius dui, nec varius mi quam id dui. Quisque sit amet 218 | imperdiet dolor, quis feugiat quam. Nunc laoreet scelerisque diam ut tincidunt. Suspendisse potenti. Maecenas 219 | finibus diam est, sed placerat lectus elementum non. Phasellus tincidunt mauris lacus, sit amet vestibulum arcu 220 | ullamcorper tincidunt. Quisque dui orci, aliquam non est bibendum, rutrum sagittis lectus. Nullam tincidunt eu 221 | diam sed dapibus. Praesent interdum vestibulum nisi nec porta. Maecenas ut porttitor diam, a accumsan ligula. 222 | Fusce facilisis porta elit ac facilisis. Sed aliquam pretium tortor elementum pellentesque. Proin ut ligula leo. 223 | Nunc luctus augue vel purus tempus, et venenatis ligula rhoncus. Donec commodo scelerisque metus, eu rhoncus 224 | metus tristique at. Vivamus urna tellus, aliquet sit amet dolor a, pharetra finibus libero. Nulla non diam sit 225 | amet risus tempus convallis et sed ligula. Aenean cursus magna vel mauris euismod, et elementum magna placerat. 226 | Duis vehicula sagittis sem vestibulum posuere. Donec eu pellentesque ex, et aliquet arcu. Phasellus ac dolor ac 227 | sem rutrum pharetra. Suspendisse id justo in est ultrices bibendum. Morbi dictum lorem mattis ante imperdiet 228 | egestas. Mauris commodo diam sit amet posuere sodales. Curabitur fringilla placerat tellus a consectetur. Cras 229 | ut ipsum ut libero tempus suscipit sed fermentum quam. Proin quis erat turpis. Nam semper dictum augue id 230 | fermentum. Morbi vel nulla at nulla varius pulvinar. Nulla pretium viverra eros, vel ullamcorper enim aliquam 231 | in. Fusce sapien metus, luctus a purus ac, vulputate suscipit dolor. Mauris lectus justo, fringilla nec arcu sit 232 | amet, cursus sagittis metus. Vivamus semper enim a rhoncus faucibus. Suspendisse vel ornare ligula, quis 233 | ultricies tellus. Phasellus ultrices, magna tristique iaculis tincidunt, quam odio vestibulum ex, ut consequat 234 | lorem orci tincidunt dolor. Quisque rhoncus efficitur magna, et dictum sapien laoreet in. Quisque suscipit 235 | vehicula ipsum, congue luctus neque feugiat quis. Mauris porttitor molestie odio, nec aliquam elit fringilla ut. 236 | Maecenas sed felis eros. Aliquam sit amet velit sit amet sapien volutpat dignissim vel a nunc. Nunc quis urna 237 | feugiat nulla eleifend fringilla. Mauris laoreet dictum ex in vehicula. Ut sollicitudin diam lectus, ut mattis 238 | diam sagittis nec. Cras orci turpis, feugiat sit amet ornare sit amet, tristique ac massa. Aliquam tincidunt eu 239 | quam eu ullamcorper. Nunc finibus elit ac leo pretium, nec dictum nisl viverra. Nulla sit amet mollis sem, a 240 | ultrices urna. Curabitur sit amet tortor vehicula, interdum ante a, porta mauris. Curabitur accumsan, est at 241 | dapibus mattis, ipsum nunc venenatis urna, ac iaculis eros nulla at arcu. Duis sit amet magna non leo pretium 242 | consequat eget quis orci. Quisque in molestie diam, sed faucibus nisi. Sed tempus enim ut justo ultricies, nec 243 | facilisis ante iaculis. In bibendum faucibus nisl vitae lobortis. Fusce vitae sem auctor, pharetra nisl vel, 244 | volutpat turpis. Sed venenatis felis a tortor malesuada fringilla. Aenean sed arcu at erat convallis sodales. 245 | Aenean feugiat, lectus eu vehicula malesuada, nisi quam eleifend quam, sed faucibus urna eros sed ante. Nunc 246 | pretium, dui in laoreet gravida, nisl urna faucibus tortor, ut dignissim lorem tellus at lectus. Suspendisse 247 | lacinia tortor a sapien viverra sodales. Vestibulum id sapien feugiat massa mollis rhoncus varius eu odio. 248 | Pellentesque ut est euismod, faucibus felis et, tincidunt dolor. In hac habitasse platea dictumst. Morbi 249 | egestas, dolor quis sodales interdum, lacus odio malesuada justo, at luctus mauris mi vel eros. Ut ex odio, 250 | ultricies in sem placerat, ornare finibus tellus. Aliquam vel tincidunt sem. Aenean fringilla lacinia nisi, in 251 | scelerisque ante fermentum sed. Duis massa felis, tempus ut est ac, aliquam cursus magna. Vivamus malesuada 252 | finibus mi, in volutpat dui vehicula at. Sed arcu elit, fermentum dapibus sollicitudin eget, condimentum dapibus 253 | sem. Maecenas porta est a varius suscipit. Suspendisse potenti. Vestibulum at porta risus, egestas maximus 254 | tellus. Morbi at massa et lacus tempor eleifend. Praesent id volutpat arcu, in dictum justo. Cras lacinia 255 | sodales purus, faucibus ornare sem rutrum sed. Mauris tempus eros at quam efficitur dapibus. Donec in justo et 256 | lorem ultrices pellentesque. Ut consequat dolor velit, nec ultrices nunc fringilla in. Maecenas rhoncus velit 257 | vel dui porttitor varius. In id facilisis mauris, in dictum ante. Fusce hendrerit tristique dolor. Sed fermentum 258 | semper tempor. Ut facilisis volutpat magna id ornare. Praesent aliquam iaculis venenatis. Cras et erat ex. Donec 259 | pellentesque erat eget ante porttitor lacinia. Donec finibus odio at lacus sagittis finibus. Curabitur egestas 260 | massa orci, sit amet interdum risus dignissim vel. Cras sed quam a purus ornare lacinia ut eu sem. Praesent 261 | euismod luctus odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut tempus lorem erat, quis viverra 262 | ipsum rhoncus nec. Sed ac pharetra velit, non pellentesque sem. Etiam convallis posuere metus, in tempus ex 263 | hendrerit pellentesque. Curabitur est felis, molestie sit amet metus id, suscipit auctor erat. Fusce viverra mi 264 | nec luctus bibendum. Cras sodales orci vel urna tristique facilisis. Mauris pretium consequat ex nec finibus. 265 | Integer lectus dui, aliquet eget mi suscipit, blandit aliquam arcu. Mauris in aliquet eros, in gravida leo. 266 | Morbi id libero sed orci suscipit pharetra eget sit amet sapien. Maecenas non massa tincidunt, hendrerit mi 267 | vitae, semper nulla. Ut sapien nisi, porttitor sit amet consectetur at, molestie ut mi. In ac massa eros. In 268 | vehicula eleifend nibh, ut fringilla ante vulputate at. Sed gravida finibus iaculis. Integer consequat posuere 269 | elit in ultricies. Ut dignissim arcu lacinia sem euismod, vel ultrices eros bibendum. Integer lacinia aliquam 270 | ultrices. Phasellus nunc mi, cursus vitae euismod nec, luctus sit amet tellus. Pellentesque condimentum dui id 271 | justo tempus, quis posuere eros sollicitudin. Nam porttitor ornare sem, at maximus tellus bibendum vel. Nullam 272 | tempor urna orci. Sed vel sollicitudin magna. Curabitur lobortis sem at urna venenatis, id convallis elit 273 | congue. Quisque quam nulla, aliquet ac quam in, placerat gravida nisi. Curabitur tristique metus id efficitur 274 | malesuada. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis rutrum, lacus et dictum pharetra, velit 275 | leo consectetur metus, eu tincidunt orci elit vitae eros. Nulla eget tellus eget ipsum viverra iaculis nec ac 276 | ex. Nulla at leo pulvinar, maximus sapien quis, euismod lacus. Praesent quis risus molestie, sollicitudin massa 277 | sit amet, pulvinar neque. Cras volutpat, tortor nec posuere maximus, libero orci fringilla mauris, porttitor 278 | semper tellus massa ut eros. Donec dapibus non arcu sed congue. Maecenas fermentum, nulla molestie aliquet 279 | condimentum, ex mi egestas lorem, quis convallis ipsum libero a quam. Morbi in odio auctor nisl suscipit 280 | eleifend. Donec ultrices tempus consequat. Orci varius natoque penatibus et magnis dis parturient montes, 281 | nascetur ridiculus mus. Quisque dapibus, tortor vitae pretium ultrices, tellus sapien hendrerit risus, eget 282 | vestibulum sem lorem eu mauris. Fusce convallis orci ac lacus pharetra vulputate. Sed fermentum egestas enim, in 283 | finibus mauris mattis eu. Sed pretium, urna id mollis pretium, risus neque pellentesque risus, id gravida tellus 284 | erat quis eros. Praesent aliquam nibh id erat sodales placerat. Etiam nec nibh vehicula mi eleifend vestibulum. 285 | Nunc et ante ac magna auctor pellentesque id tempor lacus. Mauris nec erat vitae magna posuere vehicula a non 286 | ipsum. Integer pharetra et urna id sollicitudin. Phasellus in mi vel risus sagittis ultricies. Aliquam quis 287 | ligula lacinia, pretium diam sagittis, pulvinar purus. Nulla ullamcorper est lectus, eu cursus leo imperdiet 288 | vitae. Nulla fringilla in ante et molestie. Class aptent taciti sociosqu ad litora torquent per conubia nostra, 289 | per inceptos himenaeos. Curabitur id scelerisque velit, vehicula lobortis metus. Donec ut enim et dui euismod 290 | maximus ac vel libero. Integer nec felis a mi aliquet euismod. Fusce pretium felis nec efficitur interdum. 291 | Praesent mattis dui nulla, sed tempus libero semper eu. Cras gravida malesuada nunc ut facilisis. Donec vehicula 292 | neque nec tellus posuere dapibus. Etiam id est eros. Nam in imperdiet nunc, at consequat massa. Praesent eu 293 | tellus in turpis auctor hendrerit sit amet non mauris. Nullam congue neque et turpis mattis, eu sollicitudin 294 | dolor suscipit. Vestibulum eget tortor metus. Curabitur metus nisl, tincidunt sollicitudin placerat vitae, 295 | dignissim et nisi. Etiam scelerisque nibh non tellus dapibus, sit amet maximus libero lobortis. Proin porttitor 296 | enim lacus, et dictum sem tristique ac. Proin porttitor enim quis elementum sagittis. Morbi consectetur, massa 297 | id ornare accumsan, ex metus porttitor ante, nec porta nisl urna nec nisi. Maecenas tristique arcu condimentum 298 | risus molestie auctor. Nulla magna massa, interdum vel iaculis sed, venenatis eleifend tortor. Etiam luctus 299 | gravida fringilla. Vestibulum lectus urna, tristique non interdum vitae, consectetur in est. Nullam eu ex 300 | feugiat, auctor tortor ac, aliquet urna. Phasellus et tincidunt elit. Donec sit amet hendrerit lorem. Mauris 301 | cursus eros augue, ut tristique nulla luctus et. Sed laoreet tempor ipsum, sed mollis nisi consequat id. Nullam 302 | cursus sem elit, sed posuere nisl molestie quis. Mauris bibendum sit amet mauris quis consectetur. Pellentesque 303 | eros eros, cursus eu ante nec, consectetur porta magna. Sed justo neque, ultricies ac libero eget, euismod 304 | scelerisque nisl. Aenean posuere mauris velit. Integer laoreet sagittis felis, quis tempus quam lobortis eu. 305 | Aenean dapibus massa sit amet elementum ornare. Praesent dolor tortor, maximus quis magna et, suscipit 306 | pellentesque odio. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In 307 | sit amet fermentum elit. Curabitur luctus, neque id rhoncus semper, nibh mi varius arcu, at maximus diam orci 308 | interdum neque. Nunc diam tortor, cursus et libero et, varius imperdiet metus. Aenean tincidunt justo at augue 309 | posuere accumsan. Vivamus vitae nibh tincidunt, sagittis neque venenatis, fermentum enim. Vestibulum ante ipsum 310 | primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla hendrerit porta suscipit. Phasellus 311 | eleifend, mi sed semper posuere, augue felis feugiat lacus, a molestie eros ante non risus. Ut augue ante, 312 | ultrices vitae neque sit amet, euismod tristique leo. Donec arcu metus, laoreet sed semper eget, lacinia porta 313 | massa. Pellentesque condimentum justo ut metus aliquet, in sodales mauris blandit. Donec eget luctus magna. 314 | Fusce interdum orci id purus vehicula, pellentesque finibus massa accumsan. Nunc eleifend consequat nibh, 315 | placerat convallis urna accumsan eu. Nulla bibendum ipsum turpis, a sollicitudin turpis imperdiet in. Praesent 316 | vestibulum leo non nibh pulvinar faucibus. Donec dictum ligula eget efficitur pulvinar. Maecenas pulvinar ante 317 | vel urna rutrum tincidunt. Aliquam erat volutpat. In hac habitasse platea dictumst. Nulla eu magna quis nulla 318 | euismod viverra. Nulla semper diam eget convallis pulvinar. Nulla facilisi. Nulla condimentum gravida 319 | sollicitudin. Mauris scelerisque, ligula quis varius dignissim, libero tellus faucibus enim, id gravida nibh 320 | nisl vitae massa. Pellentesque auctor diam et dolor fringilla, sed tristique nunc pharetra. Cras quis gravida 321 | purus. Sed dignissim lobortis neque, vitae fringilla ex sagittis ut. Praesent dignissim placerat risus, in 322 | consectetur magna sollicitudin vitae. Proin blandit nisl vitae metus rhoncus pharetra. Phasellus elementum dolor 323 | at nunc dictum, vitae vulputate dui tempor. Praesent feugiat sit amet dolor eget scelerisque. Interdum et 324 | malesuada fames ac ante ipsum primis in faucibus. Nullam quis arcu nisl. In vehicula enim mauris, vitae viverra 325 | nisl rutrum molestie. Phasellus et erat eu felis tristique ultrices a eget metus. In pulvinar lectus nisl, sed 326 | volutpat neque viverra nec. Nam consequat eleifend leo, sagittis egestas est. Proin nulla dolor, eleifend sed 327 | sollicitudin et, dignissim id sapien. In lacinia tincidunt leo ut ultricies. Etiam ultrices euismod tortor. Sed 328 | rutrum aliquet maximus. Mauris at risus eu eros dapibus scelerisque eget vel eros. In non est sem. Duis at risus 329 | quam. Proin sed nibh sagittis, dapibus quam quis, posuere leo. Pellentesque ut eros ullamcorper, porttitor velit 330 | et, pharetra nunc. Morbi laoreet mollis diam quis vestibulum. Etiam at quam quis neque vulputate sodales. 331 | Praesent fringilla ligula sed vehicula scelerisque. Donec viverra eget libero faucibus dignissim. Vivamus nec 332 | nibh nisl. Mauris pretium finibus arcu. Nulla maximus, tortor sit amet tempor bibendum, dui nulla vehicula orci, 333 | et sodales massa arcu eu odio. Fusce semper ut ligula non feugiat. Aliquam condimentum ultrices nisl et 334 | malesuada. Maecenas sed neque metus. Vestibulum velit nunc, molestie placerat mattis non, consequat vitae purus. 335 | Duis id porttitor felis, ac elementum nunc. Nam ac aliquam nisl, et facilisis est. Phasellus lacinia mi a nulla 336 | hendrerit, a cursus nibh porttitor. Duis quis consequat nibh, ac interdum sem. Vestibulum ante ipsum primis in 337 | faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus aliquet nunc a ex mollis, laoreet venenatis 338 | quam accumsan. Suspendisse sit amet sem non diam gravida pharetra a vitae velit. Donec tincidunt nulla non nisl 339 | accumsan, nec placerat arcu vehicula. Donec eget ante sollicitudin, ultrices lectus quis, facilisis libero. 340 | Phasellus accumsan ornare eros, vitae molestie urna placerat non. Etiam tempus leo ac purus varius ultricies. 341 | Donec libero lorem, interdum et risus sit amet, accumsan pellentesque justo. Pellentesque in odio dapibus, 342 | tempor est sit amet, iaculis sem. Curabitur id volutpat arcu. Sed sit amet nunc eu urna tincidunt lobortis. 343 | Nullam tempor, diam at scelerisque efficitur, quam mauris dignissim eros, nec scelerisque massa magna ut enim. 344 | Maecenas pretium, purus in cursus sagittis, mi nibh vulputate arcu, id rutrum nisl ipsum non dui. Quisque at 345 | massa eget libero ornare interdum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc interdum 346 | velit at ipsum congue, in venenatis orci euismod. Maecenas nec nibh a orci congue aliquet a eget quam. Integer 347 | ex mauris, porta at rutrum eu, pulvinar ut massa. Pellentesque ut turpis in ipsum finibus condimentum. Phasellus 348 | porttitor varius varius. Nunc ut dui et est posuere fringilla. Etiam vel sem eu mauris laoreet malesuada ac at 349 | sem. Fusce pharetra libero ante, sed euismod metus blandit in. Vestibulum quis luctus ligula. Maecenas et odio 350 | elementum, vestibulum purus tempus, laoreet risus. Ut vel viverra tortor, non lobortis neque. Vivamus tincidunt 351 | nibh quis magna tincidunt, id fringilla lacus viverra. In posuere ac libero et molestie. Donec sed enim egestas, 352 | gravida quam quis, sagittis augue. Sed arcu justo, aliquam sit amet pulvinar et, bibendum eget metus. 353 | Suspendisse lectus nisi, sagittis pellentesque tristique sed, tristique vitae nisl. Praesent non malesuada 354 | ipsum. Vivamus vehicula urna vitae libero consequat mattis. Pellentesque in mattis magna, in interdum lacus. 355 | Donec lacus velit, maximus ac nibh non, eleifend ultrices libero. Pellentesque in convallis orci. Nullam et 356 | massa felis. Maecenas tellus velit, viverra ut ligula vitae, dignissim eleifend tellus. Nam faucibus sem est, 357 | sed molestie magna scelerisque in. Maecenas ultrices metus at libero convallis facilisis. Nullam lacinia ut 358 | tortor sit amet placerat. Vestibulum efficitur mattis nibh sit amet hendrerit. Vivamus eget orci diam. Curabitur 359 | mauris lectus, bibendum luctus iaculis non, commodo eget justo. Maecenas diam risus, viverra in nisi placerat, 360 | ultrices pellentesque arcu. Proin lectus ante, consequat vel suscipit at, tempor at lectus. Aliquam nec nulla 361 | vitae dolor porta blandit ut non sapien. Nulla pretium arcu eu enim tincidunt finibus. In hac habitasse platea 362 | dictumst. Quisque ut faucibus tellus. Sed in erat ligula. Nam sodales posuere varius. Curabitur dapibus felis 363 | tortor, eleifend suscipit risus feugiat ut. Sed eget sapien nisl. Curabitur ante neque, condimentum non lacus 364 | sit amet, commodo gravida lorem. Praesent aliquam sodales blandit. Praesent quis purus tristique, ultrices orci 365 | nec, gravida ex. Mauris nulla urna, mollis non volutpat at, elementum non mauris. Ut hendrerit auctor tempor. 366 | Pellentesque dictum sagittis velit, a fermentum tortor mattis id. Cras pulvinar est quis augue posuere finibus. 367 | Fusce ullamcorper risus ac tortor rutrum, vitae lacinia orci euismod. Orci varius natoque penatibus et magnis 368 | dis parturient montes, nascetur ridiculus mus. Quisque dapibus, ipsum vel pellentesque efficitur, enim purus 369 | viverra lacus, ut auctor dui sapien nec elit. Donec egestas iaculis sodales. Vivamus id augue sed enim lobortis 370 | molestie in vel erat. Ut vitae mi ut neque venenatis consectetur. Phasellus varius mollis arcu at dapibus. 371 | Integer congue magna ipsum, vitae malesuada nibh tempus non. Nullam eu molestie elit, eget tincidunt lacus. 372 | Fusce eu imperdiet magna. Curabitur scelerisque elit at aliquam ullamcorper. Aenean tempus sem sit amet libero 373 | pellentesque, non tristique sem vestibulum. Fusce vestibulum eros ut neque posuere, a posuere tellus volutpat. 374 | Nunc ultricies eleifend nulla tempus mattis. Donec convallis suscipit mi, quis ornare ex fringilla quis. Nullam 375 | nec dui ut arcu interdum semper. Suspendisse sit amet augue eget leo rutrum varius. Integer euismod eget quam 376 | non vestibulum. Integer ornare ipsum ac erat placerat euismod. Sed eu arcu sed arcu pharetra ultrices. Sed 377 | mattis tincidunt lacinia. Vestibulum ut sodales ex. Pellentesque maximus ipsum magna, rhoncus pellentesque metus 378 | mattis nec. Pellentesque tincidunt quis lorem nec euismod. Ut in orci interdum, dignissim eros faucibus, gravida 379 | urna. Vestibulum rhoncus massa nec accumsan convallis. Nulla velit nisl, molestie quis libero in, semper congue 380 | magna. Aliquam sit amet eleifend nisi. Duis efficitur feugiat sollicitudin. Sed scelerisque augue vestibulum, 381 | condimentum sem vitae, ultricies odio. Integer porttitor vel ante in laoreet. Curabitur tempus lacus quis metus 382 | egestas, tempus pulvinar mi facilisis. Integer porttitor mattis ipsum, in accumsan felis. Morbi vestibulum urna 383 | lectus, sed gravida mauris laoreet in. Suspendisse maximus sodales pharetra. Etiam molestie auctor augue, et 384 | lacinia massa malesuada et. Etiam lobortis tellus et nisl posuere dictum. Lorem ipsum dolor sit amet, 385 | consectetur adipiscing elit. Phasellus luctus at turpis in semper. Aenean id volutpat lorem. Class aptent taciti 386 | sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed vel egestas massa, eu cursus nulla. 387 | Nulla tellus nisl, tempus non semper non, molestie efficitur elit. Sed in pellentesque sapien, sit amet 388 | elementum odio. Aenean et velit nisl. Ut non neque metus. Nullam ornare elit vel mi maximus molestie. Quisque 389 | consequat efficitur leo, at ultrices libero consequat et. Maecenas a nunc at ligula interdum hendrerit in quis 390 | ligula. Duis condimentum nisi mi, id fermentum augue maximus non. Donec auctor porttitor ex id rutrum. Etiam 391 | efficitur augue in justo convallis, sed pellentesque risus tincidunt. Donec tincidunt lorem nec tellus porta, 392 | non efficitur augue auctor. Phasellus dignissim diam sapien, ut efficitur tortor mollis at. Donec faucibus, arcu 393 | eu tristique fringilla, ante purus lacinia diam, condimentum tempus lectus massa vitae nisl. Quisque vel sapien 394 | ac mi fringilla posuere. Nullam ac consectetur nibh. Aenean tristique metus id dui elementum mollis. 395 | Pellentesque a vestibulum risus, tempus suscipit mauris. Nullam vehicula vehicula malesuada. Curabitur sed 396 | molestie ipsum, non auctor nibh. Aenean ut enim sit amet velit feugiat consequat. In hac habitasse platea 397 | dictumst. Praesent eget maximus enim, nec malesuada felis. Maecenas at sapien nec ipsum dictum suscipit nec at 398 | felis. Vivamus aliquam velit at pretium porttitor. Cras lacus purus, commodo sed nulla quis, ultricies sodales 399 | turpis. Nullam faucibus ipsum in accumsan mollis. Maecenas ac laoreet odio. Praesent consectetur nisl non 400 | elementum cursus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. 401 | Vestibulum eu malesuada ipsum. Duis ligula justo, fringilla ut condimentum tempor, tincidunt rhoncus diam. Nunc 402 | sit amet libero porttitor, porta nibh sit amet, gravida nunc. Vestibulum cursus tincidunt odio. Quisque non 403 | massa et ipsum iaculis iaculis eu in est. Suspendisse potenti. Mauris quis consequat nulla. Vivamus dictum, nibh 404 | ac consequat volutpat, mi sapien maximus urna, vel congue metus lectus pretium arcu. Suspendisse eleifend ante 405 | sit amet tellus rhoncus aliquam. Aenean augue erat, bibendum a blandit eu, tincidunt ac purus. Curabitur luctus 406 | rutrum est sed dignissim. Morbi a gravida nunc. Vestibulum eleifend nulla felis, id bibendum lectus dapibus in. 407 | Duis lacinia hendrerit mi, ac posuere urna tincidunt sit amet. Pellentesque vitae pulvinar lacus, vitae ultrices 408 | ligula. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque 409 | habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque habitant morbi 410 | tristique senectus et netus et malesuada fames ac turpis egestas. In consectetur, lectus id elementum tristique, 411 | lacus ipsum pharetra mauris, in dapibus sapien est sed velit. Etiam nec est id odio consectetur venenatis. Donec 412 | porttitor varius lacus, quis lacinia odio malesuada sit amet. Cras felis arcu, dictum eu ante hendrerit, 413 | consequat volutpat metus. Nulla facilisi. Aenean orci ligula, gravida nec maximus eget, faucibus sed nisl. 414 | Suspendisse potenti. Pellentesque viverra eget neque sed mollis. Morbi eget sapien sed est pretium sollicitudin. 415 | Quisque faucibus ex vitae scelerisque pharetra. Curabitur vitae dolor nulla. Cras non arcu quis justo tempor 416 | condimentum non at tellus. Nam sed pharetra metus, vitae pretium nisl. Maecenas commodo, mauris eu lacinia 417 | luctus, lacus justo vestibulum lectus, quis venenatis quam nulla sed dui. Donec efficitur odio ac dolor rhoncus 418 | tempor. Suspendisse vitae metus vitae odio feugiat vestibulum nec a augue. Sed a ligula interdum, aliquam leo 419 | non, bibendum ligula. Nulla eget turpis non lacus ultricies varius nec sit amet ligula. Nam sed ultrices nibh. 420 | Integer efficitur euismod ligula ut interdum. Phasellus nec consequat urna. Curabitur vitae aliquam tellus, vel 421 | sagittis nulla. Integer cursus purus ipsum, quis pellentesque turpis ultrices nec. Suspendisse mollis urna 422 | tristique vehicula ornare. Etiam in ex et dui lacinia sagittis. Donec sit amet laoreet arcu, tincidunt 423 | sollicitudin lacus. Suspendisse est tellus, euismod eget magna in, tempor consectetur eros. Cras rhoncus nisl ac 424 | pharetra cursus. Sed elementum odio et tellus tristique hendrerit. Phasellus consequat condimentum enim ac 425 | semper. Aenean congue ultricies ligula. Aenean at suscipit ligula. Proin non luctus mauris, fermentum vulputate 426 | dui. Praesent eu tincidunt odio, non feugiat risus. In hac habitasse platea dictumst. Vivamus varius ornare 427 | eros, vitae pretium enim eleifend at. Ut vestibulum nisi nec malesuada facilisis. Duis quis leo mattis mauris 428 | cursus bibendum sed quis mi. In sit amet sollicitudin velit, vel lacinia velit. Morbi posuere id massa at 429 | condimentum. Phasellus tristique quam quis pellentesque aliquet. Etiam quis suscipit nulla. Pellentesque tempor 430 | consequat massa lacinia scelerisque. Ut ut est nibh. Mauris mollis rhoncus aliquet. Quisque eget libero et augue 431 | volutpat tincidunt sit amet a mauris. Sed pellentesque cursus leo vitae mollis. Sed ut condimentum mi, id 432 | tincidunt nibh. Donec pharetra metus nibh, at tincidunt tortor mollis elementum. Pellentesque habitant morbi 433 | tristique senectus et netus et malesuada fames ac turpis egestas. Nullam ac tortor consequat, aliquet ligula 434 | nec, gravida massa. Etiam in lectus placerat, dapibus ex nec, lacinia risus. Sed interdum orci vitae augue 435 | malesuada, ut ullamcorper leo fermentum. Sed convallis tincidunt nisi nec dictum. In hac habitasse platea 436 | dictumst. Aliquam erat volutpat. Vivamus tincidunt turpis arcu, ac laoreet magna tempus nec. Fusce nisl purus, 437 | blandit in diam ac, dictum laoreet leo. Nunc dictum quam non est rutrum, quis pulvinar eros lacinia. Donec 438 | cursus libero non erat tristique, et ullamcorper ante pretium. Orci varius natoque penatibus et magnis dis 439 | parturient montes, nascetur ridiculus mus. Donec turpis elit, ultrices non feugiat ut, feugiat eget ante. Nullam 440 | accumsan, quam et consectetur porttitor, urna purus eleifend erat, at hendrerit lacus sapien nec sem. Vestibulum 441 | sodales vestibulum felis a auctor. Aenean venenatis quam orci, id porta ipsum vulputate non. Mauris mattis 442 | tempus finibus. Nullam faucibus bibendum bibendum. Sed urna libero, mattis nec metus quis, aliquam ultrices 443 | elit. Maecenas semper convallis erat vitae rutrum. Proin malesuada enim a magna efficitur, ac sagittis quam 444 | consectetur. Suspendisse pulvinar id nulla eget aliquam. Curabitur metus ante, euismod eget risus et, elementum 445 | porta massa. Morbi aliquet malesuada augue, eget sagittis risus iaculis a. Aliquam nec leo purus. Pellentesque 446 | vel diam mi. Donec ac luctus velit. Integer tristique dui id dui luctus, vel tincidunt erat volutpat. Donec 447 | ultricies velit id tellus dapibus, quis consequat ante scelerisque. Cras finibus molestie ipsum non vehicula. 448 | Vivamus interdum aliquet metus quis dignissim. Etiam libero tortor, molestie quis consequat quis, aliquet in 449 | tellus. Etiam ut porttitor metus. Praesent eget est ullamcorper, eleifend augue in, dapibus ligula. Donec eu 450 | pretium neque. Sed ultrices tortor ut orci tempor, non tristique nunc tempus. Maecenas quis leo eu sem lobortis 451 | malesuada rutrum et odio. Etiam hendrerit placerat bibendum. Mauris posuere odio nisi, nec suscipit tortor 452 | finibus non. Sed porttitor tortor quis ante lacinia euismod. Mauris auctor pretium odio ac cursus. Curabitur 453 | varius mi ut mi porta, ut convallis nibh consequat. Etiam auctor rutrum nisi, sit amet rutrum dui efficitur at. 454 | Cras scelerisque accumsan rhoncus. Maecenas ut sapien non metus ultricies aliquam. Mauris at leo turpis. Morbi 455 | vel tellus eros. Interdum et malesuada fames ac ante ipsum primis in faucibus. Etiam vestibulum commodo 456 | lobortis. Mauris vestibulum luctus consequat. Integer ut convallis enim, varius iaculis est. Nunc vulputate 457 | risus quis purus tempor, at tristique erat laoreet. Maecenas sodales nisl et urna fringilla, vitae blandit 458 | tortor elementum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. 459 | Cras lorem ex, ultricies ac arcu quis, pretium convallis lorem. Praesent ut ligula aliquet, maximus justo quis, 460 | accumsan diam. Vivamus eleifend vitae dui ut egestas. Integer non cursus est. Maecenas viverra quis nisl vel 461 | euismod. Donec et quam eget nisi congue pulvinar. Phasellus bibendum scelerisque magna. Quisque eu tellus 462 | vulputate, pulvinar velit nec, euismod massa. Nullam ullamcorper quam ac luctus tincidunt. Quisque massa neque, 463 | tristique sed lorem vitae, sagittis lacinia tellus. Aliquam aliquam nisl nec placerat fringilla. Phasellus 464 | auctor velit nec elit consequat ultrices. Praesent eu sapien lobortis ipsum vehicula dictum ut eu nisi. Integer 465 | ut dictum felis. Nunc dictum sapien felis, nec gravida lorem porttitor id. Suspendisse gravida congue lacus, nec 466 | rutrum enim mattis nec. Vivamus nec tellus turpis. Nulla semper non odio ut consectetur. Fusce mattis massa 467 | enim, at congue sapien eleifend quis. Suspendisse varius urna in elit interdum, vitae viverra dui convallis. 468 | Curabitur quis iaculis nisi. Praesent varius cursus enim eu ullamcorper. Nullam ac tincidunt enim. Donec ut 469 | vehicula velit. 470 |

471 |
472 | 473 | 474 | 475 | 476 | -------------------------------------------------------------------------------- /examples/modal/main.js: -------------------------------------------------------------------------------- 1 | const bodyScrollLock = require('../../lib/bodyScrollLock.js'); 2 | 3 | const disableBodyScrollButton = document.querySelector('.disableBodyScroll'); 4 | const enableBodyScrollButton = document.querySelector('.enableBodyScroll'); 5 | const statusElement = document.querySelector('.bodyScrollLockStatus'); 6 | const modalElement = document.querySelector('.modal'); 7 | const scrollTargetElement = document.querySelector('.scrollTarget'); 8 | 9 | disableBodyScrollButton.onclick = function() { 10 | console.info('disableBodyScrollButton'); 11 | 12 | // show modal 13 | modalElement.classList.add('active'); 14 | 15 | bodyScrollLock.disableBodyScroll(scrollTargetElement); 16 | 17 | statusElement.innerHTML = ' — Scroll Locked'; 18 | statusElement.style.color = 'red'; 19 | }; 20 | 21 | enableBodyScrollButton.onclick = function() { 22 | console.info('enableBodyScrollButton'); 23 | 24 | // hide modal 25 | modalElement.classList.remove('active'); 26 | 27 | bodyScrollLock.enableBodyScroll(scrollTargetElement); 28 | 29 | statusElement.innerHTML = ' — Scroll Unlocked'; 30 | statusElement.style.color = ''; 31 | }; 32 | -------------------------------------------------------------------------------- /examples/modal/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "alias": ["bodyscrolllock-modal"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/modal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "body-scroll-lock-example-modal", 3 | "description": "Example for body scroll lock, with a Modal", 4 | "author": "diachedelic", 5 | "license": "MIT", 6 | "dependencies": {}, 7 | "scripts": { 8 | "compile": "browserify ./main.js > bundle.js", 9 | "deployNow": "now --prod" 10 | }, 11 | "devDependencies": { 12 | "browserify": "^16.5.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/modal/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | JSONStream@^1.0.3: 6 | version "1.3.5" 7 | resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" 8 | integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== 9 | dependencies: 10 | jsonparse "^1.2.0" 11 | through ">=2.2.7 <3" 12 | 13 | acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.6.1: 14 | version "1.8.2" 15 | resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" 16 | integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== 17 | dependencies: 18 | acorn "^7.0.0" 19 | acorn-walk "^7.0.0" 20 | xtend "^4.0.2" 21 | 22 | acorn-walk@^7.0.0: 23 | version "7.1.1" 24 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" 25 | integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== 26 | 27 | acorn@^7.0.0: 28 | version "7.1.1" 29 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" 30 | integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== 31 | 32 | asn1.js@^4.0.0: 33 | version "4.10.1" 34 | resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" 35 | integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== 36 | dependencies: 37 | bn.js "^4.0.0" 38 | inherits "^2.0.1" 39 | minimalistic-assert "^1.0.0" 40 | 41 | assert@^1.4.0: 42 | version "1.5.0" 43 | resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" 44 | integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== 45 | dependencies: 46 | object-assign "^4.1.1" 47 | util "0.10.3" 48 | 49 | balanced-match@^1.0.0: 50 | version "1.0.0" 51 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 52 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 53 | 54 | base64-js@^1.0.2: 55 | version "1.3.1" 56 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" 57 | integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== 58 | 59 | bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: 60 | version "4.11.8" 61 | resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" 62 | integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== 63 | 64 | brace-expansion@^1.1.7: 65 | version "1.1.11" 66 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 67 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 68 | dependencies: 69 | balanced-match "^1.0.0" 70 | concat-map "0.0.1" 71 | 72 | brorand@^1.0.1: 73 | version "1.1.0" 74 | resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" 75 | integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= 76 | 77 | browser-pack@^6.0.1: 78 | version "6.1.0" 79 | resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774" 80 | integrity sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA== 81 | dependencies: 82 | JSONStream "^1.0.3" 83 | combine-source-map "~0.8.0" 84 | defined "^1.0.0" 85 | safe-buffer "^5.1.1" 86 | through2 "^2.0.0" 87 | umd "^3.0.0" 88 | 89 | browser-resolve@^1.11.0, browser-resolve@^1.7.0: 90 | version "1.11.3" 91 | resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" 92 | integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== 93 | dependencies: 94 | resolve "1.1.7" 95 | 96 | browserify-aes@^1.0.0, browserify-aes@^1.0.4: 97 | version "1.2.0" 98 | resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" 99 | integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== 100 | dependencies: 101 | buffer-xor "^1.0.3" 102 | cipher-base "^1.0.0" 103 | create-hash "^1.1.0" 104 | evp_bytestokey "^1.0.3" 105 | inherits "^2.0.1" 106 | safe-buffer "^5.0.1" 107 | 108 | browserify-cipher@^1.0.0: 109 | version "1.0.1" 110 | resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" 111 | integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== 112 | dependencies: 113 | browserify-aes "^1.0.4" 114 | browserify-des "^1.0.0" 115 | evp_bytestokey "^1.0.0" 116 | 117 | browserify-des@^1.0.0: 118 | version "1.0.2" 119 | resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" 120 | integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== 121 | dependencies: 122 | cipher-base "^1.0.1" 123 | des.js "^1.0.0" 124 | inherits "^2.0.1" 125 | safe-buffer "^5.1.2" 126 | 127 | browserify-rsa@^4.0.0: 128 | version "4.0.1" 129 | resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" 130 | integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= 131 | dependencies: 132 | bn.js "^4.1.0" 133 | randombytes "^2.0.1" 134 | 135 | browserify-sign@^4.0.0: 136 | version "4.0.4" 137 | resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" 138 | integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= 139 | dependencies: 140 | bn.js "^4.1.1" 141 | browserify-rsa "^4.0.0" 142 | create-hash "^1.1.0" 143 | create-hmac "^1.1.2" 144 | elliptic "^6.0.0" 145 | inherits "^2.0.1" 146 | parse-asn1 "^5.0.0" 147 | 148 | browserify-zlib@~0.2.0: 149 | version "0.2.0" 150 | resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" 151 | integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== 152 | dependencies: 153 | pako "~1.0.5" 154 | 155 | browserify@^16.5.0: 156 | version "16.5.0" 157 | resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.5.0.tgz#a1c2bc0431bec11fd29151941582e3f645ede881" 158 | integrity sha512-6bfI3cl76YLAnCZ75AGu/XPOsqUhRyc0F/olGIJeCxtfxF2HvPKEcmjU9M8oAPxl4uBY1U7Nry33Q6koV3f2iw== 159 | dependencies: 160 | JSONStream "^1.0.3" 161 | assert "^1.4.0" 162 | browser-pack "^6.0.1" 163 | browser-resolve "^1.11.0" 164 | browserify-zlib "~0.2.0" 165 | buffer "^5.0.2" 166 | cached-path-relative "^1.0.0" 167 | concat-stream "^1.6.0" 168 | console-browserify "^1.1.0" 169 | constants-browserify "~1.0.0" 170 | crypto-browserify "^3.0.0" 171 | defined "^1.0.0" 172 | deps-sort "^2.0.0" 173 | domain-browser "^1.2.0" 174 | duplexer2 "~0.1.2" 175 | events "^2.0.0" 176 | glob "^7.1.0" 177 | has "^1.0.0" 178 | htmlescape "^1.1.0" 179 | https-browserify "^1.0.0" 180 | inherits "~2.0.1" 181 | insert-module-globals "^7.0.0" 182 | labeled-stream-splicer "^2.0.0" 183 | mkdirp "^0.5.0" 184 | module-deps "^6.0.0" 185 | os-browserify "~0.3.0" 186 | parents "^1.0.1" 187 | path-browserify "~0.0.0" 188 | process "~0.11.0" 189 | punycode "^1.3.2" 190 | querystring-es3 "~0.2.0" 191 | read-only-stream "^2.0.0" 192 | readable-stream "^2.0.2" 193 | resolve "^1.1.4" 194 | shasum "^1.0.0" 195 | shell-quote "^1.6.1" 196 | stream-browserify "^2.0.0" 197 | stream-http "^3.0.0" 198 | string_decoder "^1.1.1" 199 | subarg "^1.0.0" 200 | syntax-error "^1.1.1" 201 | through2 "^2.0.0" 202 | timers-browserify "^1.0.1" 203 | tty-browserify "0.0.1" 204 | url "~0.11.0" 205 | util "~0.10.1" 206 | vm-browserify "^1.0.0" 207 | xtend "^4.0.0" 208 | 209 | buffer-from@^1.0.0: 210 | version "1.1.1" 211 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 212 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 213 | 214 | buffer-xor@^1.0.3: 215 | version "1.0.3" 216 | resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" 217 | integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= 218 | 219 | buffer@^5.0.2: 220 | version "5.5.0" 221 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce" 222 | integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww== 223 | dependencies: 224 | base64-js "^1.0.2" 225 | ieee754 "^1.1.4" 226 | 227 | builtin-status-codes@^3.0.0: 228 | version "3.0.0" 229 | resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" 230 | integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= 231 | 232 | cached-path-relative@^1.0.0, cached-path-relative@^1.0.2: 233 | version "1.0.2" 234 | resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db" 235 | integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg== 236 | 237 | cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: 238 | version "1.0.4" 239 | resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" 240 | integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== 241 | dependencies: 242 | inherits "^2.0.1" 243 | safe-buffer "^5.0.1" 244 | 245 | combine-source-map@^0.8.0, combine-source-map@~0.8.0: 246 | version "0.8.0" 247 | resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" 248 | integrity sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos= 249 | dependencies: 250 | convert-source-map "~1.1.0" 251 | inline-source-map "~0.6.0" 252 | lodash.memoize "~3.0.3" 253 | source-map "~0.5.3" 254 | 255 | concat-map@0.0.1: 256 | version "0.0.1" 257 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 258 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 259 | 260 | concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@~1.6.0: 261 | version "1.6.2" 262 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 263 | integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== 264 | dependencies: 265 | buffer-from "^1.0.0" 266 | inherits "^2.0.3" 267 | readable-stream "^2.2.2" 268 | typedarray "^0.0.6" 269 | 270 | console-browserify@^1.1.0: 271 | version "1.2.0" 272 | resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" 273 | integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== 274 | 275 | constants-browserify@~1.0.0: 276 | version "1.0.0" 277 | resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" 278 | integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= 279 | 280 | convert-source-map@~1.1.0: 281 | version "1.1.3" 282 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" 283 | integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA= 284 | 285 | core-util-is@~1.0.0: 286 | version "1.0.2" 287 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 288 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 289 | 290 | create-ecdh@^4.0.0: 291 | version "4.0.3" 292 | resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" 293 | integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== 294 | dependencies: 295 | bn.js "^4.1.0" 296 | elliptic "^6.0.0" 297 | 298 | create-hash@^1.1.0, create-hash@^1.1.2: 299 | version "1.2.0" 300 | resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" 301 | integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== 302 | dependencies: 303 | cipher-base "^1.0.1" 304 | inherits "^2.0.1" 305 | md5.js "^1.3.4" 306 | ripemd160 "^2.0.1" 307 | sha.js "^2.4.0" 308 | 309 | create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: 310 | version "1.1.7" 311 | resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" 312 | integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== 313 | dependencies: 314 | cipher-base "^1.0.3" 315 | create-hash "^1.1.0" 316 | inherits "^2.0.1" 317 | ripemd160 "^2.0.0" 318 | safe-buffer "^5.0.1" 319 | sha.js "^2.4.8" 320 | 321 | crypto-browserify@^3.0.0: 322 | version "3.12.0" 323 | resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" 324 | integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== 325 | dependencies: 326 | browserify-cipher "^1.0.0" 327 | browserify-sign "^4.0.0" 328 | create-ecdh "^4.0.0" 329 | create-hash "^1.1.0" 330 | create-hmac "^1.1.0" 331 | diffie-hellman "^5.0.0" 332 | inherits "^2.0.1" 333 | pbkdf2 "^3.0.3" 334 | public-encrypt "^4.0.0" 335 | randombytes "^2.0.0" 336 | randomfill "^1.0.3" 337 | 338 | dash-ast@^1.0.0: 339 | version "1.0.0" 340 | resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" 341 | integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA== 342 | 343 | defined@^1.0.0: 344 | version "1.0.0" 345 | resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" 346 | integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= 347 | 348 | deps-sort@^2.0.0: 349 | version "2.0.1" 350 | resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.1.tgz#9dfdc876d2bcec3386b6829ac52162cda9fa208d" 351 | integrity sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw== 352 | dependencies: 353 | JSONStream "^1.0.3" 354 | shasum-object "^1.0.0" 355 | subarg "^1.0.0" 356 | through2 "^2.0.0" 357 | 358 | des.js@^1.0.0: 359 | version "1.0.1" 360 | resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" 361 | integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== 362 | dependencies: 363 | inherits "^2.0.1" 364 | minimalistic-assert "^1.0.0" 365 | 366 | detective@^5.2.0: 367 | version "5.2.0" 368 | resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" 369 | integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== 370 | dependencies: 371 | acorn-node "^1.6.1" 372 | defined "^1.0.0" 373 | minimist "^1.1.1" 374 | 375 | diffie-hellman@^5.0.0: 376 | version "5.0.3" 377 | resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" 378 | integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== 379 | dependencies: 380 | bn.js "^4.1.0" 381 | miller-rabin "^4.0.0" 382 | randombytes "^2.0.0" 383 | 384 | domain-browser@^1.2.0: 385 | version "1.2.0" 386 | resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" 387 | integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== 388 | 389 | duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: 390 | version "0.1.4" 391 | resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" 392 | integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= 393 | dependencies: 394 | readable-stream "^2.0.2" 395 | 396 | elliptic@^6.0.0: 397 | version "6.5.2" 398 | resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" 399 | integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== 400 | dependencies: 401 | bn.js "^4.4.0" 402 | brorand "^1.0.1" 403 | hash.js "^1.0.0" 404 | hmac-drbg "^1.0.0" 405 | inherits "^2.0.1" 406 | minimalistic-assert "^1.0.0" 407 | minimalistic-crypto-utils "^1.0.0" 408 | 409 | events@^2.0.0: 410 | version "2.1.0" 411 | resolved "https://registry.yarnpkg.com/events/-/events-2.1.0.tgz#2a9a1e18e6106e0e812aa9ebd4a819b3c29c0ba5" 412 | integrity sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg== 413 | 414 | evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: 415 | version "1.0.3" 416 | resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" 417 | integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== 418 | dependencies: 419 | md5.js "^1.3.4" 420 | safe-buffer "^5.1.1" 421 | 422 | fast-safe-stringify@^2.0.7: 423 | version "2.0.7" 424 | resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" 425 | integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== 426 | 427 | fs.realpath@^1.0.0: 428 | version "1.0.0" 429 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 430 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 431 | 432 | function-bind@^1.1.1: 433 | version "1.1.1" 434 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 435 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 436 | 437 | get-assigned-identifiers@^1.2.0: 438 | version "1.2.0" 439 | resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" 440 | integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== 441 | 442 | glob@^7.1.0: 443 | version "7.1.6" 444 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 445 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 446 | dependencies: 447 | fs.realpath "^1.0.0" 448 | inflight "^1.0.4" 449 | inherits "2" 450 | minimatch "^3.0.4" 451 | once "^1.3.0" 452 | path-is-absolute "^1.0.0" 453 | 454 | has@^1.0.0: 455 | version "1.0.3" 456 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 457 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 458 | dependencies: 459 | function-bind "^1.1.1" 460 | 461 | hash-base@^3.0.0: 462 | version "3.0.4" 463 | resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" 464 | integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= 465 | dependencies: 466 | inherits "^2.0.1" 467 | safe-buffer "^5.0.1" 468 | 469 | hash.js@^1.0.0, hash.js@^1.0.3: 470 | version "1.1.7" 471 | resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" 472 | integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== 473 | dependencies: 474 | inherits "^2.0.3" 475 | minimalistic-assert "^1.0.1" 476 | 477 | hmac-drbg@^1.0.0: 478 | version "1.0.1" 479 | resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" 480 | integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= 481 | dependencies: 482 | hash.js "^1.0.3" 483 | minimalistic-assert "^1.0.0" 484 | minimalistic-crypto-utils "^1.0.1" 485 | 486 | htmlescape@^1.1.0: 487 | version "1.1.1" 488 | resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" 489 | integrity sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E= 490 | 491 | https-browserify@^1.0.0: 492 | version "1.0.0" 493 | resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" 494 | integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= 495 | 496 | ieee754@^1.1.4: 497 | version "1.1.13" 498 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" 499 | integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== 500 | 501 | inflight@^1.0.4: 502 | version "1.0.6" 503 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 504 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 505 | dependencies: 506 | once "^1.3.0" 507 | wrappy "1" 508 | 509 | inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: 510 | version "2.0.4" 511 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 512 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 513 | 514 | inherits@2.0.1: 515 | version "2.0.1" 516 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" 517 | integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= 518 | 519 | inherits@2.0.3: 520 | version "2.0.3" 521 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 522 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 523 | 524 | inline-source-map@~0.6.0: 525 | version "0.6.2" 526 | resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" 527 | integrity sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU= 528 | dependencies: 529 | source-map "~0.5.3" 530 | 531 | insert-module-globals@^7.0.0: 532 | version "7.2.0" 533 | resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.0.tgz#ec87e5b42728479e327bd5c5c71611ddfb4752ba" 534 | integrity sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw== 535 | dependencies: 536 | JSONStream "^1.0.3" 537 | acorn-node "^1.5.2" 538 | combine-source-map "^0.8.0" 539 | concat-stream "^1.6.1" 540 | is-buffer "^1.1.0" 541 | path-is-absolute "^1.0.1" 542 | process "~0.11.0" 543 | through2 "^2.0.0" 544 | undeclared-identifiers "^1.1.2" 545 | xtend "^4.0.0" 546 | 547 | is-buffer@^1.1.0: 548 | version "1.1.6" 549 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 550 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 551 | 552 | isarray@~1.0.0: 553 | version "1.0.0" 554 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 555 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 556 | 557 | json-stable-stringify@~0.0.0: 558 | version "0.0.1" 559 | resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" 560 | integrity sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U= 561 | dependencies: 562 | jsonify "~0.0.0" 563 | 564 | jsonify@~0.0.0: 565 | version "0.0.0" 566 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 567 | integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= 568 | 569 | jsonparse@^1.2.0: 570 | version "1.3.1" 571 | resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" 572 | integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= 573 | 574 | labeled-stream-splicer@^2.0.0: 575 | version "2.0.2" 576 | resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz#42a41a16abcd46fd046306cf4f2c3576fffb1c21" 577 | integrity sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw== 578 | dependencies: 579 | inherits "^2.0.1" 580 | stream-splicer "^2.0.0" 581 | 582 | lodash.memoize@~3.0.3: 583 | version "3.0.4" 584 | resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" 585 | integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= 586 | 587 | md5.js@^1.3.4: 588 | version "1.3.5" 589 | resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" 590 | integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== 591 | dependencies: 592 | hash-base "^3.0.0" 593 | inherits "^2.0.1" 594 | safe-buffer "^5.1.2" 595 | 596 | miller-rabin@^4.0.0: 597 | version "4.0.1" 598 | resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" 599 | integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== 600 | dependencies: 601 | bn.js "^4.0.0" 602 | brorand "^1.0.1" 603 | 604 | minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: 605 | version "1.0.1" 606 | resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" 607 | integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== 608 | 609 | minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: 610 | version "1.0.1" 611 | resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" 612 | integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= 613 | 614 | minimatch@^3.0.4: 615 | version "3.0.4" 616 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 617 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 618 | dependencies: 619 | brace-expansion "^1.1.7" 620 | 621 | minimist@0.0.8: 622 | version "0.0.8" 623 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 624 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 625 | 626 | minimist@^1.1.0, minimist@^1.1.1: 627 | version "1.2.5" 628 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 629 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 630 | 631 | mkdirp@^0.5.0: 632 | version "0.5.1" 633 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 634 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 635 | dependencies: 636 | minimist "0.0.8" 637 | 638 | module-deps@^6.0.0: 639 | version "6.2.2" 640 | resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.2.tgz#d8a15c2265dfc119153c29bb47386987d0ee423b" 641 | integrity sha512-a9y6yDv5u5I4A+IPHTnqFxcaKr4p50/zxTjcQJaX2ws9tN/W6J6YXnEKhqRyPhl494dkcxx951onSKVezmI+3w== 642 | dependencies: 643 | JSONStream "^1.0.3" 644 | browser-resolve "^1.7.0" 645 | cached-path-relative "^1.0.2" 646 | concat-stream "~1.6.0" 647 | defined "^1.0.0" 648 | detective "^5.2.0" 649 | duplexer2 "^0.1.2" 650 | inherits "^2.0.1" 651 | parents "^1.0.0" 652 | readable-stream "^2.0.2" 653 | resolve "^1.4.0" 654 | stream-combiner2 "^1.1.1" 655 | subarg "^1.0.0" 656 | through2 "^2.0.0" 657 | xtend "^4.0.0" 658 | 659 | object-assign@^4.1.1: 660 | version "4.1.1" 661 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 662 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 663 | 664 | once@^1.3.0: 665 | version "1.4.0" 666 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 667 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 668 | dependencies: 669 | wrappy "1" 670 | 671 | os-browserify@~0.3.0: 672 | version "0.3.0" 673 | resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" 674 | integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= 675 | 676 | pako@~1.0.5: 677 | version "1.0.11" 678 | resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" 679 | integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== 680 | 681 | parents@^1.0.0, parents@^1.0.1: 682 | version "1.0.1" 683 | resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" 684 | integrity sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E= 685 | dependencies: 686 | path-platform "~0.11.15" 687 | 688 | parse-asn1@^5.0.0: 689 | version "5.1.5" 690 | resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" 691 | integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== 692 | dependencies: 693 | asn1.js "^4.0.0" 694 | browserify-aes "^1.0.0" 695 | create-hash "^1.1.0" 696 | evp_bytestokey "^1.0.0" 697 | pbkdf2 "^3.0.3" 698 | safe-buffer "^5.1.1" 699 | 700 | path-browserify@~0.0.0: 701 | version "0.0.1" 702 | resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" 703 | integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== 704 | 705 | path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: 706 | version "1.0.1" 707 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 708 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 709 | 710 | path-parse@^1.0.6: 711 | version "1.0.6" 712 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 713 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 714 | 715 | path-platform@~0.11.15: 716 | version "0.11.15" 717 | resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" 718 | integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= 719 | 720 | pbkdf2@^3.0.3: 721 | version "3.0.17" 722 | resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" 723 | integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== 724 | dependencies: 725 | create-hash "^1.1.2" 726 | create-hmac "^1.1.4" 727 | ripemd160 "^2.0.1" 728 | safe-buffer "^5.0.1" 729 | sha.js "^2.4.8" 730 | 731 | process-nextick-args@~2.0.0: 732 | version "2.0.1" 733 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 734 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 735 | 736 | process@~0.11.0: 737 | version "0.11.10" 738 | resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" 739 | integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= 740 | 741 | public-encrypt@^4.0.0: 742 | version "4.0.3" 743 | resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" 744 | integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== 745 | dependencies: 746 | bn.js "^4.1.0" 747 | browserify-rsa "^4.0.0" 748 | create-hash "^1.1.0" 749 | parse-asn1 "^5.0.0" 750 | randombytes "^2.0.1" 751 | safe-buffer "^5.1.2" 752 | 753 | punycode@1.3.2: 754 | version "1.3.2" 755 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" 756 | integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= 757 | 758 | punycode@^1.3.2: 759 | version "1.4.1" 760 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" 761 | integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= 762 | 763 | querystring-es3@~0.2.0: 764 | version "0.2.1" 765 | resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" 766 | integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= 767 | 768 | querystring@0.2.0: 769 | version "0.2.0" 770 | resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" 771 | integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= 772 | 773 | randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: 774 | version "2.1.0" 775 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" 776 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== 777 | dependencies: 778 | safe-buffer "^5.1.0" 779 | 780 | randomfill@^1.0.3: 781 | version "1.0.4" 782 | resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" 783 | integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== 784 | dependencies: 785 | randombytes "^2.0.5" 786 | safe-buffer "^5.1.0" 787 | 788 | read-only-stream@^2.0.0: 789 | version "2.0.0" 790 | resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" 791 | integrity sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A= 792 | dependencies: 793 | readable-stream "^2.0.2" 794 | 795 | readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.6: 796 | version "2.3.7" 797 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 798 | integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 799 | dependencies: 800 | core-util-is "~1.0.0" 801 | inherits "~2.0.3" 802 | isarray "~1.0.0" 803 | process-nextick-args "~2.0.0" 804 | safe-buffer "~5.1.1" 805 | string_decoder "~1.1.1" 806 | util-deprecate "~1.0.1" 807 | 808 | readable-stream@^3.0.6: 809 | version "3.6.0" 810 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 811 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 812 | dependencies: 813 | inherits "^2.0.3" 814 | string_decoder "^1.1.1" 815 | util-deprecate "^1.0.1" 816 | 817 | resolve@1.1.7: 818 | version "1.1.7" 819 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 820 | integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= 821 | 822 | resolve@^1.1.4, resolve@^1.4.0: 823 | version "1.15.1" 824 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" 825 | integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== 826 | dependencies: 827 | path-parse "^1.0.6" 828 | 829 | ripemd160@^2.0.0, ripemd160@^2.0.1: 830 | version "2.0.2" 831 | resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" 832 | integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== 833 | dependencies: 834 | hash-base "^3.0.0" 835 | inherits "^2.0.1" 836 | 837 | safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: 838 | version "5.2.0" 839 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" 840 | integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== 841 | 842 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 843 | version "5.1.2" 844 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 845 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 846 | 847 | sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: 848 | version "2.4.11" 849 | resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" 850 | integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== 851 | dependencies: 852 | inherits "^2.0.1" 853 | safe-buffer "^5.0.1" 854 | 855 | shasum-object@^1.0.0: 856 | version "1.0.0" 857 | resolved "https://registry.yarnpkg.com/shasum-object/-/shasum-object-1.0.0.tgz#0b7b74ff5b66ecf9035475522fa05090ac47e29e" 858 | integrity sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg== 859 | dependencies: 860 | fast-safe-stringify "^2.0.7" 861 | 862 | shasum@^1.0.0: 863 | version "1.0.2" 864 | resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" 865 | integrity sha1-5wEjENj0F/TetXEhUOVni4euVl8= 866 | dependencies: 867 | json-stable-stringify "~0.0.0" 868 | sha.js "~2.4.4" 869 | 870 | shell-quote@^1.6.1: 871 | version "1.7.2" 872 | resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" 873 | integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== 874 | 875 | simple-concat@^1.0.0: 876 | version "1.0.0" 877 | resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" 878 | integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= 879 | 880 | source-map@~0.5.3: 881 | version "0.5.7" 882 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 883 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= 884 | 885 | stream-browserify@^2.0.0: 886 | version "2.0.2" 887 | resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" 888 | integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== 889 | dependencies: 890 | inherits "~2.0.1" 891 | readable-stream "^2.0.2" 892 | 893 | stream-combiner2@^1.1.1: 894 | version "1.1.1" 895 | resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" 896 | integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= 897 | dependencies: 898 | duplexer2 "~0.1.0" 899 | readable-stream "^2.0.2" 900 | 901 | stream-http@^3.0.0: 902 | version "3.1.0" 903 | resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.0.tgz#22fb33fe9b4056b4eccf58bd8f400c4b993ffe57" 904 | integrity sha512-cuB6RgO7BqC4FBYzmnvhob5Do3wIdIsXAgGycHJnW+981gHqoYcYz9lqjJrk8WXRddbwPuqPYRl+bag6mYv4lw== 905 | dependencies: 906 | builtin-status-codes "^3.0.0" 907 | inherits "^2.0.1" 908 | readable-stream "^3.0.6" 909 | xtend "^4.0.0" 910 | 911 | stream-splicer@^2.0.0: 912 | version "2.0.1" 913 | resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.1.tgz#0b13b7ee2b5ac7e0609a7463d83899589a363fcd" 914 | integrity sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg== 915 | dependencies: 916 | inherits "^2.0.1" 917 | readable-stream "^2.0.2" 918 | 919 | string_decoder@^1.1.1: 920 | version "1.3.0" 921 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 922 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 923 | dependencies: 924 | safe-buffer "~5.2.0" 925 | 926 | string_decoder@~1.1.1: 927 | version "1.1.1" 928 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 929 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 930 | dependencies: 931 | safe-buffer "~5.1.0" 932 | 933 | subarg@^1.0.0: 934 | version "1.0.0" 935 | resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" 936 | integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= 937 | dependencies: 938 | minimist "^1.1.0" 939 | 940 | syntax-error@^1.1.1: 941 | version "1.4.0" 942 | resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" 943 | integrity sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w== 944 | dependencies: 945 | acorn-node "^1.2.0" 946 | 947 | through2@^2.0.0: 948 | version "2.0.5" 949 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" 950 | integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== 951 | dependencies: 952 | readable-stream "~2.3.6" 953 | xtend "~4.0.1" 954 | 955 | "through@>=2.2.7 <3": 956 | version "2.3.8" 957 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 958 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 959 | 960 | timers-browserify@^1.0.1: 961 | version "1.4.2" 962 | resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" 963 | integrity sha1-ycWLV1voQHN1y14kYtrO50NZ9B0= 964 | dependencies: 965 | process "~0.11.0" 966 | 967 | tty-browserify@0.0.1: 968 | version "0.0.1" 969 | resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" 970 | integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== 971 | 972 | typedarray@^0.0.6: 973 | version "0.0.6" 974 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 975 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 976 | 977 | umd@^3.0.0: 978 | version "3.0.3" 979 | resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" 980 | integrity sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow== 981 | 982 | undeclared-identifiers@^1.1.2: 983 | version "1.1.3" 984 | resolved "https://registry.yarnpkg.com/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz#9254c1d37bdac0ac2b52de4b6722792d2a91e30f" 985 | integrity sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw== 986 | dependencies: 987 | acorn-node "^1.3.0" 988 | dash-ast "^1.0.0" 989 | get-assigned-identifiers "^1.2.0" 990 | simple-concat "^1.0.0" 991 | xtend "^4.0.1" 992 | 993 | url@~0.11.0: 994 | version "0.11.0" 995 | resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" 996 | integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= 997 | dependencies: 998 | punycode "1.3.2" 999 | querystring "0.2.0" 1000 | 1001 | util-deprecate@^1.0.1, util-deprecate@~1.0.1: 1002 | version "1.0.2" 1003 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1004 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1005 | 1006 | util@0.10.3: 1007 | version "0.10.3" 1008 | resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" 1009 | integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= 1010 | dependencies: 1011 | inherits "2.0.1" 1012 | 1013 | util@~0.10.1: 1014 | version "0.10.4" 1015 | resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" 1016 | integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== 1017 | dependencies: 1018 | inherits "2.0.3" 1019 | 1020 | vm-browserify@^1.0.0: 1021 | version "1.1.2" 1022 | resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" 1023 | integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== 1024 | 1025 | wrappy@1: 1026 | version "1.0.2" 1027 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1028 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1029 | 1030 | xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: 1031 | version "4.0.2" 1032 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 1033 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 1034 | -------------------------------------------------------------------------------- /examples/vanilla/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /examples/vanilla/README.md: -------------------------------------------------------------------------------- 1 | ### Instructions 2 | 3 | 1. Run `yarn compile` 4 | 2. Open `index.html` 5 | -------------------------------------------------------------------------------- /examples/vanilla/bundle.js: -------------------------------------------------------------------------------- 1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 1); 78 | 79 | 80 | var locks = []; 81 | var documentListenerAdded = false; 82 | var initialClientY = -1; 83 | var previousBodyOverflowSetting = void 0; 84 | var previousBodyPosition = void 0; 85 | var previousBodyPaddingRight = void 0; 86 | 87 | // returns true if `el` should be allowed to receive touchmove events. 88 | var allowTouchMove = function allowTouchMove(el) { 89 | return locks.some(function (lock) { 90 | if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { 91 | return true; 92 | } 93 | 94 | return false; 95 | }); 96 | }; 97 | 98 | var preventDefault = function preventDefault(rawEvent) { 99 | var e = rawEvent || window.event; 100 | 101 | // For the case whereby consumers adds a touchmove event listener to document. 102 | // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false }) 103 | // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then 104 | // the touchmove event on document will break. 105 | if (allowTouchMove(e.target)) { 106 | return true; 107 | } 108 | 109 | // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom). 110 | if (e.touches.length > 1) return true; 111 | 112 | if (e.preventDefault) e.preventDefault(); 113 | 114 | return false; 115 | }; 116 | 117 | var setOverflowHidden = function setOverflowHidden(options) { 118 | // If previousBodyPaddingRight is already set, don't set it again. 119 | if (previousBodyPaddingRight === undefined) { 120 | var _reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; 121 | var scrollBarGap = window.innerWidth - document.documentElement.clientWidth; 122 | 123 | if (_reserveScrollBarGap && scrollBarGap > 0) { 124 | var computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10); 125 | previousBodyPaddingRight = document.body.style.paddingRight; 126 | document.body.style.paddingRight = computedBodyPaddingRight + scrollBarGap + 'px'; 127 | } 128 | } 129 | 130 | // If previousBodyOverflowSetting is already set, don't set it again. 131 | if (previousBodyOverflowSetting === undefined) { 132 | previousBodyOverflowSetting = document.body.style.overflow; 133 | document.body.style.overflow = 'hidden'; 134 | } 135 | }; 136 | 137 | var restoreOverflowSetting = function restoreOverflowSetting() { 138 | if (previousBodyPaddingRight !== undefined) { 139 | document.body.style.paddingRight = previousBodyPaddingRight; 140 | 141 | // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it 142 | // can be set again. 143 | previousBodyPaddingRight = undefined; 144 | } 145 | 146 | if (previousBodyOverflowSetting !== undefined) { 147 | document.body.style.overflow = previousBodyOverflowSetting; 148 | 149 | // Restore previousBodyOverflowSetting to undefined 150 | // so setOverflowHidden knows it can be set again. 151 | previousBodyOverflowSetting = undefined; 152 | } 153 | }; 154 | 155 | var setPositionFixed = function setPositionFixed() { 156 | return window.requestAnimationFrame(function () { 157 | // If previousBodyPosition is already set, don't set it again. 158 | if (previousBodyPosition === undefined) { 159 | previousBodyPosition = { 160 | position: document.body.style.position, 161 | top: document.body.style.top, 162 | left: document.body.style.left 163 | }; 164 | 165 | // Update the dom inside an animation frame 166 | var _window = window, 167 | scrollY = _window.scrollY, 168 | scrollX = _window.scrollX, 169 | innerHeight = _window.innerHeight; 170 | 171 | document.body.style.position = 'fixed'; 172 | document.body.style.top = -scrollY; 173 | document.body.style.left = -scrollX; 174 | 175 | setTimeout(function () { 176 | return window.requestAnimationFrame(function () { 177 | // Attempt to check if the bottom bar appeared due to the position change 178 | var bottomBarHeight = innerHeight - window.innerHeight; 179 | // console.log({ 180 | // bottomBarHeight, 181 | // innerHeight, 182 | // innerHeightNew: window.innerHeight, 183 | // scrollY 184 | // }) 185 | if (bottomBarHeight && scrollY >= innerHeight) { 186 | // Move the content further up so that the bottom bar doesn't hide it 187 | document.body.style.top = -(scrollY + bottomBarHeight); 188 | } 189 | }); 190 | }, 300); 191 | } 192 | }); 193 | }; 194 | 195 | var restorePositionSetting = function restorePositionSetting() { 196 | if (previousBodyPosition !== undefined) { 197 | // Convert the position from "px" to Int 198 | var y = -parseInt(document.body.style.top, 10); 199 | var x = -parseInt(document.body.style.left, 10); 200 | 201 | // Restore styles 202 | document.body.style.position = previousBodyPosition.position; 203 | document.body.style.top = previousBodyPosition.top; 204 | document.body.style.left = previousBodyPosition.left; 205 | 206 | // Restore scroll 207 | window.scrollTo(x, y); 208 | 209 | previousBodyPosition = undefined; 210 | } 211 | }; 212 | 213 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions 214 | var isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled(targetElement) { 215 | return targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false; 216 | }; 217 | 218 | var handleScroll = function handleScroll(event, targetElement) { 219 | var clientY = event.targetTouches[0].clientY - initialClientY; 220 | 221 | if (allowTouchMove(event.target)) { 222 | return false; 223 | } 224 | 225 | if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { 226 | // element is at the top of its scroll. 227 | return preventDefault(event); 228 | } 229 | 230 | if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { 231 | // element is at the bottom of its scroll. 232 | return preventDefault(event); 233 | } 234 | 235 | event.stopPropagation(); 236 | return true; 237 | }; 238 | 239 | var disableBodyScroll = exports.disableBodyScroll = function disableBodyScroll(targetElement, options) { 240 | // targetElement must be provided 241 | if (!targetElement) { 242 | // eslint-disable-next-line no-console 243 | console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.'); 244 | return; 245 | } 246 | 247 | // disableBodyScroll must not have been called on this targetElement before 248 | if (locks.some(function (lock) { 249 | return lock.targetElement === targetElement; 250 | })) { 251 | return; 252 | } 253 | 254 | var lock = { 255 | targetElement: targetElement, 256 | options: options || {} 257 | }; 258 | 259 | locks = [].concat(_toConsumableArray(locks), [lock]); 260 | 261 | if (isIosDevice) { 262 | setPositionFixed(); 263 | } else { 264 | setOverflowHidden(options); 265 | } 266 | 267 | if (isIosDevice) { 268 | targetElement.ontouchstart = function (event) { 269 | if (event.targetTouches.length === 1) { 270 | // detect single touch. 271 | initialClientY = event.targetTouches[0].clientY; 272 | } 273 | }; 274 | targetElement.ontouchmove = function (event) { 275 | if (event.targetTouches.length === 1) { 276 | // detect single touch. 277 | handleScroll(event, targetElement); 278 | } 279 | }; 280 | 281 | if (!documentListenerAdded) { 282 | document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 283 | documentListenerAdded = true; 284 | } 285 | } 286 | }; 287 | 288 | var clearAllBodyScrollLocks = exports.clearAllBodyScrollLocks = function clearAllBodyScrollLocks() { 289 | if (isIosDevice) { 290 | // Clear all locks ontouchstart/ontouchmove handlers, and the references. 291 | locks.forEach(function (lock) { 292 | lock.targetElement.ontouchstart = null; 293 | lock.targetElement.ontouchmove = null; 294 | }); 295 | 296 | if (documentListenerAdded) { 297 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 298 | documentListenerAdded = false; 299 | } 300 | 301 | // Reset initial clientY. 302 | initialClientY = -1; 303 | } 304 | 305 | if (isIosDevice) { 306 | restorePositionSetting(); 307 | } else { 308 | restoreOverflowSetting(); 309 | } 310 | 311 | locks = []; 312 | }; 313 | 314 | var enableBodyScroll = exports.enableBodyScroll = function enableBodyScroll(targetElement) { 315 | if (!targetElement) { 316 | // eslint-disable-next-line no-console 317 | console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.'); 318 | return; 319 | } 320 | 321 | locks = locks.filter(function (lock) { 322 | return lock.targetElement !== targetElement; 323 | }); 324 | 325 | if (isIosDevice) { 326 | targetElement.ontouchstart = null; 327 | targetElement.ontouchmove = null; 328 | 329 | if (documentListenerAdded && locks.length === 0) { 330 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 331 | documentListenerAdded = false; 332 | } 333 | } 334 | 335 | if (isIosDevice) { 336 | restorePositionSetting(); 337 | } else { 338 | restoreOverflowSetting(); 339 | } 340 | }; 341 | }); 342 | 343 | 344 | },{}]},{},[1]); 345 | -------------------------------------------------------------------------------- /examples/vanilla/main.js: -------------------------------------------------------------------------------- 1 | const bodyScrollLock = require('../../lib/bodyScrollLock.js'); 2 | 3 | function initialize(id) { 4 | const disableBodyScrollButton = document.querySelector(`.disableBodyScroll.${id}`); 5 | const enableBodyScrollButton = document.querySelector(`.enableBodyScroll.${id}`); 6 | const statusElement = document.querySelector(`.bodyScrollLockStatus.${id}`); 7 | 8 | disableBodyScrollButton.onclick = function() { 9 | console.info(`disableBodyScrollButton ${id}`); 10 | bodyScrollLock.disableBodyScroll(document.querySelector(`.scrollTarget.${id}`)); 11 | 12 | statusElement.innerHTML = ' — Scroll Locked'; 13 | statusElement.style.color = 'red'; 14 | }; 15 | 16 | enableBodyScrollButton.onclick = function() { 17 | console.info(`enableBodyScrollButton ${id}`); 18 | bodyScrollLock.enableBodyScroll(document.querySelector(`.scrollTarget.${id}`)); 19 | 20 | statusElement.innerHTML = ' — Scroll Unlocked'; 21 | statusElement.style.color = ''; 22 | }; 23 | } 24 | 25 | initialize('top') 26 | initialize('bottom') -------------------------------------------------------------------------------- /examples/vanilla/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "alias": ["bodyscrolllock"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/vanilla/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "body-scroll-lock-example", 3 | "description": "Example for body scroll lock", 4 | "author": "Will Po", 5 | "license": "MIT", 6 | "dependencies": {}, 7 | "scripts": { 8 | "compile": "browserify ./main.js > bundle.js", 9 | "deployNow": "now --prod" 10 | }, 11 | "devDependencies": { 12 | "browserify": "^16.5.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /flow-typed/globals.js: -------------------------------------------------------------------------------- 1 | declare var document: any; 2 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willmcpo/body-scroll-lock/79c4cf2c956eb7d5cf8d54a03d12751bc6ac8aa3/images/logo.png -------------------------------------------------------------------------------- /lib/bodyScrollLock.es6.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Older browsers don't support event options, feature detect it. 4 | 5 | // Adopted and modified solution from Bohdan Didukh (2017) 6 | // https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi 7 | 8 | let hasPassiveEvents = false; 9 | if (typeof window !== 'undefined') { 10 | const passiveTestOptions = { 11 | get passive() { 12 | hasPassiveEvents = true; 13 | return undefined; 14 | } 15 | }; 16 | window.addEventListener('testPassive', null, passiveTestOptions); 17 | window.removeEventListener('testPassive', null, passiveTestOptions); 18 | } 19 | 20 | const isIosDevice = typeof window !== 'undefined' && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === 'MacIntel' && window.navigator.maxTouchPoints > 1); 21 | 22 | 23 | let locks = []; 24 | let documentListenerAdded = false; 25 | let initialClientY = -1; 26 | let previousBodyOverflowSetting; 27 | let previousBodyPosition; 28 | let previousBodyPaddingRight; 29 | 30 | // returns true if `el` should be allowed to receive touchmove events. 31 | const allowTouchMove = el => locks.some(lock => { 32 | if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { 33 | return true; 34 | } 35 | 36 | return false; 37 | }); 38 | 39 | const preventDefault = rawEvent => { 40 | const e = rawEvent || window.event; 41 | 42 | // For the case whereby consumers adds a touchmove event listener to document. 43 | // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false }) 44 | // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then 45 | // the touchmove event on document will break. 46 | if (allowTouchMove(e.target)) { 47 | return true; 48 | } 49 | 50 | // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom). 51 | if (e.touches.length > 1) return true; 52 | 53 | if (e.preventDefault) e.preventDefault(); 54 | 55 | return false; 56 | }; 57 | 58 | const setOverflowHidden = options => { 59 | // If previousBodyPaddingRight is already set, don't set it again. 60 | if (previousBodyPaddingRight === undefined) { 61 | const reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; 62 | const scrollBarGap = window.innerWidth - document.documentElement.clientWidth; 63 | 64 | if (reserveScrollBarGap && scrollBarGap > 0) { 65 | const computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10); 66 | previousBodyPaddingRight = document.body.style.paddingRight; 67 | document.body.style.paddingRight = `${computedBodyPaddingRight + scrollBarGap}px`; 68 | } 69 | } 70 | 71 | // If previousBodyOverflowSetting is already set, don't set it again. 72 | if (previousBodyOverflowSetting === undefined) { 73 | previousBodyOverflowSetting = document.body.style.overflow; 74 | document.body.style.overflow = 'hidden'; 75 | } 76 | }; 77 | 78 | const restoreOverflowSetting = () => { 79 | if (previousBodyPaddingRight !== undefined) { 80 | document.body.style.paddingRight = previousBodyPaddingRight; 81 | 82 | // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it 83 | // can be set again. 84 | previousBodyPaddingRight = undefined; 85 | } 86 | 87 | if (previousBodyOverflowSetting !== undefined) { 88 | document.body.style.overflow = previousBodyOverflowSetting; 89 | 90 | // Restore previousBodyOverflowSetting to undefined 91 | // so setOverflowHidden knows it can be set again. 92 | previousBodyOverflowSetting = undefined; 93 | } 94 | }; 95 | 96 | const setPositionFixed = () => window.requestAnimationFrame(() => { 97 | // If previousBodyPosition is already set, don't set it again. 98 | if (previousBodyPosition === undefined) { 99 | previousBodyPosition = { 100 | position: document.body.style.position, 101 | top: document.body.style.top, 102 | left: document.body.style.left 103 | }; 104 | 105 | // Update the dom inside an animation frame 106 | const { scrollY, scrollX, innerHeight } = window; 107 | document.body.style.position = 'fixed'; 108 | document.body.style.top = `${-scrollY}px`; 109 | document.body.style.left = `${-scrollX}px`; 110 | 111 | setTimeout(() => window.requestAnimationFrame(() => { 112 | // Attempt to check if the bottom bar appeared due to the position change 113 | const bottomBarHeight = innerHeight - window.innerHeight; 114 | if (bottomBarHeight && scrollY >= innerHeight) { 115 | // Move the content further up so that the bottom bar doesn't hide it 116 | document.body.style.top = -(scrollY + bottomBarHeight); 117 | } 118 | }), 300); 119 | } 120 | }); 121 | 122 | const restorePositionSetting = () => { 123 | if (previousBodyPosition !== undefined) { 124 | // Convert the position from "px" to Int 125 | const y = -parseInt(document.body.style.top, 10); 126 | const x = -parseInt(document.body.style.left, 10); 127 | 128 | // Restore styles 129 | document.body.style.position = previousBodyPosition.position; 130 | document.body.style.top = previousBodyPosition.top; 131 | document.body.style.left = previousBodyPosition.left; 132 | 133 | // Restore scroll 134 | window.scrollTo(x, y); 135 | 136 | previousBodyPosition = undefined; 137 | } 138 | }; 139 | 140 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions 141 | const isTargetElementTotallyScrolled = targetElement => targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false; 142 | 143 | const handleScroll = (event, targetElement) => { 144 | const clientY = event.targetTouches[0].clientY - initialClientY; 145 | 146 | if (allowTouchMove(event.target)) { 147 | return false; 148 | } 149 | 150 | if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { 151 | // element is at the top of its scroll. 152 | return preventDefault(event); 153 | } 154 | 155 | if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { 156 | // element is at the bottom of its scroll. 157 | return preventDefault(event); 158 | } 159 | 160 | event.stopPropagation(); 161 | return true; 162 | }; 163 | 164 | export const disableBodyScroll = (targetElement, options) => { 165 | // targetElement must be provided 166 | if (!targetElement) { 167 | // eslint-disable-next-line no-console 168 | console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.'); 169 | return; 170 | } 171 | 172 | // disableBodyScroll must not have been called on this targetElement before 173 | if (locks.some(lock => lock.targetElement === targetElement)) { 174 | return; 175 | } 176 | 177 | const lock = { 178 | targetElement, 179 | options: options || {} 180 | }; 181 | 182 | locks = [...locks, lock]; 183 | 184 | if (isIosDevice) { 185 | setPositionFixed(); 186 | } else { 187 | setOverflowHidden(options); 188 | } 189 | 190 | if (isIosDevice) { 191 | targetElement.ontouchstart = event => { 192 | if (event.targetTouches.length === 1) { 193 | // detect single touch. 194 | initialClientY = event.targetTouches[0].clientY; 195 | } 196 | }; 197 | targetElement.ontouchmove = event => { 198 | if (event.targetTouches.length === 1) { 199 | // detect single touch. 200 | handleScroll(event, targetElement); 201 | } 202 | }; 203 | 204 | if (!documentListenerAdded) { 205 | document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 206 | documentListenerAdded = true; 207 | } 208 | } 209 | }; 210 | 211 | export const clearAllBodyScrollLocks = () => { 212 | if (isIosDevice) { 213 | // Clear all locks ontouchstart/ontouchmove handlers, and the references. 214 | locks.forEach(lock => { 215 | lock.targetElement.ontouchstart = null; 216 | lock.targetElement.ontouchmove = null; 217 | }); 218 | 219 | if (documentListenerAdded) { 220 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 221 | documentListenerAdded = false; 222 | } 223 | 224 | // Reset initial clientY. 225 | initialClientY = -1; 226 | } 227 | 228 | if (isIosDevice) { 229 | restorePositionSetting(); 230 | } else { 231 | restoreOverflowSetting(); 232 | } 233 | 234 | locks = []; 235 | }; 236 | 237 | export const enableBodyScroll = targetElement => { 238 | if (!targetElement) { 239 | // eslint-disable-next-line no-console 240 | console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.'); 241 | return; 242 | } 243 | 244 | locks = locks.filter(lock => lock.targetElement !== targetElement); 245 | 246 | if (isIosDevice) { 247 | targetElement.ontouchstart = null; 248 | targetElement.ontouchmove = null; 249 | 250 | if (documentListenerAdded && locks.length === 0) { 251 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 252 | documentListenerAdded = false; 253 | } 254 | } 255 | 256 | if (isIosDevice) { 257 | restorePositionSetting(); 258 | } else { 259 | restoreOverflowSetting(); 260 | } 261 | }; 262 | 263 | -------------------------------------------------------------------------------- /lib/bodyScrollLock.esm.js: -------------------------------------------------------------------------------- 1 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 2 | 3 | // Older browsers don't support event options, feature detect it. 4 | 5 | // Adopted and modified solution from Bohdan Didukh (2017) 6 | // https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi 7 | 8 | var hasPassiveEvents = false; 9 | if (typeof window !== 'undefined') { 10 | var passiveTestOptions = { 11 | get passive() { 12 | hasPassiveEvents = true; 13 | return undefined; 14 | } 15 | }; 16 | window.addEventListener('testPassive', null, passiveTestOptions); 17 | window.removeEventListener('testPassive', null, passiveTestOptions); 18 | } 19 | 20 | var isIosDevice = typeof window !== 'undefined' && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === 'MacIntel' && window.navigator.maxTouchPoints > 1); 21 | 22 | 23 | var locks = []; 24 | var documentListenerAdded = false; 25 | var initialClientY = -1; 26 | var previousBodyOverflowSetting = void 0; 27 | var previousBodyPosition = void 0; 28 | var previousBodyPaddingRight = void 0; 29 | 30 | // returns true if `el` should be allowed to receive touchmove events. 31 | var allowTouchMove = function allowTouchMove(el) { 32 | return locks.some(function (lock) { 33 | if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { 34 | return true; 35 | } 36 | 37 | return false; 38 | }); 39 | }; 40 | 41 | var preventDefault = function preventDefault(rawEvent) { 42 | var e = rawEvent || window.event; 43 | 44 | // For the case whereby consumers adds a touchmove event listener to document. 45 | // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false }) 46 | // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then 47 | // the touchmove event on document will break. 48 | if (allowTouchMove(e.target)) { 49 | return true; 50 | } 51 | 52 | // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom). 53 | if (e.touches.length > 1) return true; 54 | 55 | if (e.preventDefault) e.preventDefault(); 56 | 57 | return false; 58 | }; 59 | 60 | var setOverflowHidden = function setOverflowHidden(options) { 61 | // If previousBodyPaddingRight is already set, don't set it again. 62 | if (previousBodyPaddingRight === undefined) { 63 | var _reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; 64 | var scrollBarGap = window.innerWidth - document.documentElement.clientWidth; 65 | 66 | if (_reserveScrollBarGap && scrollBarGap > 0) { 67 | var computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10); 68 | previousBodyPaddingRight = document.body.style.paddingRight; 69 | document.body.style.paddingRight = computedBodyPaddingRight + scrollBarGap + 'px'; 70 | } 71 | } 72 | 73 | // If previousBodyOverflowSetting is already set, don't set it again. 74 | if (previousBodyOverflowSetting === undefined) { 75 | previousBodyOverflowSetting = document.body.style.overflow; 76 | document.body.style.overflow = 'hidden'; 77 | } 78 | }; 79 | 80 | var restoreOverflowSetting = function restoreOverflowSetting() { 81 | if (previousBodyPaddingRight !== undefined) { 82 | document.body.style.paddingRight = previousBodyPaddingRight; 83 | 84 | // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it 85 | // can be set again. 86 | previousBodyPaddingRight = undefined; 87 | } 88 | 89 | if (previousBodyOverflowSetting !== undefined) { 90 | document.body.style.overflow = previousBodyOverflowSetting; 91 | 92 | // Restore previousBodyOverflowSetting to undefined 93 | // so setOverflowHidden knows it can be set again. 94 | previousBodyOverflowSetting = undefined; 95 | } 96 | }; 97 | 98 | var setPositionFixed = function setPositionFixed() { 99 | return window.requestAnimationFrame(function () { 100 | // If previousBodyPosition is already set, don't set it again. 101 | if (previousBodyPosition === undefined) { 102 | previousBodyPosition = { 103 | position: document.body.style.position, 104 | top: document.body.style.top, 105 | left: document.body.style.left 106 | }; 107 | 108 | // Update the dom inside an animation frame 109 | var _window = window, 110 | scrollY = _window.scrollY, 111 | scrollX = _window.scrollX, 112 | innerHeight = _window.innerHeight; 113 | 114 | document.body.style.position = 'fixed'; 115 | document.body.style.top = -scrollY + 'px'; 116 | document.body.style.left = -scrollX + 'px'; 117 | 118 | setTimeout(function () { 119 | return window.requestAnimationFrame(function () { 120 | // Attempt to check if the bottom bar appeared due to the position change 121 | var bottomBarHeight = innerHeight - window.innerHeight; 122 | if (bottomBarHeight && scrollY >= innerHeight) { 123 | // Move the content further up so that the bottom bar doesn't hide it 124 | document.body.style.top = -(scrollY + bottomBarHeight); 125 | } 126 | }); 127 | }, 300); 128 | } 129 | }); 130 | }; 131 | 132 | var restorePositionSetting = function restorePositionSetting() { 133 | if (previousBodyPosition !== undefined) { 134 | // Convert the position from "px" to Int 135 | var y = -parseInt(document.body.style.top, 10); 136 | var x = -parseInt(document.body.style.left, 10); 137 | 138 | // Restore styles 139 | document.body.style.position = previousBodyPosition.position; 140 | document.body.style.top = previousBodyPosition.top; 141 | document.body.style.left = previousBodyPosition.left; 142 | 143 | // Restore scroll 144 | window.scrollTo(x, y); 145 | 146 | previousBodyPosition = undefined; 147 | } 148 | }; 149 | 150 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions 151 | var isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled(targetElement) { 152 | return targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false; 153 | }; 154 | 155 | var handleScroll = function handleScroll(event, targetElement) { 156 | var clientY = event.targetTouches[0].clientY - initialClientY; 157 | 158 | if (allowTouchMove(event.target)) { 159 | return false; 160 | } 161 | 162 | if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { 163 | // element is at the top of its scroll. 164 | return preventDefault(event); 165 | } 166 | 167 | if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { 168 | // element is at the bottom of its scroll. 169 | return preventDefault(event); 170 | } 171 | 172 | event.stopPropagation(); 173 | return true; 174 | }; 175 | 176 | export var disableBodyScroll = function disableBodyScroll(targetElement, options) { 177 | // targetElement must be provided 178 | if (!targetElement) { 179 | // eslint-disable-next-line no-console 180 | console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.'); 181 | return; 182 | } 183 | 184 | // disableBodyScroll must not have been called on this targetElement before 185 | if (locks.some(function (lock) { 186 | return lock.targetElement === targetElement; 187 | })) { 188 | return; 189 | } 190 | 191 | var lock = { 192 | targetElement: targetElement, 193 | options: options || {} 194 | }; 195 | 196 | locks = [].concat(_toConsumableArray(locks), [lock]); 197 | 198 | if (isIosDevice) { 199 | setPositionFixed(); 200 | } else { 201 | setOverflowHidden(options); 202 | } 203 | 204 | if (isIosDevice) { 205 | targetElement.ontouchstart = function (event) { 206 | if (event.targetTouches.length === 1) { 207 | // detect single touch. 208 | initialClientY = event.targetTouches[0].clientY; 209 | } 210 | }; 211 | targetElement.ontouchmove = function (event) { 212 | if (event.targetTouches.length === 1) { 213 | // detect single touch. 214 | handleScroll(event, targetElement); 215 | } 216 | }; 217 | 218 | if (!documentListenerAdded) { 219 | document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 220 | documentListenerAdded = true; 221 | } 222 | } 223 | }; 224 | 225 | export var clearAllBodyScrollLocks = function clearAllBodyScrollLocks() { 226 | if (isIosDevice) { 227 | // Clear all locks ontouchstart/ontouchmove handlers, and the references. 228 | locks.forEach(function (lock) { 229 | lock.targetElement.ontouchstart = null; 230 | lock.targetElement.ontouchmove = null; 231 | }); 232 | 233 | if (documentListenerAdded) { 234 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 235 | documentListenerAdded = false; 236 | } 237 | 238 | // Reset initial clientY. 239 | initialClientY = -1; 240 | } 241 | 242 | if (isIosDevice) { 243 | restorePositionSetting(); 244 | } else { 245 | restoreOverflowSetting(); 246 | } 247 | 248 | locks = []; 249 | }; 250 | 251 | export var enableBodyScroll = function enableBodyScroll(targetElement) { 252 | if (!targetElement) { 253 | // eslint-disable-next-line no-console 254 | console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.'); 255 | return; 256 | } 257 | 258 | locks = locks.filter(function (lock) { 259 | return lock.targetElement !== targetElement; 260 | }); 261 | 262 | if (isIosDevice) { 263 | targetElement.ontouchstart = null; 264 | targetElement.ontouchmove = null; 265 | 266 | if (documentListenerAdded && locks.length === 0) { 267 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 268 | documentListenerAdded = false; 269 | } 270 | } 271 | 272 | if (isIosDevice) { 273 | restorePositionSetting(); 274 | } else { 275 | restoreOverflowSetting(); 276 | } 277 | }; 278 | 279 | -------------------------------------------------------------------------------- /lib/bodyScrollLock.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define(['exports'], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(exports); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports); 11 | global.bodyScrollLock = mod.exports; 12 | } 13 | })(this, function (exports) { 14 | 'use strict'; 15 | 16 | Object.defineProperty(exports, "__esModule", { 17 | value: true 18 | }); 19 | 20 | function _toConsumableArray(arr) { 21 | if (Array.isArray(arr)) { 22 | for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { 23 | arr2[i] = arr[i]; 24 | } 25 | 26 | return arr2; 27 | } else { 28 | return Array.from(arr); 29 | } 30 | } 31 | 32 | // Older browsers don't support event options, feature detect it. 33 | 34 | // Adopted and modified solution from Bohdan Didukh (2017) 35 | // https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi 36 | 37 | var hasPassiveEvents = false; 38 | if (typeof window !== 'undefined') { 39 | var passiveTestOptions = { 40 | get passive() { 41 | hasPassiveEvents = true; 42 | return undefined; 43 | } 44 | }; 45 | window.addEventListener('testPassive', null, passiveTestOptions); 46 | window.removeEventListener('testPassive', null, passiveTestOptions); 47 | } 48 | 49 | var isIosDevice = typeof window !== 'undefined' && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === 'MacIntel' && window.navigator.maxTouchPoints > 1); 50 | 51 | 52 | var locks = []; 53 | var documentListenerAdded = false; 54 | var initialClientY = -1; 55 | var previousBodyOverflowSetting = void 0; 56 | var previousBodyPosition = void 0; 57 | var previousBodyPaddingRight = void 0; 58 | 59 | // returns true if `el` should be allowed to receive touchmove events. 60 | var allowTouchMove = function allowTouchMove(el) { 61 | return locks.some(function (lock) { 62 | if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { 63 | return true; 64 | } 65 | 66 | return false; 67 | }); 68 | }; 69 | 70 | var preventDefault = function preventDefault(rawEvent) { 71 | var e = rawEvent || window.event; 72 | 73 | // For the case whereby consumers adds a touchmove event listener to document. 74 | // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false }) 75 | // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then 76 | // the touchmove event on document will break. 77 | if (allowTouchMove(e.target)) { 78 | return true; 79 | } 80 | 81 | // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom). 82 | if (e.touches.length > 1) return true; 83 | 84 | if (e.preventDefault) e.preventDefault(); 85 | 86 | return false; 87 | }; 88 | 89 | var setOverflowHidden = function setOverflowHidden(options) { 90 | // If previousBodyPaddingRight is already set, don't set it again. 91 | if (previousBodyPaddingRight === undefined) { 92 | var _reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; 93 | var scrollBarGap = window.innerWidth - document.documentElement.clientWidth; 94 | 95 | if (_reserveScrollBarGap && scrollBarGap > 0) { 96 | var computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10); 97 | previousBodyPaddingRight = document.body.style.paddingRight; 98 | document.body.style.paddingRight = computedBodyPaddingRight + scrollBarGap + 'px'; 99 | } 100 | } 101 | 102 | // If previousBodyOverflowSetting is already set, don't set it again. 103 | if (previousBodyOverflowSetting === undefined) { 104 | previousBodyOverflowSetting = document.body.style.overflow; 105 | document.body.style.overflow = 'hidden'; 106 | } 107 | }; 108 | 109 | var restoreOverflowSetting = function restoreOverflowSetting() { 110 | if (previousBodyPaddingRight !== undefined) { 111 | document.body.style.paddingRight = previousBodyPaddingRight; 112 | 113 | // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it 114 | // can be set again. 115 | previousBodyPaddingRight = undefined; 116 | } 117 | 118 | if (previousBodyOverflowSetting !== undefined) { 119 | document.body.style.overflow = previousBodyOverflowSetting; 120 | 121 | // Restore previousBodyOverflowSetting to undefined 122 | // so setOverflowHidden knows it can be set again. 123 | previousBodyOverflowSetting = undefined; 124 | } 125 | }; 126 | 127 | var setPositionFixed = function setPositionFixed() { 128 | return window.requestAnimationFrame(function () { 129 | // If previousBodyPosition is already set, don't set it again. 130 | if (previousBodyPosition === undefined) { 131 | previousBodyPosition = { 132 | position: document.body.style.position, 133 | top: document.body.style.top, 134 | left: document.body.style.left 135 | }; 136 | 137 | // Update the dom inside an animation frame 138 | var _window = window, 139 | scrollY = _window.scrollY, 140 | scrollX = _window.scrollX, 141 | innerHeight = _window.innerHeight; 142 | 143 | document.body.style.position = 'fixed'; 144 | document.body.style.top = -scrollY + 'px'; 145 | document.body.style.left = -scrollX + 'px'; 146 | 147 | setTimeout(function () { 148 | return window.requestAnimationFrame(function () { 149 | // Attempt to check if the bottom bar appeared due to the position change 150 | var bottomBarHeight = innerHeight - window.innerHeight; 151 | if (bottomBarHeight && scrollY >= innerHeight) { 152 | // Move the content further up so that the bottom bar doesn't hide it 153 | document.body.style.top = -(scrollY + bottomBarHeight); 154 | } 155 | }); 156 | }, 300); 157 | } 158 | }); 159 | }; 160 | 161 | var restorePositionSetting = function restorePositionSetting() { 162 | if (previousBodyPosition !== undefined) { 163 | // Convert the position from "px" to Int 164 | var y = -parseInt(document.body.style.top, 10); 165 | var x = -parseInt(document.body.style.left, 10); 166 | 167 | // Restore styles 168 | document.body.style.position = previousBodyPosition.position; 169 | document.body.style.top = previousBodyPosition.top; 170 | document.body.style.left = previousBodyPosition.left; 171 | 172 | // Restore scroll 173 | window.scrollTo(x, y); 174 | 175 | previousBodyPosition = undefined; 176 | } 177 | }; 178 | 179 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions 180 | var isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled(targetElement) { 181 | return targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false; 182 | }; 183 | 184 | var handleScroll = function handleScroll(event, targetElement) { 185 | var clientY = event.targetTouches[0].clientY - initialClientY; 186 | 187 | if (allowTouchMove(event.target)) { 188 | return false; 189 | } 190 | 191 | if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { 192 | // element is at the top of its scroll. 193 | return preventDefault(event); 194 | } 195 | 196 | if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { 197 | // element is at the bottom of its scroll. 198 | return preventDefault(event); 199 | } 200 | 201 | event.stopPropagation(); 202 | return true; 203 | }; 204 | 205 | var disableBodyScroll = exports.disableBodyScroll = function disableBodyScroll(targetElement, options) { 206 | // targetElement must be provided 207 | if (!targetElement) { 208 | // eslint-disable-next-line no-console 209 | console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.'); 210 | return; 211 | } 212 | 213 | // disableBodyScroll must not have been called on this targetElement before 214 | if (locks.some(function (lock) { 215 | return lock.targetElement === targetElement; 216 | })) { 217 | return; 218 | } 219 | 220 | var lock = { 221 | targetElement: targetElement, 222 | options: options || {} 223 | }; 224 | 225 | locks = [].concat(_toConsumableArray(locks), [lock]); 226 | 227 | if (isIosDevice) { 228 | setPositionFixed(); 229 | } else { 230 | setOverflowHidden(options); 231 | } 232 | 233 | if (isIosDevice) { 234 | targetElement.ontouchstart = function (event) { 235 | if (event.targetTouches.length === 1) { 236 | // detect single touch. 237 | initialClientY = event.targetTouches[0].clientY; 238 | } 239 | }; 240 | targetElement.ontouchmove = function (event) { 241 | if (event.targetTouches.length === 1) { 242 | // detect single touch. 243 | handleScroll(event, targetElement); 244 | } 245 | }; 246 | 247 | if (!documentListenerAdded) { 248 | document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 249 | documentListenerAdded = true; 250 | } 251 | } 252 | }; 253 | 254 | var clearAllBodyScrollLocks = exports.clearAllBodyScrollLocks = function clearAllBodyScrollLocks() { 255 | if (isIosDevice) { 256 | // Clear all locks ontouchstart/ontouchmove handlers, and the references. 257 | locks.forEach(function (lock) { 258 | lock.targetElement.ontouchstart = null; 259 | lock.targetElement.ontouchmove = null; 260 | }); 261 | 262 | if (documentListenerAdded) { 263 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 264 | documentListenerAdded = false; 265 | } 266 | 267 | // Reset initial clientY. 268 | initialClientY = -1; 269 | } 270 | 271 | if (isIosDevice) { 272 | restorePositionSetting(); 273 | } else { 274 | restoreOverflowSetting(); 275 | } 276 | 277 | locks = []; 278 | }; 279 | 280 | var enableBodyScroll = exports.enableBodyScroll = function enableBodyScroll(targetElement) { 281 | if (!targetElement) { 282 | // eslint-disable-next-line no-console 283 | console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.'); 284 | return; 285 | } 286 | 287 | locks = locks.filter(function (lock) { 288 | return lock.targetElement !== targetElement; 289 | }); 290 | 291 | if (isIosDevice) { 292 | targetElement.ontouchstart = null; 293 | targetElement.ontouchmove = null; 294 | 295 | if (documentListenerAdded && locks.length === 0) { 296 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 297 | documentListenerAdded = false; 298 | } 299 | } 300 | 301 | if (isIosDevice) { 302 | restorePositionSetting(); 303 | } else { 304 | restoreOverflowSetting(); 305 | } 306 | }; 307 | }); 308 | 309 | -------------------------------------------------------------------------------- /lib/bodyScrollLock.min.js: -------------------------------------------------------------------------------- 1 | !function(e,o){if("function"==typeof define&&define.amd)define(["exports"],o);else if("undefined"!=typeof exports)o(exports);else{var t={};o(t),e.bodyScrollLock=t}}(this,function(exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=!1;if("undefined"!=typeof window){var e={get passive(){t=!0}};window.addEventListener("testPassive",null,e),window.removeEventListener("testPassive",null,e)}function d(o){return s.some(function(e){return!(!e.options.allowTouchMove||!e.options.allowTouchMove(o))})}function l(e){var o=e||window.event;return!!d(o.target)||(1 lib/bodyScrollLock.esm.js", 63 | "buildEs6": "BABEL_ENV=es6 babel src/bodyScrollLock.js > lib/bodyScrollLock.es6.js", 64 | "buildUmd": "BABEL_ENV=umd babel src/bodyScrollLock.js > lib/bodyScrollLock.js && uglifyjs --compress unused,dead_code --mangle reserved=['require','exports'] lib/bodyScrollLock.js > lib/bodyScrollLock.min.js", 65 | "build": "yarn buildModule && yarn buildEs6 && yarn buildUmd", 66 | "lint": "eslint ./src", 67 | "prerelease": "yarn flow && yarn lint && yarn run clean && yarn build", 68 | "flow": "flow", 69 | "prettierAll": "prettier --write '**/*.{md,json,js,html,yml}'", 70 | "precommit": "lint-staged" 71 | }, 72 | "lint-staged": { 73 | "*.js": [ 74 | "prettier --write --single-quote --trailing-comma es5 --print-width 120", 75 | "git add" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/bodyScrollLock.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // Adopted and modified solution from Bohdan Didukh (2017) 3 | // https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi 4 | 5 | export interface BodyScrollOptions { 6 | reserveScrollBarGap?: boolean; 7 | allowTouchMove?: (el: any) => boolean; 8 | } 9 | 10 | interface Lock { 11 | targetElement: any; 12 | options: BodyScrollOptions; 13 | } 14 | 15 | // Older browsers don't support event options, feature detect it. 16 | let hasPassiveEvents = false; 17 | if (typeof window !== 'undefined') { 18 | const passiveTestOptions = { 19 | get passive() { 20 | hasPassiveEvents = true; 21 | return undefined; 22 | }, 23 | }; 24 | window.addEventListener('testPassive', null, passiveTestOptions); 25 | window.removeEventListener('testPassive', null, passiveTestOptions); 26 | } 27 | 28 | const isIosDevice = 29 | typeof window !== 'undefined' && 30 | window.navigator && 31 | window.navigator.platform && 32 | (/iP(ad|hone|od)/.test(window.navigator.platform) || 33 | (window.navigator.platform === 'MacIntel' && window.navigator.maxTouchPoints > 1)); 34 | type HandleScrollEvent = TouchEvent; 35 | 36 | let locks: Array = []; 37 | let documentListenerAdded: boolean = false; 38 | let initialClientY: number = -1; 39 | let previousBodyOverflowSetting; 40 | let previousBodyPosition; 41 | let previousBodyPaddingRight; 42 | 43 | // returns true if `el` should be allowed to receive touchmove events. 44 | const allowTouchMove = (el: EventTarget): boolean => 45 | locks.some(lock => { 46 | if (lock.options.allowTouchMove && lock.options.allowTouchMove(el)) { 47 | return true; 48 | } 49 | 50 | return false; 51 | }); 52 | 53 | const preventDefault = (rawEvent: HandleScrollEvent): boolean => { 54 | const e = rawEvent || window.event; 55 | 56 | // For the case whereby consumers adds a touchmove event listener to document. 57 | // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false }) 58 | // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then 59 | // the touchmove event on document will break. 60 | if (allowTouchMove(e.target)) { 61 | return true; 62 | } 63 | 64 | // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom). 65 | if (e.touches.length > 1) return true; 66 | 67 | if (e.preventDefault) e.preventDefault(); 68 | 69 | return false; 70 | }; 71 | 72 | const setOverflowHidden = (options?: BodyScrollOptions) => { 73 | // If previousBodyPaddingRight is already set, don't set it again. 74 | if (previousBodyPaddingRight === undefined) { 75 | const reserveScrollBarGap = !!options && options.reserveScrollBarGap === true; 76 | const scrollBarGap = window.innerWidth - document.documentElement.clientWidth; 77 | 78 | if (reserveScrollBarGap && scrollBarGap > 0) { 79 | const computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10); 80 | previousBodyPaddingRight = document.body.style.paddingRight; 81 | document.body.style.paddingRight = `${computedBodyPaddingRight + scrollBarGap}px`; 82 | } 83 | } 84 | 85 | // If previousBodyOverflowSetting is already set, don't set it again. 86 | if (previousBodyOverflowSetting === undefined) { 87 | previousBodyOverflowSetting = document.body.style.overflow; 88 | document.body.style.overflow = 'hidden'; 89 | } 90 | }; 91 | 92 | const restoreOverflowSetting = () => { 93 | if (previousBodyPaddingRight !== undefined) { 94 | document.body.style.paddingRight = previousBodyPaddingRight; 95 | 96 | // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it 97 | // can be set again. 98 | previousBodyPaddingRight = undefined; 99 | } 100 | 101 | if (previousBodyOverflowSetting !== undefined) { 102 | document.body.style.overflow = previousBodyOverflowSetting; 103 | 104 | // Restore previousBodyOverflowSetting to undefined 105 | // so setOverflowHidden knows it can be set again. 106 | previousBodyOverflowSetting = undefined; 107 | } 108 | }; 109 | 110 | const setPositionFixed = () => window.requestAnimationFrame(() => { 111 | // If previousBodyPosition is already set, don't set it again. 112 | if (previousBodyPosition === undefined) { 113 | previousBodyPosition = { 114 | position: document.body.style.position, 115 | top: document.body.style.top, 116 | left: document.body.style.left 117 | }; 118 | 119 | // Update the dom inside an animation frame 120 | const { scrollY, scrollX, innerHeight } = window; 121 | document.body.style.position = 'fixed'; 122 | document.body.style.top = `${-scrollY}px`; 123 | document.body.style.left = `${-scrollX}px`; 124 | 125 | setTimeout(() => window.requestAnimationFrame(() => { 126 | // Attempt to check if the bottom bar appeared due to the position change 127 | const bottomBarHeight = innerHeight - window.innerHeight; 128 | if (bottomBarHeight && scrollY >= innerHeight) { 129 | // Move the content further up so that the bottom bar doesn't hide it 130 | document.body.style.top = -(scrollY + bottomBarHeight); 131 | } 132 | }), 300) 133 | } 134 | }); 135 | 136 | const restorePositionSetting = () => { 137 | if (previousBodyPosition !== undefined) { 138 | // Convert the position from "px" to Int 139 | const y = -parseInt(document.body.style.top, 10); 140 | const x = -parseInt(document.body.style.left, 10); 141 | 142 | // Restore styles 143 | document.body.style.position = previousBodyPosition.position; 144 | document.body.style.top = previousBodyPosition.top; 145 | document.body.style.left = previousBodyPosition.left; 146 | 147 | // Restore scroll 148 | window.scrollTo(x, y); 149 | 150 | previousBodyPosition = undefined; 151 | } 152 | }; 153 | 154 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions 155 | const isTargetElementTotallyScrolled = (targetElement: any): boolean => 156 | targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false; 157 | 158 | const handleScroll = (event: HandleScrollEvent, targetElement: any): boolean => { 159 | const clientY = event.targetTouches[0].clientY - initialClientY; 160 | 161 | if (allowTouchMove(event.target)) { 162 | return false; 163 | } 164 | 165 | if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { 166 | // element is at the top of its scroll. 167 | return preventDefault(event); 168 | } 169 | 170 | if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { 171 | // element is at the bottom of its scroll. 172 | return preventDefault(event); 173 | } 174 | 175 | event.stopPropagation(); 176 | return true; 177 | }; 178 | 179 | export const disableBodyScroll = (targetElement: any, options?: BodyScrollOptions): void => { 180 | // targetElement must be provided 181 | if (!targetElement) { 182 | // eslint-disable-next-line no-console 183 | console.error( 184 | 'disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.' 185 | ); 186 | return; 187 | } 188 | 189 | // disableBodyScroll must not have been called on this targetElement before 190 | if (locks.some(lock => lock.targetElement === targetElement)) { 191 | return; 192 | } 193 | 194 | const lock = { 195 | targetElement, 196 | options: options || {}, 197 | }; 198 | 199 | locks = [...locks, lock]; 200 | 201 | if (isIosDevice) { 202 | setPositionFixed(); 203 | } else { 204 | setOverflowHidden(options); 205 | } 206 | 207 | if (isIosDevice) { 208 | targetElement.ontouchstart = (event: HandleScrollEvent) => { 209 | if (event.targetTouches.length === 1) { 210 | // detect single touch. 211 | initialClientY = event.targetTouches[0].clientY; 212 | } 213 | }; 214 | targetElement.ontouchmove = (event: HandleScrollEvent) => { 215 | if (event.targetTouches.length === 1) { 216 | // detect single touch. 217 | handleScroll(event, targetElement); 218 | } 219 | }; 220 | 221 | if (!documentListenerAdded) { 222 | document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 223 | documentListenerAdded = true; 224 | } 225 | } 226 | }; 227 | 228 | export const clearAllBodyScrollLocks = (): void => { 229 | if (isIosDevice) { 230 | // Clear all locks ontouchstart/ontouchmove handlers, and the references. 231 | locks.forEach((lock: Lock) => { 232 | lock.targetElement.ontouchstart = null; 233 | lock.targetElement.ontouchmove = null; 234 | }); 235 | 236 | if (documentListenerAdded) { 237 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 238 | documentListenerAdded = false; 239 | } 240 | 241 | // Reset initial clientY. 242 | initialClientY = -1; 243 | } 244 | 245 | if (isIosDevice) { 246 | restorePositionSetting(); 247 | } else { 248 | restoreOverflowSetting(); 249 | } 250 | 251 | locks = []; 252 | }; 253 | 254 | export const enableBodyScroll = (targetElement: any): void => { 255 | if (!targetElement) { 256 | // eslint-disable-next-line no-console 257 | console.error( 258 | 'enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.' 259 | ); 260 | return; 261 | } 262 | 263 | locks = locks.filter(lock => lock.targetElement !== targetElement); 264 | 265 | if (isIosDevice) { 266 | targetElement.ontouchstart = null; 267 | targetElement.ontouchmove = null; 268 | 269 | if (documentListenerAdded && locks.length === 0) { 270 | document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined); 271 | documentListenerAdded = false; 272 | } 273 | } 274 | 275 | if (isIosDevice) { 276 | restorePositionSetting(); 277 | } else { 278 | restoreOverflowSetting(); 279 | } 280 | }; 281 | --------------------------------------------------------------------------------