Repository: jlmakes/scrollreveal
Branch: master
Commit: b5f4e01a6558
Files: 54
Total size: 152.0 KB
Directory structure:
gitextract_vf2236hi/
├── .eslintrc.json
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── build/
│ ├── rollup.conf.banner.js
│ └── rollup.conf.js
├── dist/
│ ├── scrollreveal.es.js
│ └── scrollreveal.js
├── package.json
├── src/
│ ├── index.js
│ ├── instance/
│ │ ├── constructor.js
│ │ ├── defaults.js
│ │ ├── functions/
│ │ │ ├── animate.js
│ │ │ ├── delegate.js
│ │ │ ├── initialize.js
│ │ │ ├── rinse.js
│ │ │ ├── sequence.js
│ │ │ └── style.js
│ │ ├── methods/
│ │ │ ├── clean.js
│ │ │ ├── destroy.js
│ │ │ ├── reveal.js
│ │ │ └── sync.js
│ │ └── mount.js
│ ├── polyfills/
│ │ └── math-sign.js
│ └── utils/
│ ├── deep-assign.js
│ ├── each.js
│ ├── get-geometry.js
│ ├── get-prefixed-css-prop.js
│ ├── get-scrolled.js
│ ├── is-element-visible.js
│ ├── is-mobile.js
│ ├── is-object.js
│ ├── is-transform-supported.js
│ ├── is-transition-supported.js
│ ├── logger.js
│ └── next-unique-id.js
└── test/
├── instance/
│ └── constructor.spec.js
├── karma.conf.js
├── polyfills/
│ └── math-sign.spec.js
├── sauce.conf.js
├── timeout.spec.js
└── utils/
├── deep-assign.spec.js
├── each.spec.js
├── get-prefixed-css-prop.spec.js
├── is-mobile.spec.js
├── is-object.spec.js
├── is-transform-supported.spec.js
├── is-transition-supported.spec.js
├── logger.spec.js
└── next-unique-id.spec.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc.json
================================================
{
"env": {
"es6": true,
"amd": true,
"browser": true,
"mocha": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module"
},
"globals": {
"describe": true,
"it": true,
"expect": true,
"sinon": true
},
"rules": {
"no-cond-assign": 2,
"no-console": 1,
"no-const-assign": 2,
"no-class-assign": 2,
"no-this-before-super": 2,
"no-unused-vars": 1,
"no-var": 2,
"object-shorthand": [2, "always"]
}
}
================================================
FILE: .gitattributes
================================================
*.js eol=lf
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!--
Thanks for raising an issue! To help us help you, if you've found a bug please consider the following:
* If you can demonstrate the bug using JSBin: https://goo.gl/6b4OeX — please do.
* If that's not possible, perhaps because your bug involves plugins, we recommend creating a small repo that illustrates the problem.
And please, search existing issues before creating a new one.
-->
### Environment
* Operating System:
* Browser Version:
* ScrollReveal Version:
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thank you for creating a pull request. Before submitting, please note the following:
* If your pull request implements a new feature, please raise an issue to discuss it before sending code.
* This message body should clearly illustrate what problems it solves.
* If there are related issues, remember to reference them.
* Ideally, include a test that fails without this PR but passes with it. PRs will only be merged once they pass CI.
-->
================================================
FILE: .gitignore
================================================
.DS_Store
.ignore/
.vscode/
node_modules/
yarn.lock
package-lock.json
================================================
FILE: .travis.yml
================================================
language: node_js
dist: trusty
node_js:
- '9'
addons:
chrome: stable
hosts: localsauce
sudo: required
before_script:
- 'sudo chown root /opt/google/chrome/chrome-sandbox'
- 'sudo chmod 4755 /opt/google/chrome/chrome-sandbox'
after_success:
- npm run coverage
================================================
FILE: CHANGELOG.md
================================================
# Change Log
## [4.0.9] - 2021-03-04
### Fixed
- Styles applied using CSSOM don't drop `:` characters.
## [4.0.8] - 2021-03-02
### Fixed
- Avoid Content Security Policy (CSP) violations. [@lambdacasserole](https://github.com/lambdacasserole) [#431](https://github.com/jlmakes/scrollreveal/pull/431)
## [4.0.7] - 2020-07-15
### Fixed
- Ensure element geometry exists. [#437](https://github.com/jlmakes/scrollreveal/issues/437)
## [4.0.6] - 2020-03-15
### Fixed
- Default transition values of `none` are now correctly ignored. [#231](https://github.com/jlmakes/scrollreveal/issues/231)
### Fixed
## [4.0.5] - 2018-10-20
### Fixed
- Calling `reveal()` on the same `target` breaking animation. [#468](https://github.com/jlmakes/scrollreveal/issues/468)
## [4.0.4] - 2018-09-22
### Fixed
- Malformed `package.json`
## [4.0.3] - 2018-09-21
### Fixed
– `options.cleanup` is now correctly set to `false` by default. [#457](https://github.com/jlmakes/scrollreveal/issues/457)
## [4.0.2] - 2018-09-11
### Fixed
- Null property assignment regression in mount function. [#456](https://github.com/jlmakes/scrollreveal/issues/456)
## [4.0.1] - 2018-09-09
### Fixed
- Noop instances were not correctly unmounting from the DOM. [#455](https://github.com/jlmakes/scrollreveal/issues/455)
- Readme links to pricing page no longer 404.
## [4.0.0] - 2018-08-06
### Added
- ScrollReveal can be enabled/disabled on desktops using `options.desktop`.
- The class `sr` is added to `<html>` during instantiation when supported. [#294](https://github.com/jlmakes/scrollreveal/issues/294)
- `height: 100%` is added to `<body>` during instantiation when supported. [#298](https://github.com/jlmakes/scrollreveal/issues/298)
- Unused containers are removed from the store, and their event listeners destroyed.
- ScrollReveal skips generating opacity styles when `options.opacity` is set to `null`.
- ScrollReveal retains element CSS transformations. [#251](https://github.comjlmakes/scrollreveal/issues/251)
- New `options.cleanup` toggles whether generated styles are removed upon reveal completion (when `options.reset` is `false`). [#292](https://github.comjlmakes/scrollreveal/issues/292)
- ScrollReveal tracks scroll direction as container store data. [#384](https://github.com/jlmakes/scrollreveal/issues/384)
- New `clean()` method removes specific generated styles and event listeners. [#227](https://github.com/jlmakes/scrollreveal/issues/227)
- New `destroy()` method removes all generated styles and event listeners. [#227](https://github.com/jlmakes/scrollreveal/issues/227)
- New `debug` static property toggles error messages in console. [#351](https://github.com/jlmakes/scrollreveal/issues/351)
- Instance methods now accept native arrays of HTML elements.
### Changed
- **Breaking:** The `reveal()` method no longer accepts an `interval` parameter. Instead, sequence intervals are now defined with `options.interval`.
- **Breaking:** The instance method `isSupported()` is now static.
- **Breaking:** `options.distance` supports only `em` `px` and `%` values.
- **Breaking:** ScrollReveal methods are no longer chainable.
- **Breaking:** ScrollReveal requires a commercial license, unless for [GPL-3.0](https://opensource.org/licenses/GPL-3.0) compatible open source projects.
- Elements in a reveal sequence are no longer grouped, and reveal progressively when visible.
- ScrollReveal uses a single `matrix3d()` property, with the correct prefix and only when necessary. [#292](https://github.com/jlmakes/scrollreveal/issues/292)
- ScrollReveal returns a non-operational instance when instantiated in unsupported browsers.
- ScrollReveal `version` is now a read-only instance property.
- ScrollReveal methods are now bound read-only instance properties.
- `options.viewFactor` clamps values outside of `0.0` to `1.0`.
- ScrollReveal constructor now returns a singleton.
### Fixed
- The `requestAnimationFrame` polyfill now reliably throttles callback invocations.
## [3.3.6] - 2017-06-23
### Fixed
- Element visibility now checks left and right boundaries correctly. [#352](https://github.com/jlmakes/scrollreveal/issues/352)
- Library version instance property is again accurate.
## [3.3.5] - 2017-04-05
### Fixed
- Patched to ensure version 3 is the default NPM package.
## [3.3.4] - 2017-02-18
### Fixed
- Update stale CDN link in README.
### Changed
- Add deprecation warnings to README.
## [3.3.3] - 2017-02-18
### Fixed
- Fix error when using Bower and Wordpress due to missing semi-colon. [#278](https://github.com/jlmakes/scrollreveal/issues/278)
## [3.3.2] - 2016-10-02
### Changed
- Updated Starting Defaults section in README. [#273](https://github.comjlmakes/scrollreveal/issues/273)
### Fixed
- Using a selector to define a default container during instantiation now works. [#289](https://github.com/jlmakes/scrollreveal/issues/289)
## [3.3.1] - 2016-07-22
### Fixed
- Instance variable `version` updated with correct library version.
## [3.3.0] - 2016-07-22
### Added
- New callback `beforeReveal(el)`. [#273](https://github.comjlmakes/scrollreveal/issues/273)
- New callback `beforeReset(el)`. [#273](https://github.com/jlmakes/scrollreveal/issues/273)
## [3.2.0] - 2016-07-08
### Added
- New `isNodeList()` method added to `Tools`.
- New `version` instance variable contains library version.
- HTML Collections are now supported as the first argument in `reveal()`. [#246](https://github.com/jlmakes/scrollreveal/issues/246)
- Added fallback for `requestAnimationFrame`. [#267](https://github.comjlmakes/scrollreveal/issues/267)
### Changed
- Updated Starting Defaults section in README.
### Fixed
- Calling `reveal()` multiple times on an element with `config.origin` as `top` or `left` no longer produces invalid CSS. [#270](https://github.com/jlmakes/scrollreveal/issues/270)
- Refactored AMD/CommonJS module wrapper to work with Codekit. [#253](https://github.com/jlmakes/scrollreveal/issues/253)
## [3.1.5] - 2016-07-06
### Fixed
- `sync()` method now properly supports sequences.
## [3.1.4] - 2016-03-28
### Changed
- Added `console.log` calls back to non-minified distribution. [#235](https://github.com/jlmakes/scrollreveal/issues/235)
## [3.1.3] - 2016-03-28
### Removed
- Removed `console.log` calls from distribution. [#235](https://github.comjlmakes/scrollreveal/issues/235)
## [3.1.2] - 2016-03-23
### Fixed
- Removed stray quotation mark in `reveal()` error message.
## [3.1.1] - 2016-03-08
### Fixed
- `config.reset` now works properly with sequences. [#241](https://github.comjlmakes/scrollreveal/issues/241)
## [3.1.0] - 2016-03-07
### Added
- New `isNode()` method added to `Tools`.
- HTML elements are now supported as the first argument in `reveal()`.
- Selector strings assigned to `config.container` are now supported.
- `reveal()` now accepts an `interval` as it's last argument to create sequences. [#86](https://github.com/jlmakes/scrollreveal/issues/86) [#180](https://github.com/jlmakes/scrollreveal/issues/180) [#187](https://github.comjlmakes/scrollreveal/issues/187) [#215](https://github.com/jlmakes/scrollreveal/issues/215) [#234](https://github.com/jlmakes/scrollreveal/issues/234)
- New section on sequenced animations added to README.
### Changed
- Messages logged to console are now prepended with `ScrollReveal:` for clarity.
- Revised and renamed `supported()` method to `isSupported()`.
- Updated Custom Containers section in README with an example using a selector.
- Updated Tips section in README.
### Fixed
- Added semi-colon before global IIFE to improve reliability. [#228](https://github.com/jlmakes/scrollreveal/issues/228)
- The existence of `console.log` is now confirmed for IE9. [#230](https://github.com/jlmakes/scrollreveal/issues/230)
- Typos, indentation and semicolons corrected in README.
## [3.0.9] - 2016-01-14
### Changed
- Updated example site links in the README.
### Fixed
- Fixed operator mismatch inside `supported()`. [#220](https://github.comjlmakes/scrollreveal/issues/220)
## [3.0.8] - 2016-01-13
### Changed
- Public methods now verify that ScrollReveal is supported.
### Fixed
- Updated Tips section in README.
## [3.0.7] - 2016-01-13
### Added
- Added brower support information to README. [#219](https://github.comjlmakes/scrollreveal/issues/219)
### Changed
- `console.log` is now used instead of `console.warn`. [#215](https://github.com/jlmakes/scrollreveal/issues/215)
- Moved `tools.isSupported` method to `ScrollReveal.prototype.supported`.
- Updated the configuration and tips documentation in the README.
### Removed
- The `init()` method was removed.
### Fixed
- Using `config.mobile` in `reveal()` now works. [#216](https://github.comjlmakes/scrollreveal/issues/216)
## [3.0.6] - 2016-01-02
### Fixed
- Custom default containers are now used.
- Critical issues affecting Chrome on iOS were (finally) solved. [#196](https://github.com/jlmakes/scrollreveal/issues/196)
- Revisited `3.0.4` changes to chaining `reveal()` calls. [#212](https://github.com/jlmakes/scrollreveal/issues/212)
## [3.0.5] - 2015-12-30
### Fixed
- Fixed compatibility issues with Webpack. [#209](https://github.comjlmakes/scrollreveal/issues/209)
## [3.0.4] - 2015-12-30
### Fixed
- Squashed Webkit browser bugs due to syntax errors. [#208](https://github.comjlmakes/scrollreveal/issues/208)
- Chaining `reveal()` calls no longer prematurely initialize animation.
- Cleaned up README typos, and stale reference to `config.wait`.
## [3.0.3] - 2015-12-22
### Changed
- `reveal()` and `sync()` now return the ScrollReveal instance even on failure. [#198](https://github.com/jlmakes/scrollreveal/issues/198)
## [3.0.2] - 2015-12-22
### Added
- Added `bower.json` to release package. [#199](https://github.comjlmakes/scrollreveal/issues/199)
### Fixed
- Preexisting CSS transition styles are no longer destroyed. [#197](https://github.com/jlmakes/scrollreveal/issues/197)
## [3.0.1] - 2015-12-21
### Changed
- Updated Getting Started section in the README.
### Fixed
- Hard learned NPM and Bower issues related to release management were endured.
- Issues related to element visibility and animation behavior were addressed. [#193](https://github.com/jlmakes/scrollreveal/issues/193) [#196](https://github.comjlmakes/scrollreveal/issues/196)
## [3.0.0] - 2015-12-15
This version marks a significant change in how developers use ScrollReveal, introducing a JavaScript API to replace the inline attribute parser. It's a big shift, but prioritizes maintainability and flexibility over the novelty of natural language parsing.
### Added
- New method `reveal()`. [#1](https://github.com/jlmakes/scrollreveal/issues/1) [#122](https://github.com/jlmakes/scrollreveal/issues/122)
- New method `sync()`.
- New callback `config.afterReset`.
- Horizontal scrolling is now supported. [#184](https://github.comjlmakes/scrollreveal/issues/184)
### Changed
- **Breaking:** `config.enter` renamed `config.origin`.
- **Breaking:** `config.wait` renamed `config.delay`.
- **Breaking:** `config.delay` renamed `config.useDelay`.
- **Breaking:** `config.over` renamed `config.duration`.
- **Breaking:** `config.move` renamed `config.distance`.
- **Breaking:** `config.viewport` renamed `config.container`.
- **Breaking:** `config.vFactor` renamed `config.viewFactor`.
- **Breaking:** `config.complete` renamed `config.afterReveal`.
- **Breaking:** Time values are now expected in milliseconds (instead of `string`).
- **Breaking:** `config.scale` expects value type `number` (instead of `object`).
- **Breaking:** `config.rotation` axis values require `string` with unit type (instead of `number`).
- **Breaking:** ScrollReveal constructor is now capitalized.
- Reveals now resolve to element's computed opacity, instead of `1`. [#185](https://github.com/jlmakes/scrollreveal/issues/185)
### Removed
- ScrollReveal no longer recognizes `data-sr` attributes.
### Fixed
- Improved reliability of callback timers.
## [2.3.2] - 2015-06-15
### Changed
- Updated `bower.json` syntax. [#150](https://github.com/jlmakes/scrollreveal/issues/150)
## [2.3.1] - 2015-06-04
### Added
- Simple instantiation (without `new` keyword) is now supported. [#148](https://github.com/jlmakes/scrollreveal/issues/148)
## [2.3.0] - 2015-04-25
### Added
- New keyword `vFactor` and alias `vF` control when an element is considered visible.
- New keyword `opacity` controls starting opacity.
### Removed
- The easing keyword `hustle` was removed.
## [2.2.0] - 2015-03-18
### Added
- New keyword `spin` controls yaw.
- New keyword `roll` controls roll.
- New keyword `flip` controls pitch.
### Changed
- Improved Basic Usage examples in README.
## [2.1.0] - 2014-11-25
### Added
- Various tablets added to mobile device detection. [#32](https://github.comjlmakes/scrollreveal/issues/32) [#81](https://github.com/jlmakes/scrollreveal/issues/81)
- CSS Transition support is now confirmed during instantiation. [#109](https://github.com/jlmakes/scrollreveal/issues/109)
## [2.0.5] - 2014-11-23
### Changed
- Reverted `2.0.4` change to element animation logic. [#108](https://github.comjlmakes/scrollreveal/issues/108)
## [2.0.4] - 2014-11-21
### Changed
- Revised how element animations are handled.
- Reverted `2.0.3` change to element visibility logic. [#106](https://github.com/jlmakes/scrollreveal/issues/106)
## [2.0.3] - 2014-11-14
### Added
- `data-sr` attributes are now stripped from initialized elements. [#100](https://github.com/jlmakes/scrollreveal/issues/100) @orapouso.
- Live Reload added to development environment.
### Changed
- Revised how element visibility is determined.
### Removed
- Multiple instances sharing the same viewport element no longer throw an error. [#98](https://github.com/jlmakes/scrollreveal/issues/98) @orapouso.
### Fixed
- Incomplete support for `config.delay = "onload"` was addressed.
- Issues related to `setTimeout`, `config.complete` and incorrect animation timing were addressed. [#96](https://github.com/jlmakes/scrollreveal/issues/96)
## [2.0.2] - 2014-10-23
### Added
- An error is now thrown when multiple instances share the same viewport element. [#91](https://github.com/jlmakes/scrollreveal/issues/91)
### Fixed
- Updated NPM and Bower references with new distribution path.
## [2.0.1] - 2014-10-18
### Fixed
- Incomplete support for `config.viewport` was addressed. [#67](https://github.com/jlmakes/scrollreveal/issues/67) [#68](https://github.comjlmakes/scrollreveal/issues/68)
## [2.0.0] - 2014-10-17
### Added
- New keyword `scale` controls element starting size.
- New option `config.complete` defines a callback for when reveals finish.
- New option `config.viewport` defines custom viewports.
- New option `config.mobile` enables/disables ScrollReveal on mobile devices.
- New option `config.delay` controls when animations are delayed.
### Changed
- **BREAKING:** ScrollReveal now uses the `data-sr` instead of `data-scroll-reveal`.
- Repository now follows [Semantic Versioning](http://semver.org/).
### Removed
- The `after` keyword was removed.
## 0.1.3 - 2014-05-26 [YANKED]
### Added
- Configuration now includes starting opacity. [#33](https://github.comjlmakes/scrollreveal/issues/33) @kierzniak
- New `data-scroll-reveal-id` attribute added to revealed DOM elements.
### Changed
- Scroll event handling now uses `requestAnimationFrame`. [#48](https://github.com/jlmakes/scrollreveal/issues/48) @pazguille
- Generated styles are now stored in an object corresponding to the `data-scroll-reveal-id` attribute on each element. [#38](https://github.com/jlmakes/scrollreveal/pull/38) @georgelee1
## 0.1.2 - 2014-03-13 [YANKED]
### Added
- Elements with `position: fixed` are now supported. [#35](https://github.comjlmakes/scrollreveal/issues/35)
### Fixed
- Generated styles are now more specific. [#37](https://github.comjlmakes/scrollreveal/issues/37)
## 0.1.1 - 2014-03-06 [YANKED]
### Fixed
- Squashed bug with `enter top` and `enter left`. [#13](https://github.comjlmakes/scrollreveal/issues/13) [#31](https://github.com/jlmakes/scrollreveal/issues/31) @sherban @danycerone
## 0.1.0 - 2014-03-05 [YANKED]
### Added
- Distribution now supports AMD/CommonJS.
- Repository now uses Gulp.
- Boilerplate Testline suite added to repository.
### Changed
- **BREAKING:** ScrollReveal now uses the `data-scroll-reveal` attribute to parse animation instructions, in place of `data-scrollReveal`.
## 0.0.4 - 2014-02-28 [YANKED]
### Fixed
- ScrollReveal no longer destroys the existing style attribute on revealed elements, but instead, now appends the necessary animation styles to existing inline styles.
## 0.0.3 - 2014-02-22 [YANKED]
### Fixed
- Removed unused CSS Transition/Transform prefixes for Mozilla and Opera.
## 0.0.2 - 2014-02-13 [YANKED]
### Added
- Constructor now accepts a configuration object to customize defaults.
- New `reset` keyword allows elements to reveal each time they enter the viewport.
- The `move` keyword can now be replaced with with CSS easing keywords (e.g. `ease-in-out`).
- Library documentation and code examples added to README.
### Changed
- ScrollReveal is no longer automatically instantiated by the `DOMContentLoaded` event.
## 0.0.1 - 2014-01-22 [YANKED]
### Hello World
[4.0.9]: https://github.com/jlmakes/scrollreveal/compare/v4.0.8...v4.0.9
[4.0.8]: https://github.com/jlmakes/scrollreveal/compare/v4.0.7...v4.0.8
[4.0.7]: https://github.com/jlmakes/scrollreveal/compare/v4.0.6...v4.0.7
[4.0.6]: https://github.com/jlmakes/scrollreveal/compare/v4.0.5...v4.0.6
[4.0.5]: https://github.com/jlmakes/scrollreveal/compare/v4.0.4...v4.0.5
[4.0.4]: https://github.com/jlmakes/scrollreveal/compare/v4.0.3...v4.0.4
[4.0.3]: https://github.com/jlmakes/scrollreveal/compare/v4.0.2...v4.0.3
[4.0.2]: https://github.com/jlmakes/scrollreveal/compare/v4.0.1...v4.0.2
[4.0.1]: https://github.com/jlmakes/scrollreveal/compare/v4.0.0...v4.0.1
[4.0.0]: https://github.com/jlmakes/scrollreveal/compare/v3.3.6...v4.0.0
[3.3.6]: https://github.com/jlmakes/scrollreveal/compare/v3.3.5...v3.3.6
[3.3.5]: https://github.com/jlmakes/scrollreveal/compare/v3.3.4...v3.3.5
[3.3.4]: https://github.com/jlmakes/scrollreveal/compare/v3.3.3...v3.3.4
[3.3.3]: https://github.com/jlmakes/scrollreveal/compare/v3.2.2...v3.3.3
[3.3.2]: https://github.com/jlmakes/scrollreveal/compare/v3.3.1...v3.3.2
[3.3.1]: https://github.com/jlmakes/scrollreveal/compare/v3.3.0...v3.3.1
[3.3.0]: https://github.com/jlmakes/scrollreveal/compare/v3.2.0...v3.3.0
[3.2.0]: https://github.com/jlmakes/scrollreveal/compare/v3.1.5...v3.2.0
[3.1.5]: https://github.com/jlmakes/scrollreveal/compare/v3.1.4...v3.1.5
[3.1.4]: https://github.com/jlmakes/scrollreveal/compare/v3.1.3...v3.1.4
[3.1.3]: https://github.com/jlmakes/scrollreveal/compare/v3.1.2...v3.1.3
[3.1.2]: https://github.com/jlmakes/scrollreveal/compare/v3.1.1...v3.1.2
[3.1.1]: https://github.com/jlmakes/scrollreveal/compare/v3.1.0...v3.1.1
[3.1.0]: https://github.com/jlmakes/scrollreveal/compare/v3.0.9...v3.1.0
[3.0.9]: https://github.com/jlmakes/scrollreveal/compare/v3.0.8...v3.0.9
[3.0.8]: https://github.com/jlmakes/scrollreveal/compare/v3.0.7...v3.0.8
[3.0.7]: https://github.com/jlmakes/scrollreveal/compare/v3.0.6...v3.0.7
[3.0.6]: https://github.com/jlmakes/scrollreveal/compare/v3.0.5...v3.0.6
[3.0.5]: https://github.com/jlmakes/scrollreveal/compare/v3.0.4...v3.0.5
[3.0.4]: https://github.com/jlmakes/scrollreveal/compare/v3.0.3...v3.0.4
[3.0.3]: https://github.com/jlmakes/scrollreveal/compare/v3.0.2...v3.0.3
[3.0.2]: https://github.com/jlmakes/scrollreveal/compare/v3.0.1...v3.0.2
[3.0.1]: https://github.com/jlmakes/scrollreveal/compare/v3.0.0...v3.0.1
[3.0.0]: https://github.com/jlmakes/scrollreveal/compare/v2.3.2...v3.0.0
[2.3.2]: https://github.com/jlmakes/scrollreveal/compare/v2.3.1...v2.3.2
[2.3.1]: https://github.com/jlmakes/scrollreveal/compare/v2.3.0...v2.3.1
[2.3.0]: https://github.com/jlmakes/scrollreveal/compare/v2.2.0...v2.3.0
[2.2.0]: https://github.com/jlmakes/scrollreveal/compare/v2.1.0...v2.2.0
[2.1.0]: https://github.com/jlmakes/scrollreveal/compare/v2.0.5...v2.1.0
[2.0.5]: https://github.com/jlmakes/scrollreveal/compare/v2.0.4...v2.0.5
[2.0.4]: https://github.com/jlmakes/scrollreveal/compare/v2.0.3...v2.0.4
[2.0.3]: https://github.com/jlmakes/scrollreveal/compare/v2.0.2...v2.0.3
[2.0.2]: https://github.com/jlmakes/scrollreveal/compare/v2.0.1...v2.0.2
[2.0.1]: https://github.com/jlmakes/scrollreveal/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/jlmakes/scrollreveal/tree/v2.0.0
================================================
FILE: README.md
================================================
<p align="center">
<a href="https://scrollrevealjs.org" title="Visit ScrollReveal home page">
<img src="https://scrollrevealjs.org/img/logomark.svg" alt="ScrollReveal" width="120">
</a>
</p>
<br>
<p align="center">
<a href="https://scrollrevealjs.org" title="Visit ScrollReveal home page">
<img width="200" src="https://scrollrevealjs.org/img/scrollreveal-logotype-dark.svg" alt="ScrollReveal">
</a>
</p>
<p align="center">Animate elements as they scroll into view.</p>
<p align="center">
<a href="https://travis-ci.org/jlmakes/scrollreveal">
<img src="https://img.shields.io/travis/jlmakes/scrollreveal.svg" alt="Build status">
</a>
<a href="https://www.npmjs.com/package/scrollreveal">
<img src="https://img.shields.io/npm/dm/scrollreveal.svg" alt="Monthly downloads">
</a>
<a href="https://www.npmjs.com/package/scrollreveal">
<img src="https://img.shields.io/npm/v/scrollreveal.svg" alt="Version">
</a>
<img src="https://img.shields.io/badge/min+gzip-5.7_kB-blue.svg" alt="5.7 kB min+gzip">
<a href="https://opensource.org/licenses/GPL-3.0">
<img src="https://img.shields.io/badge/license-GPLv3-blue.svg" alt="GPLv3 License">
</a>
</p>
<br>
# Installation
## Browser
A simple and fast way to get started is to include this script on your page:
```html
<script src="https://unpkg.com/scrollreveal"></script>
```
This will create the global variable `ScrollReveal`
> Be careful using this method in production. Without specifying a fixed version number, Unpkg may delay your page load while it resolves the latest version. Learn more at [unpkg.com](https://unpkg.com)
## Module
```bash
$ npm install scrollreveal
```
#### CommonJS
```js
const ScrollReveal = require('scrollreveal')
```
#### ES2015
```js
import ScrollReveal from 'scrollreveal'
```
<br>
# Usage
Installation provides us with the constructor function [`ScrollReveal()`](https://scrollrevealjs.org/api/constructor.html). Calling this function returns the ScrollReveal instance, the “brain” behind the magic.
> ScrollReveal employs the singleton pattern; no matter how many times the constructor is called, it will always return the same instance. This means we can call it anywhere, worry-free.
There’s a lot we can do with this instance, but most of the time we’ll be using the [`reveal()`](https://scrollrevealjs.org/api/reveal.html) method to create animation. Fundamentally, this is how to use ScrollReveal:
```html
<h1 class="headline">
Widget Inc.
</h1>
```
```js
ScrollReveal().reveal('.headline')
```
**🔎 See this demo live on [JSBin](http://jsbin.com/jufohaxonu/edit?html,output)**
<br>
---
### The full documentation can be found at [https://scrollrevealjs.org](https://scrollrevealjs.org)
> If you’re using an older version of ScrollReveal, you can find legacy documentation in the [wiki](https://github.com/jlmakes/scrollreveal/wiki)
---
<br>
<a href="https://scrollrevealjs.org/pricing/" title="Visit ScrollReveal pricing page">
<img align="right" height="300" src="https://scrollrevealjs.org/img/license.svg" alt="Commercial License Badge">
</a>
<br>
# License
**For commercial sites, themes, projects, and applications, keep your source code private/proprietary by purchasing a [Commercial License](https://scrollrevealjs.org/pricing/).**
Licensed under the GNU General Public License 3.0 for compatible open source projects and non-commercial use.
<br>
Copyright 2023 Fisssion LLC
================================================
FILE: build/rollup.conf.banner.js
================================================
const { version } = require('../package.json')
const banner = `/*! @license ScrollReveal v${version}
Copyright 2021 Fisssion LLC.
Licensed under the GNU General Public License 3.0 for
compatible open source projects and non-commercial use.
For commercial sites, themes, projects, and applications,
keep your source code private/proprietary by purchasing
a commercial license from https://scrollrevealjs.org/
*/`
export default banner
================================================
FILE: build/rollup.conf.js
================================================
import buble from 'rollup-plugin-buble'
import json from 'rollup-plugin-json'
import pkg from '../package.json'
import nodeResolve from 'rollup-plugin-node-resolve'
import banner from './rollup.conf.banner'
const base = {
input: './src/index.js',
plugins: [json(), nodeResolve(), buble()]
}
const es = Object.assign({}, base, {
external: [...Object.keys(pkg.dependencies || {})],
output: { banner, format: 'es', file: './dist/scrollreveal.es.js' }
})
const umd = Object.assign({}, base, {
output: {
banner,
format: 'umd',
file: './dist/scrollreveal.js',
name: 'ScrollReveal'
}
})
export default [es, umd]
================================================
FILE: dist/scrollreveal.es.js
================================================
/*! @license ScrollReveal v4.0.9
Copyright 2021 Fisssion LLC.
Licensed under the GNU General Public License 3.0 for
compatible open source projects and non-commercial use.
For commercial sites, themes, projects, and applications,
keep your source code private/proprietary by purchasing
a commercial license from https://scrollrevealjs.org/
*/
import $ from 'tealight';
import { translateY, translateX, rotateX, rotateY, rotateZ, scale, parse, multiply } from 'rematrix';
import raf from 'miniraf';
var defaults = {
delay: 0,
distance: '0',
duration: 600,
easing: 'cubic-bezier(0.5, 0, 0, 1)',
interval: 0,
opacity: 0,
origin: 'bottom',
rotate: {
x: 0,
y: 0,
z: 0
},
scale: 1,
cleanup: false,
container: document.documentElement,
desktop: true,
mobile: true,
reset: false,
useDelay: 'always',
viewFactor: 0.0,
viewOffset: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
afterReset: function afterReset() {},
afterReveal: function afterReveal() {},
beforeReset: function beforeReset() {},
beforeReveal: function beforeReveal() {}
};
function failure() {
document.documentElement.classList.remove('sr');
return {
clean: function clean() {},
destroy: function destroy() {},
reveal: function reveal() {},
sync: function sync() {},
get noop() {
return true
}
}
}
function success() {
document.documentElement.classList.add('sr');
if (document.body) {
document.body.style.height = '100%';
} else {
document.addEventListener('DOMContentLoaded', function () {
document.body.style.height = '100%';
});
}
}
var mount = { success: success, failure: failure };
function isObject(x) {
return (
x !== null &&
x instanceof Object &&
(x.constructor === Object ||
Object.prototype.toString.call(x) === '[object Object]')
)
}
function each(collection, callback) {
if (isObject(collection)) {
var keys = Object.keys(collection);
return keys.forEach(function (key) { return callback(collection[key], key, collection); })
}
if (collection instanceof Array) {
return collection.forEach(function (item, i) { return callback(item, i, collection); })
}
throw new TypeError('Expected either an array or object literal.')
}
function logger(message) {
var details = [], len = arguments.length - 1;
while ( len-- > 0 ) details[ len ] = arguments[ len + 1 ];
if (this.constructor.debug && console) {
var report = "%cScrollReveal: " + message;
details.forEach(function (detail) { return (report += "\n — " + detail); });
console.log(report, 'color: #ea654b;'); // eslint-disable-line no-console
}
}
function rinse() {
var this$1 = this;
var struct = function () { return ({
active: [],
stale: []
}); };
var elementIds = struct();
var sequenceIds = struct();
var containerIds = struct();
/**
* Take stock of active element IDs.
*/
try {
each($('[data-sr-id]'), function (node) {
var id = parseInt(node.getAttribute('data-sr-id'));
elementIds.active.push(id);
});
} catch (e) {
throw e
}
/**
* Destroy stale elements.
*/
each(this.store.elements, function (element) {
if (elementIds.active.indexOf(element.id) === -1) {
elementIds.stale.push(element.id);
}
});
each(elementIds.stale, function (staleId) { return delete this$1.store.elements[staleId]; });
/**
* Take stock of active container and sequence IDs.
*/
each(this.store.elements, function (element) {
if (containerIds.active.indexOf(element.containerId) === -1) {
containerIds.active.push(element.containerId);
}
if (element.hasOwnProperty('sequence')) {
if (sequenceIds.active.indexOf(element.sequence.id) === -1) {
sequenceIds.active.push(element.sequence.id);
}
}
});
/**
* Destroy stale containers.
*/
each(this.store.containers, function (container) {
if (containerIds.active.indexOf(container.id) === -1) {
containerIds.stale.push(container.id);
}
});
each(containerIds.stale, function (staleId) {
var stale = this$1.store.containers[staleId].node;
stale.removeEventListener('scroll', this$1.delegate);
stale.removeEventListener('resize', this$1.delegate);
delete this$1.store.containers[staleId];
});
/**
* Destroy stale sequences.
*/
each(this.store.sequences, function (sequence) {
if (sequenceIds.active.indexOf(sequence.id) === -1) {
sequenceIds.stale.push(sequence.id);
}
});
each(sequenceIds.stale, function (staleId) { return delete this$1.store.sequences[staleId]; });
}
var getPrefixedCssProp = (function () {
var properties = {};
var style = document.documentElement.style;
function getPrefixedCssProperty(name, source) {
if ( source === void 0 ) source = style;
if (name && typeof name === 'string') {
if (properties[name]) {
return properties[name]
}
if (typeof source[name] === 'string') {
return (properties[name] = name)
}
if (typeof source[("-webkit-" + name)] === 'string') {
return (properties[name] = "-webkit-" + name)
}
throw new RangeError(("Unable to find \"" + name + "\" style property."))
}
throw new TypeError('Expected a string.')
}
getPrefixedCssProperty.clearCache = function () { return (properties = {}); };
return getPrefixedCssProperty
})();
function style(element) {
var computed = window.getComputedStyle(element.node);
var position = computed.position;
var config = element.config;
/**
* Generate inline styles
*/
var inline = {};
var inlineStyle = element.node.getAttribute('style') || '';
var inlineMatch = inlineStyle.match(/[\w-]+\s*:\s*[^;]+\s*/gi) || [];
inline.computed = inlineMatch ? inlineMatch.map(function (m) { return m.trim(); }).join('; ') + ';' : '';
inline.generated = inlineMatch.some(function (m) { return m.match(/visibility\s?:\s?visible/i); })
? inline.computed
: inlineMatch.concat( ['visibility: visible']).map(function (m) { return m.trim(); }).join('; ') + ';';
/**
* Generate opacity styles
*/
var computedOpacity = parseFloat(computed.opacity);
var configOpacity = !isNaN(parseFloat(config.opacity))
? parseFloat(config.opacity)
: parseFloat(computed.opacity);
var opacity = {
computed: computedOpacity !== configOpacity ? ("opacity: " + computedOpacity + ";") : '',
generated: computedOpacity !== configOpacity ? ("opacity: " + configOpacity + ";") : ''
};
/**
* Generate transformation styles
*/
var transformations = [];
if (parseFloat(config.distance)) {
var axis = config.origin === 'top' || config.origin === 'bottom' ? 'Y' : 'X';
/**
* Let’s make sure our our pixel distances are negative for top and left.
* e.g. { origin: 'top', distance: '25px' } starts at `top: -25px` in CSS.
*/
var distance = config.distance;
if (config.origin === 'top' || config.origin === 'left') {
distance = /^-/.test(distance) ? distance.substr(1) : ("-" + distance);
}
var ref = distance.match(/(^-?\d+\.?\d?)|(em$|px$|%$)/g);
var value = ref[0];
var unit = ref[1];
switch (unit) {
case 'em':
distance = parseInt(computed.fontSize) * value;
break
case 'px':
distance = value;
break
case '%':
/**
* Here we use `getBoundingClientRect` instead of
* the existing data attached to `element.geometry`
* because only the former includes any transformations
* current applied to the element.
*
* If that behavior ends up being unintuitive, this
* logic could instead utilize `element.geometry.height`
* and `element.geoemetry.width` for the distance calculation
*/
distance =
axis === 'Y'
? (element.node.getBoundingClientRect().height * value) / 100
: (element.node.getBoundingClientRect().width * value) / 100;
break
default:
throw new RangeError('Unrecognized or missing distance unit.')
}
if (axis === 'Y') {
transformations.push(translateY(distance));
} else {
transformations.push(translateX(distance));
}
}
if (config.rotate.x) { transformations.push(rotateX(config.rotate.x)); }
if (config.rotate.y) { transformations.push(rotateY(config.rotate.y)); }
if (config.rotate.z) { transformations.push(rotateZ(config.rotate.z)); }
if (config.scale !== 1) {
if (config.scale === 0) {
/**
* The CSS Transforms matrix interpolation specification
* basically disallows transitions of non-invertible
* matrixes, which means browsers won't transition
* elements with zero scale.
*
* That’s inconvenient for the API and developer
* experience, so we simply nudge their value
* slightly above zero; this allows browsers
* to transition our element as expected.
*
* `0.0002` was the smallest number
* that performed across browsers.
*/
transformations.push(scale(0.0002));
} else {
transformations.push(scale(config.scale));
}
}
var transform = {};
if (transformations.length) {
transform.property = getPrefixedCssProp('transform');
/**
* The default computed transform value should be one of:
* undefined || 'none' || 'matrix()' || 'matrix3d()'
*/
transform.computed = {
raw: computed[transform.property],
matrix: parse(computed[transform.property])
};
transformations.unshift(transform.computed.matrix);
var product = transformations.reduce(multiply);
transform.generated = {
initial: ((transform.property) + ": matrix3d(" + (product.join(', ')) + ");"),
final: ((transform.property) + ": matrix3d(" + (transform.computed.matrix.join(', ')) + ");")
};
} else {
transform.generated = {
initial: '',
final: ''
};
}
/**
* Generate transition styles
*/
var transition = {};
if (opacity.generated || transform.generated.initial) {
transition.property = getPrefixedCssProp('transition');
transition.computed = computed[transition.property];
transition.fragments = [];
var delay = config.delay;
var duration = config.duration;
var easing = config.easing;
if (opacity.generated) {
transition.fragments.push({
delayed: ("opacity " + (duration / 1000) + "s " + easing + " " + (delay / 1000) + "s"),
instant: ("opacity " + (duration / 1000) + "s " + easing + " 0s")
});
}
if (transform.generated.initial) {
transition.fragments.push({
delayed: ((transform.property) + " " + (duration / 1000) + "s " + easing + " " + (delay / 1000) + "s"),
instant: ((transform.property) + " " + (duration / 1000) + "s " + easing + " 0s")
});
}
/**
* The default computed transition property should be undefined, or one of:
* '' || 'none 0s ease 0s' || 'all 0s ease 0s' || 'all 0s 0s cubic-bezier()'
*/
var hasCustomTransition =
transition.computed && !transition.computed.match(/all 0s|none 0s/);
if (hasCustomTransition) {
transition.fragments.unshift({
delayed: transition.computed,
instant: transition.computed
});
}
var composed = transition.fragments.reduce(
function (composition, fragment, i) {
composition.delayed += i === 0 ? fragment.delayed : (", " + (fragment.delayed));
composition.instant += i === 0 ? fragment.instant : (", " + (fragment.instant));
return composition
},
{
delayed: '',
instant: ''
}
);
transition.generated = {
delayed: ((transition.property) + ": " + (composed.delayed) + ";"),
instant: ((transition.property) + ": " + (composed.instant) + ";")
};
} else {
transition.generated = {
delayed: '',
instant: ''
};
}
return {
inline: inline,
opacity: opacity,
position: position,
transform: transform,
transition: transition
}
}
/**
* apply a CSS string to an element using the CSSOM (element.style) rather
* than setAttribute, which may violate the content security policy.
*
* @param {Node} [el] Element to receive styles.
* @param {string} [declaration] Styles to apply.
*/
function applyStyle (el, declaration) {
declaration.split(';').forEach(function (pair) {
var ref = pair.split(':');
var property = ref[0];
var value = ref.slice(1);
if (property && value) {
el.style[property.trim()] = value.join(':');
}
});
}
function clean(target) {
var this$1 = this;
var dirty;
try {
each($(target), function (node) {
var id = node.getAttribute('data-sr-id');
if (id !== null) {
dirty = true;
var element = this$1.store.elements[id];
if (element.callbackTimer) {
window.clearTimeout(element.callbackTimer.clock);
}
applyStyle(element.node, element.styles.inline.generated);
node.removeAttribute('data-sr-id');
delete this$1.store.elements[id];
}
});
} catch (e) {
return logger.call(this, 'Clean failed.', e.message)
}
if (dirty) {
try {
rinse.call(this);
} catch (e) {
return logger.call(this, 'Clean failed.', e.message)
}
}
}
function destroy() {
var this$1 = this;
/**
* Remove all generated styles and element ids
*/
each(this.store.elements, function (element) {
applyStyle(element.node, element.styles.inline.generated);
element.node.removeAttribute('data-sr-id');
});
/**
* Remove all event listeners.
*/
each(this.store.containers, function (container) {
var target =
container.node === document.documentElement ? window : container.node;
target.removeEventListener('scroll', this$1.delegate);
target.removeEventListener('resize', this$1.delegate);
});
/**
* Clear all data from the store
*/
this.store = {
containers: {},
elements: {},
history: [],
sequences: {}
};
}
function deepAssign(target) {
var sources = [], len = arguments.length - 1;
while ( len-- > 0 ) sources[ len ] = arguments[ len + 1 ];
if (isObject(target)) {
each(sources, function (source) {
each(source, function (data, key) {
if (isObject(data)) {
if (!target[key] || !isObject(target[key])) {
target[key] = {};
}
deepAssign(target[key], data);
} else {
target[key] = data;
}
});
});
return target
} else {
throw new TypeError('Target must be an object literal.')
}
}
function isMobile(agent) {
if ( agent === void 0 ) agent = navigator.userAgent;
return /Android|iPhone|iPad|iPod/i.test(agent)
}
var nextUniqueId = (function () {
var uid = 0;
return function () { return uid++; }
})();
function initialize() {
var this$1 = this;
rinse.call(this);
each(this.store.elements, function (element) {
var styles = [element.styles.inline.generated];
if (element.visible) {
styles.push(element.styles.opacity.computed);
styles.push(element.styles.transform.generated.final);
element.revealed = true;
} else {
styles.push(element.styles.opacity.generated);
styles.push(element.styles.transform.generated.initial);
element.revealed = false;
}
applyStyle(element.node, styles.filter(function (s) { return s !== ''; }).join(' '));
});
each(this.store.containers, function (container) {
var target =
container.node === document.documentElement ? window : container.node;
target.addEventListener('scroll', this$1.delegate);
target.addEventListener('resize', this$1.delegate);
});
/**
* Manually invoke delegate once to capture
* element and container dimensions, container
* scroll position, and trigger any valid reveals
*/
this.delegate();
/**
* Wipe any existing `setTimeout` now
* that initialization has completed.
*/
this.initTimeout = null;
}
function animate(element, force) {
if ( force === void 0 ) force = {};
var pristine = force.pristine || this.pristine;
var delayed =
element.config.useDelay === 'always' ||
(element.config.useDelay === 'onload' && pristine) ||
(element.config.useDelay === 'once' && !element.seen);
var shouldReveal = element.visible && !element.revealed;
var shouldReset = !element.visible && element.revealed && element.config.reset;
if (force.reveal || shouldReveal) {
return triggerReveal.call(this, element, delayed)
}
if (force.reset || shouldReset) {
return triggerReset.call(this, element)
}
}
function triggerReveal(element, delayed) {
var styles = [
element.styles.inline.generated,
element.styles.opacity.computed,
element.styles.transform.generated.final
];
if (delayed) {
styles.push(element.styles.transition.generated.delayed);
} else {
styles.push(element.styles.transition.generated.instant);
}
element.revealed = element.seen = true;
applyStyle(element.node, styles.filter(function (s) { return s !== ''; }).join(' '));
registerCallbacks.call(this, element, delayed);
}
function triggerReset(element) {
var styles = [
element.styles.inline.generated,
element.styles.opacity.generated,
element.styles.transform.generated.initial,
element.styles.transition.generated.instant
];
element.revealed = false;
applyStyle(element.node, styles.filter(function (s) { return s !== ''; }).join(' '));
registerCallbacks.call(this, element);
}
function registerCallbacks(element, isDelayed) {
var this$1 = this;
var duration = isDelayed
? element.config.duration + element.config.delay
: element.config.duration;
var beforeCallback = element.revealed
? element.config.beforeReveal
: element.config.beforeReset;
var afterCallback = element.revealed
? element.config.afterReveal
: element.config.afterReset;
var elapsed = 0;
if (element.callbackTimer) {
elapsed = Date.now() - element.callbackTimer.start;
window.clearTimeout(element.callbackTimer.clock);
}
beforeCallback(element.node);
element.callbackTimer = {
start: Date.now(),
clock: window.setTimeout(function () {
afterCallback(element.node);
element.callbackTimer = null;
if (element.revealed && !element.config.reset && element.config.cleanup) {
clean.call(this$1, element.node);
}
}, duration - elapsed)
};
}
function sequence(element, pristine) {
if ( pristine === void 0 ) pristine = this.pristine;
/**
* We first check if the element should reset.
*/
if (!element.visible && element.revealed && element.config.reset) {
return animate.call(this, element, { reset: true })
}
var seq = this.store.sequences[element.sequence.id];
var i = element.sequence.index;
if (seq) {
var visible = new SequenceModel(seq, 'visible', this.store);
var revealed = new SequenceModel(seq, 'revealed', this.store);
seq.models = { visible: visible, revealed: revealed };
/**
* If the sequence has no revealed members,
* then we reveal the first visible element
* within that sequence.
*
* The sequence then cues a recursive call
* in both directions.
*/
if (!revealed.body.length) {
var nextId = seq.members[visible.body[0]];
var nextElement = this.store.elements[nextId];
if (nextElement) {
cue.call(this, seq, visible.body[0], -1, pristine);
cue.call(this, seq, visible.body[0], +1, pristine);
return animate.call(this, nextElement, { reveal: true, pristine: pristine })
}
}
/**
* If our element isn’t resetting, we check the
* element sequence index against the head, and
* then the foot of the sequence.
*/
if (
!seq.blocked.head &&
i === [].concat( revealed.head ).pop() &&
i >= [].concat( visible.body ).shift()
) {
cue.call(this, seq, i, -1, pristine);
return animate.call(this, element, { reveal: true, pristine: pristine })
}
if (
!seq.blocked.foot &&
i === [].concat( revealed.foot ).shift() &&
i <= [].concat( visible.body ).pop()
) {
cue.call(this, seq, i, +1, pristine);
return animate.call(this, element, { reveal: true, pristine: pristine })
}
}
}
function Sequence(interval) {
var i = Math.abs(interval);
if (!isNaN(i)) {
this.id = nextUniqueId();
this.interval = Math.max(i, 16);
this.members = [];
this.models = {};
this.blocked = {
head: false,
foot: false
};
} else {
throw new RangeError('Invalid sequence interval.')
}
}
function SequenceModel(seq, prop, store) {
var this$1 = this;
this.head = [];
this.body = [];
this.foot = [];
each(seq.members, function (id, index) {
var element = store.elements[id];
if (element && element[prop]) {
this$1.body.push(index);
}
});
if (this.body.length) {
each(seq.members, function (id, index) {
var element = store.elements[id];
if (element && !element[prop]) {
if (index < this$1.body[0]) {
this$1.head.push(index);
} else {
this$1.foot.push(index);
}
}
});
}
}
function cue(seq, i, direction, pristine) {
var this$1 = this;
var blocked = ['head', null, 'foot'][1 + direction];
var nextId = seq.members[i + direction];
var nextElement = this.store.elements[nextId];
seq.blocked[blocked] = true;
setTimeout(function () {
seq.blocked[blocked] = false;
if (nextElement) {
sequence.call(this$1, nextElement, pristine);
}
}, seq.interval);
}
function reveal(target, options, syncing) {
var this$1 = this;
if ( options === void 0 ) options = {};
if ( syncing === void 0 ) syncing = false;
var containerBuffer = [];
var sequence$$1;
var interval = options.interval || defaults.interval;
try {
if (interval) {
sequence$$1 = new Sequence(interval);
}
var nodes = $(target);
if (!nodes.length) {
throw new Error('Invalid reveal target.')
}
var elements = nodes.reduce(function (elementBuffer, elementNode) {
var element = {};
var existingId = elementNode.getAttribute('data-sr-id');
if (existingId) {
deepAssign(element, this$1.store.elements[existingId]);
/**
* In order to prevent previously generated styles
* from throwing off the new styles, the style tag
* has to be reverted to its pre-reveal state.
*/
applyStyle(element.node, element.styles.inline.computed);
} else {
element.id = nextUniqueId();
element.node = elementNode;
element.seen = false;
element.revealed = false;
element.visible = false;
}
var config = deepAssign({}, element.config || this$1.defaults, options);
if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
if (existingId) {
clean.call(this$1, element);
}
return elementBuffer // skip elements that are disabled
}
var containerNode = $(config.container)[0];
if (!containerNode) {
throw new Error('Invalid container.')
}
if (!containerNode.contains(elementNode)) {
return elementBuffer // skip elements found outside the container
}
var containerId;
{
containerId = getContainerId(
containerNode,
containerBuffer,
this$1.store.containers
);
if (containerId === null) {
containerId = nextUniqueId();
containerBuffer.push({ id: containerId, node: containerNode });
}
}
element.config = config;
element.containerId = containerId;
element.styles = style(element);
if (sequence$$1) {
element.sequence = {
id: sequence$$1.id,
index: sequence$$1.members.length
};
sequence$$1.members.push(element.id);
}
elementBuffer.push(element);
return elementBuffer
}, []);
/**
* Modifying the DOM via setAttribute needs to be handled
* separately from reading computed styles in the map above
* for the browser to batch DOM changes (limiting reflows)
*/
each(elements, function (element) {
this$1.store.elements[element.id] = element;
element.node.setAttribute('data-sr-id', element.id);
});
} catch (e) {
return logger.call(this, 'Reveal failed.', e.message)
}
/**
* Now that element set-up is complete...
* Let’s commit any container and sequence data we have to the store.
*/
each(containerBuffer, function (container) {
this$1.store.containers[container.id] = {
id: container.id,
node: container.node
};
});
if (sequence$$1) {
this.store.sequences[sequence$$1.id] = sequence$$1;
}
/**
* If reveal wasn't invoked by sync, we want to
* make sure to add this call to the history.
*/
if (syncing !== true) {
this.store.history.push({ target: target, options: options });
/**
* Push initialization to the event queue, giving
* multiple reveal calls time to be interpreted.
*/
if (this.initTimeout) {
window.clearTimeout(this.initTimeout);
}
this.initTimeout = window.setTimeout(initialize.bind(this), 0);
}
}
function getContainerId(node) {
var collections = [], len = arguments.length - 1;
while ( len-- > 0 ) collections[ len ] = arguments[ len + 1 ];
var id = null;
each(collections, function (collection) {
each(collection, function (container) {
if (id === null && container.node === node) {
id = container.id;
}
});
});
return id
}
/**
* Re-runs the reveal method for each record stored in history,
* for capturing new content asynchronously loaded into the DOM.
*/
function sync() {
var this$1 = this;
each(this.store.history, function (record) {
reveal.call(this$1, record.target, record.options, true);
});
initialize.call(this);
}
var polyfill = function (x) { return (x > 0) - (x < 0) || +x; };
var mathSign = Math.sign || polyfill;
function getGeometry(target, isContainer) {
/**
* We want to ignore padding and scrollbars for container elements.
* More information here: https://goo.gl/vOZpbz
*/
var height = isContainer ? target.node.clientHeight : target.node.offsetHeight;
var width = isContainer ? target.node.clientWidth : target.node.offsetWidth;
var offsetTop = 0;
var offsetLeft = 0;
var node = target.node;
do {
if (!isNaN(node.offsetTop)) {
offsetTop += node.offsetTop;
}
if (!isNaN(node.offsetLeft)) {
offsetLeft += node.offsetLeft;
}
node = node.offsetParent;
} while (node)
return {
bounds: {
top: offsetTop,
right: offsetLeft + width,
bottom: offsetTop + height,
left: offsetLeft
},
height: height,
width: width
}
}
function getScrolled(container) {
var top, left;
if (container.node === document.documentElement) {
top = window.pageYOffset;
left = window.pageXOffset;
} else {
top = container.node.scrollTop;
left = container.node.scrollLeft;
}
return { top: top, left: left }
}
function isElementVisible(element) {
if ( element === void 0 ) element = {};
var container = this.store.containers[element.containerId];
if (!container) { return }
var viewFactor = Math.max(0, Math.min(1, element.config.viewFactor));
var viewOffset = element.config.viewOffset;
var elementBounds = {
top: element.geometry.bounds.top + element.geometry.height * viewFactor,
right: element.geometry.bounds.right - element.geometry.width * viewFactor,
bottom: element.geometry.bounds.bottom - element.geometry.height * viewFactor,
left: element.geometry.bounds.left + element.geometry.width * viewFactor
};
var containerBounds = {
top: container.geometry.bounds.top + container.scroll.top + viewOffset.top,
right: container.geometry.bounds.right + container.scroll.left - viewOffset.right,
bottom:
container.geometry.bounds.bottom + container.scroll.top - viewOffset.bottom,
left: container.geometry.bounds.left + container.scroll.left + viewOffset.left
};
return (
(elementBounds.top < containerBounds.bottom &&
elementBounds.right > containerBounds.left &&
elementBounds.bottom > containerBounds.top &&
elementBounds.left < containerBounds.right) ||
element.styles.position === 'fixed'
)
}
function delegate(
event,
elements
) {
var this$1 = this;
if ( event === void 0 ) event = { type: 'init' };
if ( elements === void 0 ) elements = this.store.elements;
raf(function () {
var stale = event.type === 'init' || event.type === 'resize';
each(this$1.store.containers, function (container) {
if (stale) {
container.geometry = getGeometry.call(this$1, container, true);
}
var scroll = getScrolled.call(this$1, container);
if (container.scroll) {
container.direction = {
x: mathSign(scroll.left - container.scroll.left),
y: mathSign(scroll.top - container.scroll.top)
};
}
container.scroll = scroll;
});
/**
* Due to how the sequencer is implemented, it’s
* important that we update the state of all
* elements, before any animation logic is
* evaluated (in the second loop below).
*/
each(elements, function (element) {
if (stale || element.geometry === undefined) {
element.geometry = getGeometry.call(this$1, element);
}
element.visible = isElementVisible.call(this$1, element);
});
each(elements, function (element) {
if (element.sequence) {
sequence.call(this$1, element);
} else {
animate.call(this$1, element);
}
});
this$1.pristine = false;
});
}
function isTransformSupported() {
var style = document.documentElement.style;
return 'transform' in style || 'WebkitTransform' in style
}
function isTransitionSupported() {
var style = document.documentElement.style;
return 'transition' in style || 'WebkitTransition' in style
}
var version = "4.0.9";
var boundDelegate;
var boundDestroy;
var boundReveal;
var boundClean;
var boundSync;
var config;
var debug;
var instance;
function ScrollReveal(options) {
if ( options === void 0 ) options = {};
var invokedWithoutNew =
typeof this === 'undefined' ||
Object.getPrototypeOf(this) !== ScrollReveal.prototype;
if (invokedWithoutNew) {
return new ScrollReveal(options)
}
if (!ScrollReveal.isSupported()) {
logger.call(this, 'Instantiation failed.', 'This browser is not supported.');
return mount.failure()
}
var buffer;
try {
buffer = config
? deepAssign({}, config, options)
: deepAssign({}, defaults, options);
} catch (e) {
logger.call(this, 'Invalid configuration.', e.message);
return mount.failure()
}
try {
var container = $(buffer.container)[0];
if (!container) {
throw new Error('Invalid container.')
}
} catch (e) {
logger.call(this, e.message);
return mount.failure()
}
config = buffer;
if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
logger.call(
this,
'This device is disabled.',
("desktop: " + (config.desktop)),
("mobile: " + (config.mobile))
);
return mount.failure()
}
mount.success();
this.store = {
containers: {},
elements: {},
history: [],
sequences: {}
};
this.pristine = true;
boundDelegate = boundDelegate || delegate.bind(this);
boundDestroy = boundDestroy || destroy.bind(this);
boundReveal = boundReveal || reveal.bind(this);
boundClean = boundClean || clean.bind(this);
boundSync = boundSync || sync.bind(this);
Object.defineProperty(this, 'delegate', { get: function () { return boundDelegate; } });
Object.defineProperty(this, 'destroy', { get: function () { return boundDestroy; } });
Object.defineProperty(this, 'reveal', { get: function () { return boundReveal; } });
Object.defineProperty(this, 'clean', { get: function () { return boundClean; } });
Object.defineProperty(this, 'sync', { get: function () { return boundSync; } });
Object.defineProperty(this, 'defaults', { get: function () { return config; } });
Object.defineProperty(this, 'version', { get: function () { return version; } });
Object.defineProperty(this, 'noop', { get: function () { return false; } });
return instance ? instance : (instance = this)
}
ScrollReveal.isSupported = function () { return isTransformSupported() && isTransitionSupported(); };
Object.defineProperty(ScrollReveal, 'debug', {
get: function () { return debug || false; },
set: function (value) { return (debug = typeof value === 'boolean' ? value : debug); }
});
ScrollReveal();
export default ScrollReveal;
================================================
FILE: dist/scrollreveal.js
================================================
/*! @license ScrollReveal v4.0.9
Copyright 2021 Fisssion LLC.
Licensed under the GNU General Public License 3.0 for
compatible open source projects and non-commercial use.
For commercial sites, themes, projects, and applications,
keep your source code private/proprietary by purchasing
a commercial license from https://scrollrevealjs.org/
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.ScrollReveal = factory());
}(this, function () { 'use strict';
var defaults = {
delay: 0,
distance: '0',
duration: 600,
easing: 'cubic-bezier(0.5, 0, 0, 1)',
interval: 0,
opacity: 0,
origin: 'bottom',
rotate: {
x: 0,
y: 0,
z: 0
},
scale: 1,
cleanup: false,
container: document.documentElement,
desktop: true,
mobile: true,
reset: false,
useDelay: 'always',
viewFactor: 0.0,
viewOffset: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
afterReset: function afterReset() {},
afterReveal: function afterReveal() {},
beforeReset: function beforeReset() {},
beforeReveal: function beforeReveal() {}
};
function failure() {
document.documentElement.classList.remove('sr');
return {
clean: function clean() {},
destroy: function destroy() {},
reveal: function reveal() {},
sync: function sync() {},
get noop() {
return true
}
}
}
function success() {
document.documentElement.classList.add('sr');
if (document.body) {
document.body.style.height = '100%';
} else {
document.addEventListener('DOMContentLoaded', function () {
document.body.style.height = '100%';
});
}
}
var mount = { success: success, failure: failure };
/*! @license is-dom-node v1.0.4
Copyright 2018 Fisssion LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
function isDomNode(x) {
return typeof window.Node === 'object'
? x instanceof window.Node
: x !== null &&
typeof x === 'object' &&
typeof x.nodeType === 'number' &&
typeof x.nodeName === 'string'
}
/*! @license is-dom-node-list v1.2.1
Copyright 2018 Fisssion LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
function isDomNodeList(x) {
var prototypeToString = Object.prototype.toString.call(x);
var regex = /^\[object (HTMLCollection|NodeList|Object)\]$/;
return typeof window.NodeList === 'object'
? x instanceof window.NodeList
: x !== null &&
typeof x === 'object' &&
typeof x.length === 'number' &&
regex.test(prototypeToString) &&
(x.length === 0 || isDomNode(x[0]))
}
/*! @license Tealight v0.3.6
Copyright 2018 Fisssion LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
function tealight(target, context) {
if ( context === void 0 ) { context = document; }
if (target instanceof Array) { return target.filter(isDomNode); }
if (isDomNode(target)) { return [target]; }
if (isDomNodeList(target)) { return Array.prototype.slice.call(target); }
if (typeof target === "string") {
try {
var query = context.querySelectorAll(target);
return Array.prototype.slice.call(query);
} catch (err) {
return [];
}
}
return [];
}
function isObject(x) {
return (
x !== null &&
x instanceof Object &&
(x.constructor === Object ||
Object.prototype.toString.call(x) === '[object Object]')
)
}
function each(collection, callback) {
if (isObject(collection)) {
var keys = Object.keys(collection);
return keys.forEach(function (key) { return callback(collection[key], key, collection); })
}
if (collection instanceof Array) {
return collection.forEach(function (item, i) { return callback(item, i, collection); })
}
throw new TypeError('Expected either an array or object literal.')
}
function logger(message) {
var details = [], len = arguments.length - 1;
while ( len-- > 0 ) details[ len ] = arguments[ len + 1 ];
if (this.constructor.debug && console) {
var report = "%cScrollReveal: " + message;
details.forEach(function (detail) { return (report += "\n — " + detail); });
console.log(report, 'color: #ea654b;'); // eslint-disable-line no-console
}
}
function rinse() {
var this$1 = this;
var struct = function () { return ({
active: [],
stale: []
}); };
var elementIds = struct();
var sequenceIds = struct();
var containerIds = struct();
/**
* Take stock of active element IDs.
*/
try {
each(tealight('[data-sr-id]'), function (node) {
var id = parseInt(node.getAttribute('data-sr-id'));
elementIds.active.push(id);
});
} catch (e) {
throw e
}
/**
* Destroy stale elements.
*/
each(this.store.elements, function (element) {
if (elementIds.active.indexOf(element.id) === -1) {
elementIds.stale.push(element.id);
}
});
each(elementIds.stale, function (staleId) { return delete this$1.store.elements[staleId]; });
/**
* Take stock of active container and sequence IDs.
*/
each(this.store.elements, function (element) {
if (containerIds.active.indexOf(element.containerId) === -1) {
containerIds.active.push(element.containerId);
}
if (element.hasOwnProperty('sequence')) {
if (sequenceIds.active.indexOf(element.sequence.id) === -1) {
sequenceIds.active.push(element.sequence.id);
}
}
});
/**
* Destroy stale containers.
*/
each(this.store.containers, function (container) {
if (containerIds.active.indexOf(container.id) === -1) {
containerIds.stale.push(container.id);
}
});
each(containerIds.stale, function (staleId) {
var stale = this$1.store.containers[staleId].node;
stale.removeEventListener('scroll', this$1.delegate);
stale.removeEventListener('resize', this$1.delegate);
delete this$1.store.containers[staleId];
});
/**
* Destroy stale sequences.
*/
each(this.store.sequences, function (sequence) {
if (sequenceIds.active.indexOf(sequence.id) === -1) {
sequenceIds.stale.push(sequence.id);
}
});
each(sequenceIds.stale, function (staleId) { return delete this$1.store.sequences[staleId]; });
}
/*! @license Rematrix v0.3.0
Copyright 2018 Julian Lloyd.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/**
* @module Rematrix
*/
/**
* Transformation matrices in the browser come in two flavors:
*
* - `matrix` using 6 values (short)
* - `matrix3d` using 16 values (long)
*
* This utility follows this [conversion guide](https://goo.gl/EJlUQ1)
* to expand short form matrices to their equivalent long form.
*
* @param {array} source - Accepts both short and long form matrices.
* @return {array}
*/
function format(source) {
if (source.constructor !== Array) {
throw new TypeError('Expected array.')
}
if (source.length === 16) {
return source
}
if (source.length === 6) {
var matrix = identity();
matrix[0] = source[0];
matrix[1] = source[1];
matrix[4] = source[2];
matrix[5] = source[3];
matrix[12] = source[4];
matrix[13] = source[5];
return matrix
}
throw new RangeError('Expected array with either 6 or 16 values.')
}
/**
* Returns a matrix representing no transformation. The product of any matrix
* multiplied by the identity matrix will be the original matrix.
*
* > **Tip:** Similar to how `5 * 1 === 5`, where `1` is the identity.
*
* @return {array}
*/
function identity() {
var matrix = [];
for (var i = 0; i < 16; i++) {
i % 5 == 0 ? matrix.push(1) : matrix.push(0);
}
return matrix
}
/**
* Returns a 4x4 matrix describing the combined transformations
* of both arguments.
*
* > **Note:** Order is very important. For example, rotating 45°
* along the Z-axis, followed by translating 500 pixels along the
* Y-axis... is not the same as translating 500 pixels along the
* Y-axis, followed by rotating 45° along on the Z-axis.
*
* @param {array} m - Accepts both short and long form matrices.
* @param {array} x - Accepts both short and long form matrices.
* @return {array}
*/
function multiply(m, x) {
var fm = format(m);
var fx = format(x);
var product = [];
for (var i = 0; i < 4; i++) {
var row = [fm[i], fm[i + 4], fm[i + 8], fm[i + 12]];
for (var j = 0; j < 4; j++) {
var k = j * 4;
var col = [fx[k], fx[k + 1], fx[k + 2], fx[k + 3]];
var result =
row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3];
product[i + k] = result;
}
}
return product
}
/**
* Attempts to return a 4x4 matrix describing the CSS transform
* matrix passed in, but will return the identity matrix as a
* fallback.
*
* > **Tip:** This method is used to convert a CSS matrix (retrieved as a
* `string` from computed styles) to its equivalent array format.
*
* @param {string} source - `matrix` or `matrix3d` CSS Transform value.
* @return {array}
*/
function parse(source) {
if (typeof source === 'string') {
var match = source.match(/matrix(3d)?\(([^)]+)\)/);
if (match) {
var raw = match[2].split(', ').map(parseFloat);
return format(raw)
}
}
return identity()
}
/**
* Returns a 4x4 matrix describing X-axis rotation.
*
* @param {number} angle - Measured in degrees.
* @return {array}
*/
function rotateX(angle) {
var theta = Math.PI / 180 * angle;
var matrix = identity();
matrix[5] = matrix[10] = Math.cos(theta);
matrix[6] = matrix[9] = Math.sin(theta);
matrix[9] *= -1;
return matrix
}
/**
* Returns a 4x4 matrix describing Y-axis rotation.
*
* @param {number} angle - Measured in degrees.
* @return {array}
*/
function rotateY(angle) {
var theta = Math.PI / 180 * angle;
var matrix = identity();
matrix[0] = matrix[10] = Math.cos(theta);
matrix[2] = matrix[8] = Math.sin(theta);
matrix[2] *= -1;
return matrix
}
/**
* Returns a 4x4 matrix describing Z-axis rotation.
*
* @param {number} angle - Measured in degrees.
* @return {array}
*/
function rotateZ(angle) {
var theta = Math.PI / 180 * angle;
var matrix = identity();
matrix[0] = matrix[5] = Math.cos(theta);
matrix[1] = matrix[4] = Math.sin(theta);
matrix[4] *= -1;
return matrix
}
/**
* Returns a 4x4 matrix describing 2D scaling. The first argument
* is used for both X and Y-axis scaling, unless an optional
* second argument is provided to explicitly define Y-axis scaling.
*
* @param {number} scalar - Decimal multiplier.
* @param {number} [scalarY] - Decimal multiplier.
* @return {array}
*/
function scale(scalar, scalarY) {
var matrix = identity();
matrix[0] = scalar;
matrix[5] = typeof scalarY === 'number' ? scalarY : scalar;
return matrix
}
/**
* Returns a 4x4 matrix describing X-axis translation.
*
* @param {number} distance - Measured in pixels.
* @return {array}
*/
function translateX(distance) {
var matrix = identity();
matrix[12] = distance;
return matrix
}
/**
* Returns a 4x4 matrix describing Y-axis translation.
*
* @param {number} distance - Measured in pixels.
* @return {array}
*/
function translateY(distance) {
var matrix = identity();
matrix[13] = distance;
return matrix
}
var getPrefixedCssProp = (function () {
var properties = {};
var style = document.documentElement.style;
function getPrefixedCssProperty(name, source) {
if ( source === void 0 ) source = style;
if (name && typeof name === 'string') {
if (properties[name]) {
return properties[name]
}
if (typeof source[name] === 'string') {
return (properties[name] = name)
}
if (typeof source[("-webkit-" + name)] === 'string') {
return (properties[name] = "-webkit-" + name)
}
throw new RangeError(("Unable to find \"" + name + "\" style property."))
}
throw new TypeError('Expected a string.')
}
getPrefixedCssProperty.clearCache = function () { return (properties = {}); };
return getPrefixedCssProperty
})();
function style(element) {
var computed = window.getComputedStyle(element.node);
var position = computed.position;
var config = element.config;
/**
* Generate inline styles
*/
var inline = {};
var inlineStyle = element.node.getAttribute('style') || '';
var inlineMatch = inlineStyle.match(/[\w-]+\s*:\s*[^;]+\s*/gi) || [];
inline.computed = inlineMatch ? inlineMatch.map(function (m) { return m.trim(); }).join('; ') + ';' : '';
inline.generated = inlineMatch.some(function (m) { return m.match(/visibility\s?:\s?visible/i); })
? inline.computed
: inlineMatch.concat( ['visibility: visible']).map(function (m) { return m.trim(); }).join('; ') + ';';
/**
* Generate opacity styles
*/
var computedOpacity = parseFloat(computed.opacity);
var configOpacity = !isNaN(parseFloat(config.opacity))
? parseFloat(config.opacity)
: parseFloat(computed.opacity);
var opacity = {
computed: computedOpacity !== configOpacity ? ("opacity: " + computedOpacity + ";") : '',
generated: computedOpacity !== configOpacity ? ("opacity: " + configOpacity + ";") : ''
};
/**
* Generate transformation styles
*/
var transformations = [];
if (parseFloat(config.distance)) {
var axis = config.origin === 'top' || config.origin === 'bottom' ? 'Y' : 'X';
/**
* Let’s make sure our our pixel distances are negative for top and left.
* e.g. { origin: 'top', distance: '25px' } starts at `top: -25px` in CSS.
*/
var distance = config.distance;
if (config.origin === 'top' || config.origin === 'left') {
distance = /^-/.test(distance) ? distance.substr(1) : ("-" + distance);
}
var ref = distance.match(/(^-?\d+\.?\d?)|(em$|px$|%$)/g);
var value = ref[0];
var unit = ref[1];
switch (unit) {
case 'em':
distance = parseInt(computed.fontSize) * value;
break
case 'px':
distance = value;
break
case '%':
/**
* Here we use `getBoundingClientRect` instead of
* the existing data attached to `element.geometry`
* because only the former includes any transformations
* current applied to the element.
*
* If that behavior ends up being unintuitive, this
* logic could instead utilize `element.geometry.height`
* and `element.geoemetry.width` for the distance calculation
*/
distance =
axis === 'Y'
? (element.node.getBoundingClientRect().height * value) / 100
: (element.node.getBoundingClientRect().width * value) / 100;
break
default:
throw new RangeError('Unrecognized or missing distance unit.')
}
if (axis === 'Y') {
transformations.push(translateY(distance));
} else {
transformations.push(translateX(distance));
}
}
if (config.rotate.x) { transformations.push(rotateX(config.rotate.x)); }
if (config.rotate.y) { transformations.push(rotateY(config.rotate.y)); }
if (config.rotate.z) { transformations.push(rotateZ(config.rotate.z)); }
if (config.scale !== 1) {
if (config.scale === 0) {
/**
* The CSS Transforms matrix interpolation specification
* basically disallows transitions of non-invertible
* matrixes, which means browsers won't transition
* elements with zero scale.
*
* That’s inconvenient for the API and developer
* experience, so we simply nudge their value
* slightly above zero; this allows browsers
* to transition our element as expected.
*
* `0.0002` was the smallest number
* that performed across browsers.
*/
transformations.push(scale(0.0002));
} else {
transformations.push(scale(config.scale));
}
}
var transform = {};
if (transformations.length) {
transform.property = getPrefixedCssProp('transform');
/**
* The default computed transform value should be one of:
* undefined || 'none' || 'matrix()' || 'matrix3d()'
*/
transform.computed = {
raw: computed[transform.property],
matrix: parse(computed[transform.property])
};
transformations.unshift(transform.computed.matrix);
var product = transformations.reduce(multiply);
transform.generated = {
initial: ((transform.property) + ": matrix3d(" + (product.join(', ')) + ");"),
final: ((transform.property) + ": matrix3d(" + (transform.computed.matrix.join(', ')) + ");")
};
} else {
transform.generated = {
initial: '',
final: ''
};
}
/**
* Generate transition styles
*/
var transition = {};
if (opacity.generated || transform.generated.initial) {
transition.property = getPrefixedCssProp('transition');
transition.computed = computed[transition.property];
transition.fragments = [];
var delay = config.delay;
var duration = config.duration;
var easing = config.easing;
if (opacity.generated) {
transition.fragments.push({
delayed: ("opacity " + (duration / 1000) + "s " + easing + " " + (delay / 1000) + "s"),
instant: ("opacity " + (duration / 1000) + "s " + easing + " 0s")
});
}
if (transform.generated.initial) {
transition.fragments.push({
delayed: ((transform.property) + " " + (duration / 1000) + "s " + easing + " " + (delay / 1000) + "s"),
instant: ((transform.property) + " " + (duration / 1000) + "s " + easing + " 0s")
});
}
/**
* The default computed transition property should be undefined, or one of:
* '' || 'none 0s ease 0s' || 'all 0s ease 0s' || 'all 0s 0s cubic-bezier()'
*/
var hasCustomTransition =
transition.computed && !transition.computed.match(/all 0s|none 0s/);
if (hasCustomTransition) {
transition.fragments.unshift({
delayed: transition.computed,
instant: transition.computed
});
}
var composed = transition.fragments.reduce(
function (composition, fragment, i) {
composition.delayed += i === 0 ? fragment.delayed : (", " + (fragment.delayed));
composition.instant += i === 0 ? fragment.instant : (", " + (fragment.instant));
return composition
},
{
delayed: '',
instant: ''
}
);
transition.generated = {
delayed: ((transition.property) + ": " + (composed.delayed) + ";"),
instant: ((transition.property) + ": " + (composed.instant) + ";")
};
} else {
transition.generated = {
delayed: '',
instant: ''
};
}
return {
inline: inline,
opacity: opacity,
position: position,
transform: transform,
transition: transition
}
}
/**
* apply a CSS string to an element using the CSSOM (element.style) rather
* than setAttribute, which may violate the content security policy.
*
* @param {Node} [el] Element to receive styles.
* @param {string} [declaration] Styles to apply.
*/
function applyStyle (el, declaration) {
declaration.split(';').forEach(function (pair) {
var ref = pair.split(':');
var property = ref[0];
var value = ref.slice(1);
if (property && value) {
el.style[property.trim()] = value.join(':');
}
});
}
function clean(target) {
var this$1 = this;
var dirty;
try {
each(tealight(target), function (node) {
var id = node.getAttribute('data-sr-id');
if (id !== null) {
dirty = true;
var element = this$1.store.elements[id];
if (element.callbackTimer) {
window.clearTimeout(element.callbackTimer.clock);
}
applyStyle(element.node, element.styles.inline.generated);
node.removeAttribute('data-sr-id');
delete this$1.store.elements[id];
}
});
} catch (e) {
return logger.call(this, 'Clean failed.', e.message)
}
if (dirty) {
try {
rinse.call(this);
} catch (e) {
return logger.call(this, 'Clean failed.', e.message)
}
}
}
function destroy() {
var this$1 = this;
/**
* Remove all generated styles and element ids
*/
each(this.store.elements, function (element) {
applyStyle(element.node, element.styles.inline.generated);
element.node.removeAttribute('data-sr-id');
});
/**
* Remove all event listeners.
*/
each(this.store.containers, function (container) {
var target =
container.node === document.documentElement ? window : container.node;
target.removeEventListener('scroll', this$1.delegate);
target.removeEventListener('resize', this$1.delegate);
});
/**
* Clear all data from the store
*/
this.store = {
containers: {},
elements: {},
history: [],
sequences: {}
};
}
function deepAssign(target) {
var sources = [], len = arguments.length - 1;
while ( len-- > 0 ) sources[ len ] = arguments[ len + 1 ];
if (isObject(target)) {
each(sources, function (source) {
each(source, function (data, key) {
if (isObject(data)) {
if (!target[key] || !isObject(target[key])) {
target[key] = {};
}
deepAssign(target[key], data);
} else {
target[key] = data;
}
});
});
return target
} else {
throw new TypeError('Target must be an object literal.')
}
}
function isMobile(agent) {
if ( agent === void 0 ) agent = navigator.userAgent;
return /Android|iPhone|iPad|iPod/i.test(agent)
}
var nextUniqueId = (function () {
var uid = 0;
return function () { return uid++; }
})();
function initialize() {
var this$1 = this;
rinse.call(this);
each(this.store.elements, function (element) {
var styles = [element.styles.inline.generated];
if (element.visible) {
styles.push(element.styles.opacity.computed);
styles.push(element.styles.transform.generated.final);
element.revealed = true;
} else {
styles.push(element.styles.opacity.generated);
styles.push(element.styles.transform.generated.initial);
element.revealed = false;
}
applyStyle(element.node, styles.filter(function (s) { return s !== ''; }).join(' '));
});
each(this.store.containers, function (container) {
var target =
container.node === document.documentElement ? window : container.node;
target.addEventListener('scroll', this$1.delegate);
target.addEventListener('resize', this$1.delegate);
});
/**
* Manually invoke delegate once to capture
* element and container dimensions, container
* scroll position, and trigger any valid reveals
*/
this.delegate();
/**
* Wipe any existing `setTimeout` now
* that initialization has completed.
*/
this.initTimeout = null;
}
function animate(element, force) {
if ( force === void 0 ) force = {};
var pristine = force.pristine || this.pristine;
var delayed =
element.config.useDelay === 'always' ||
(element.config.useDelay === 'onload' && pristine) ||
(element.config.useDelay === 'once' && !element.seen);
var shouldReveal = element.visible && !element.revealed;
var shouldReset = !element.visible && element.revealed && element.config.reset;
if (force.reveal || shouldReveal) {
return triggerReveal.call(this, element, delayed)
}
if (force.reset || shouldReset) {
return triggerReset.call(this, element)
}
}
function triggerReveal(element, delayed) {
var styles = [
element.styles.inline.generated,
element.styles.opacity.computed,
element.styles.transform.generated.final
];
if (delayed) {
styles.push(element.styles.transition.generated.delayed);
} else {
styles.push(element.styles.transition.generated.instant);
}
element.revealed = element.seen = true;
applyStyle(element.node, styles.filter(function (s) { return s !== ''; }).join(' '));
registerCallbacks.call(this, element, delayed);
}
function triggerReset(element) {
var styles = [
element.styles.inline.generated,
element.styles.opacity.generated,
element.styles.transform.generated.initial,
element.styles.transition.generated.instant
];
element.revealed = false;
applyStyle(element.node, styles.filter(function (s) { return s !== ''; }).join(' '));
registerCallbacks.call(this, element);
}
function registerCallbacks(element, isDelayed) {
var this$1 = this;
var duration = isDelayed
? element.config.duration + element.config.delay
: element.config.duration;
var beforeCallback = element.revealed
? element.config.beforeReveal
: element.config.beforeReset;
var afterCallback = element.revealed
? element.config.afterReveal
: element.config.afterReset;
var elapsed = 0;
if (element.callbackTimer) {
elapsed = Date.now() - element.callbackTimer.start;
window.clearTimeout(element.callbackTimer.clock);
}
beforeCallback(element.node);
element.callbackTimer = {
start: Date.now(),
clock: window.setTimeout(function () {
afterCallback(element.node);
element.callbackTimer = null;
if (element.revealed && !element.config.reset && element.config.cleanup) {
clean.call(this$1, element.node);
}
}, duration - elapsed)
};
}
function sequence(element, pristine) {
if ( pristine === void 0 ) pristine = this.pristine;
/**
* We first check if the element should reset.
*/
if (!element.visible && element.revealed && element.config.reset) {
return animate.call(this, element, { reset: true })
}
var seq = this.store.sequences[element.sequence.id];
var i = element.sequence.index;
if (seq) {
var visible = new SequenceModel(seq, 'visible', this.store);
var revealed = new SequenceModel(seq, 'revealed', this.store);
seq.models = { visible: visible, revealed: revealed };
/**
* If the sequence has no revealed members,
* then we reveal the first visible element
* within that sequence.
*
* The sequence then cues a recursive call
* in both directions.
*/
if (!revealed.body.length) {
var nextId = seq.members[visible.body[0]];
var nextElement = this.store.elements[nextId];
if (nextElement) {
cue.call(this, seq, visible.body[0], -1, pristine);
cue.call(this, seq, visible.body[0], +1, pristine);
return animate.call(this, nextElement, { reveal: true, pristine: pristine })
}
}
/**
* If our element isn’t resetting, we check the
* element sequence index against the head, and
* then the foot of the sequence.
*/
if (
!seq.blocked.head &&
i === [].concat( revealed.head ).pop() &&
i >= [].concat( visible.body ).shift()
) {
cue.call(this, seq, i, -1, pristine);
return animate.call(this, element, { reveal: true, pristine: pristine })
}
if (
!seq.blocked.foot &&
i === [].concat( revealed.foot ).shift() &&
i <= [].concat( visible.body ).pop()
) {
cue.call(this, seq, i, +1, pristine);
return animate.call(this, element, { reveal: true, pristine: pristine })
}
}
}
function Sequence(interval) {
var i = Math.abs(interval);
if (!isNaN(i)) {
this.id = nextUniqueId();
this.interval = Math.max(i, 16);
this.members = [];
this.models = {};
this.blocked = {
head: false,
foot: false
};
} else {
throw new RangeError('Invalid sequence interval.')
}
}
function SequenceModel(seq, prop, store) {
var this$1 = this;
this.head = [];
this.body = [];
this.foot = [];
each(seq.members, function (id, index) {
var element = store.elements[id];
if (element && element[prop]) {
this$1.body.push(index);
}
});
if (this.body.length) {
each(seq.members, function (id, index) {
var element = store.elements[id];
if (element && !element[prop]) {
if (index < this$1.body[0]) {
this$1.head.push(index);
} else {
this$1.foot.push(index);
}
}
});
}
}
function cue(seq, i, direction, pristine) {
var this$1 = this;
var blocked = ['head', null, 'foot'][1 + direction];
var nextId = seq.members[i + direction];
var nextElement = this.store.elements[nextId];
seq.blocked[blocked] = true;
setTimeout(function () {
seq.blocked[blocked] = false;
if (nextElement) {
sequence.call(this$1, nextElement, pristine);
}
}, seq.interval);
}
function reveal(target, options, syncing) {
var this$1 = this;
if ( options === void 0 ) options = {};
if ( syncing === void 0 ) syncing = false;
var containerBuffer = [];
var sequence$$1;
var interval = options.interval || defaults.interval;
try {
if (interval) {
sequence$$1 = new Sequence(interval);
}
var nodes = tealight(target);
if (!nodes.length) {
throw new Error('Invalid reveal target.')
}
var elements = nodes.reduce(function (elementBuffer, elementNode) {
var element = {};
var existingId = elementNode.getAttribute('data-sr-id');
if (existingId) {
deepAssign(element, this$1.store.elements[existingId]);
/**
* In order to prevent previously generated styles
* from throwing off the new styles, the style tag
* has to be reverted to its pre-reveal state.
*/
applyStyle(element.node, element.styles.inline.computed);
} else {
element.id = nextUniqueId();
element.node = elementNode;
element.seen = false;
element.revealed = false;
element.visible = false;
}
var config = deepAssign({}, element.config || this$1.defaults, options);
if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
if (existingId) {
clean.call(this$1, element);
}
return elementBuffer // skip elements that are disabled
}
var containerNode = tealight(config.container)[0];
if (!containerNode) {
throw new Error('Invalid container.')
}
if (!containerNode.contains(elementNode)) {
return elementBuffer // skip elements found outside the container
}
var containerId;
{
containerId = getContainerId(
containerNode,
containerBuffer,
this$1.store.containers
);
if (containerId === null) {
containerId = nextUniqueId();
containerBuffer.push({ id: containerId, node: containerNode });
}
}
element.config = config;
element.containerId = containerId;
element.styles = style(element);
if (sequence$$1) {
element.sequence = {
id: sequence$$1.id,
index: sequence$$1.members.length
};
sequence$$1.members.push(element.id);
}
elementBuffer.push(element);
return elementBuffer
}, []);
/**
* Modifying the DOM via setAttribute needs to be handled
* separately from reading computed styles in the map above
* for the browser to batch DOM changes (limiting reflows)
*/
each(elements, function (element) {
this$1.store.elements[element.id] = element;
element.node.setAttribute('data-sr-id', element.id);
});
} catch (e) {
return logger.call(this, 'Reveal failed.', e.message)
}
/**
* Now that element set-up is complete...
* Let’s commit any container and sequence data we have to the store.
*/
each(containerBuffer, function (container) {
this$1.store.containers[container.id] = {
id: container.id,
node: container.node
};
});
if (sequence$$1) {
this.store.sequences[sequence$$1.id] = sequence$$1;
}
/**
* If reveal wasn't invoked by sync, we want to
* make sure to add this call to the history.
*/
if (syncing !== true) {
this.store.history.push({ target: target, options: options });
/**
* Push initialization to the event queue, giving
* multiple reveal calls time to be interpreted.
*/
if (this.initTimeout) {
window.clearTimeout(this.initTimeout);
}
this.initTimeout = window.setTimeout(initialize.bind(this), 0);
}
}
function getContainerId(node) {
var collections = [], len = arguments.length - 1;
while ( len-- > 0 ) collections[ len ] = arguments[ len + 1 ];
var id = null;
each(collections, function (collection) {
each(collection, function (container) {
if (id === null && container.node === node) {
id = container.id;
}
});
});
return id
}
/**
* Re-runs the reveal method for each record stored in history,
* for capturing new content asynchronously loaded into the DOM.
*/
function sync() {
var this$1 = this;
each(this.store.history, function (record) {
reveal.call(this$1, record.target, record.options, true);
});
initialize.call(this);
}
var polyfill = function (x) { return (x > 0) - (x < 0) || +x; };
var mathSign = Math.sign || polyfill;
/*! @license miniraf v1.0.1
Copyright 2018 Fisssion LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
var polyfill$1 = (function () {
var clock = Date.now();
return function (callback) {
var currentTime = Date.now();
if (currentTime - clock > 16) {
clock = currentTime;
callback(currentTime);
} else {
setTimeout(function () { return polyfill$1(callback); }, 0);
}
}
})();
var miniraf = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
polyfill$1;
function getGeometry(target, isContainer) {
/**
* We want to ignore padding and scrollbars for container elements.
* More information here: https://goo.gl/vOZpbz
*/
var height = isContainer ? target.node.clientHeight : target.node.offsetHeight;
var width = isContainer ? target.node.clientWidth : target.node.offsetWidth;
var offsetTop = 0;
var offsetLeft = 0;
var node = target.node;
do {
if (!isNaN(node.offsetTop)) {
offsetTop += node.offsetTop;
}
if (!isNaN(node.offsetLeft)) {
offsetLeft += node.offsetLeft;
}
node = node.offsetParent;
} while (node)
return {
bounds: {
top: offsetTop,
right: offsetLeft + width,
bottom: offsetTop + height,
left: offsetLeft
},
height: height,
width: width
}
}
function getScrolled(container) {
var top, left;
if (container.node === document.documentElement) {
top = window.pageYOffset;
left = window.pageXOffset;
} else {
top = container.node.scrollTop;
left = container.node.scrollLeft;
}
return { top: top, left: left }
}
function isElementVisible(element) {
if ( element === void 0 ) element = {};
var container = this.store.containers[element.containerId];
if (!container) { return }
var viewFactor = Math.max(0, Math.min(1, element.config.viewFactor));
var viewOffset = element.config.viewOffset;
var elementBounds = {
top: element.geometry.bounds.top + element.geometry.height * viewFactor,
right: element.geometry.bounds.right - element.geometry.width * viewFactor,
bottom: element.geometry.bounds.bottom - element.geometry.height * viewFactor,
left: element.geometry.bounds.left + element.geometry.width * viewFactor
};
var containerBounds = {
top: container.geometry.bounds.top + container.scroll.top + viewOffset.top,
right: container.geometry.bounds.right + container.scroll.left - viewOffset.right,
bottom:
container.geometry.bounds.bottom + container.scroll.top - viewOffset.bottom,
left: container.geometry.bounds.left + container.scroll.left + viewOffset.left
};
return (
(elementBounds.top < containerBounds.bottom &&
elementBounds.right > containerBounds.left &&
elementBounds.bottom > containerBounds.top &&
elementBounds.left < containerBounds.right) ||
element.styles.position === 'fixed'
)
}
function delegate(
event,
elements
) {
var this$1 = this;
if ( event === void 0 ) event = { type: 'init' };
if ( elements === void 0 ) elements = this.store.elements;
miniraf(function () {
var stale = event.type === 'init' || event.type === 'resize';
each(this$1.store.containers, function (container) {
if (stale) {
container.geometry = getGeometry.call(this$1, container, true);
}
var scroll = getScrolled.call(this$1, container);
if (container.scroll) {
container.direction = {
x: mathSign(scroll.left - container.scroll.left),
y: mathSign(scroll.top - container.scroll.top)
};
}
container.scroll = scroll;
});
/**
* Due to how the sequencer is implemented, it’s
* important that we update the state of all
* elements, before any animation logic is
* evaluated (in the second loop below).
*/
each(elements, function (element) {
if (stale || element.geometry === undefined) {
element.geometry = getGeometry.call(this$1, element);
}
element.visible = isElementVisible.call(this$1, element);
});
each(elements, function (element) {
if (element.sequence) {
sequence.call(this$1, element);
} else {
animate.call(this$1, element);
}
});
this$1.pristine = false;
});
}
function isTransformSupported() {
var style = document.documentElement.style;
return 'transform' in style || 'WebkitTransform' in style
}
function isTransitionSupported() {
var style = document.documentElement.style;
return 'transition' in style || 'WebkitTransition' in style
}
var version = "4.0.9";
var boundDelegate;
var boundDestroy;
var boundReveal;
var boundClean;
var boundSync;
var config;
var debug;
var instance;
function ScrollReveal(options) {
if ( options === void 0 ) options = {};
var invokedWithoutNew =
typeof this === 'undefined' ||
Object.getPrototypeOf(this) !== ScrollReveal.prototype;
if (invokedWithoutNew) {
return new ScrollReveal(options)
}
if (!ScrollReveal.isSupported()) {
logger.call(this, 'Instantiation failed.', 'This browser is not supported.');
return mount.failure()
}
var buffer;
try {
buffer = config
? deepAssign({}, config, options)
: deepAssign({}, defaults, options);
} catch (e) {
logger.call(this, 'Invalid configuration.', e.message);
return mount.failure()
}
try {
var container = tealight(buffer.container)[0];
if (!container) {
throw new Error('Invalid container.')
}
} catch (e) {
logger.call(this, e.message);
return mount.failure()
}
config = buffer;
if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
logger.call(
this,
'This device is disabled.',
("desktop: " + (config.desktop)),
("mobile: " + (config.mobile))
);
return mount.failure()
}
mount.success();
this.store = {
containers: {},
elements: {},
history: [],
sequences: {}
};
this.pristine = true;
boundDelegate = boundDelegate || delegate.bind(this);
boundDestroy = boundDestroy || destroy.bind(this);
boundReveal = boundReveal || reveal.bind(this);
boundClean = boundClean || clean.bind(this);
boundSync = boundSync || sync.bind(this);
Object.defineProperty(this, 'delegate', { get: function () { return boundDelegate; } });
Object.defineProperty(this, 'destroy', { get: function () { return boundDestroy; } });
Object.defineProperty(this, 'reveal', { get: function () { return boundReveal; } });
Object.defineProperty(this, 'clean', { get: function () { return boundClean; } });
Object.defineProperty(this, 'sync', { get: function () { return boundSync; } });
Object.defineProperty(this, 'defaults', { get: function () { return config; } });
Object.defineProperty(this, 'version', { get: function () { return version; } });
Object.defineProperty(this, 'noop', { get: function () { return false; } });
return instance ? instance : (instance = this)
}
ScrollReveal.isSupported = function () { return isTransformSupported() && isTransitionSupported(); };
Object.defineProperty(ScrollReveal, 'debug', {
get: function () { return debug || false; },
set: function (value) { return (debug = typeof value === 'boolean' ? value : debug); }
});
ScrollReveal();
return ScrollReveal;
}));
================================================
FILE: package.json
================================================
{
"name": "scrollreveal",
"version": "4.0.9",
"description": "Animate elements as they scroll into view",
"homepage": "https://scrollrevealjs.org",
"main": "dist/scrollreveal.js",
"module": "dist/scrollreveal.es.js",
"jsnext:main": "dist/scrollreveal.es.js",
"files": [
"dist"
],
"scripts": {
"prebuild": "rm -rf dist/*",
"build": "npm run bundle && npm run bundle:min",
"bundle": "./node_modules/rollup/bin/rollup -c ./build/rollup.conf.js",
"bundle:min": "./node_modules/rollup/bin/rollup -c ./build/rollup.conf.min.js",
"lint": "./node_modules/eslint/bin/eslint.js src test",
"pretest": "rm -rf .ignore/coverage/**/ && npm run lint",
"test": "./node_modules/karma/bin/karma start ./test/karma.conf.js",
"testing": "cross-env COVERAGE=true npm test -- --no-single-run",
"coverage": "cross-env COVERAGE=true npm test",
"sandbox:bundle": "./node_modules/rollup/bin/rollup -w -c ./.ignore/sandbox/rollup.conf.sandbox.js",
"sandbox:server": "node ./.ignore/sandbox/server.sandbox.js",
"coverage:server": "node ./.ignore/coverage/server.coverage.js",
"postinstall": "node -e \"console.log('\\u001b[35m\\u001b[1mLove ScrollReveal? 🔑 Buy a license!\\u001b[22m\\u001b[35m\\n >> \\u001b[33mhttps://scrollrevealjs.org/pricing/\\u001b[0m\\n')\""
},
"keywords": [
"scroll",
"animation",
"reveal",
"css",
"transform",
"transition"
],
"repository": {
"type": "git",
"url": "https://github.com/jlmakes/scrollreveal.git"
},
"bugs": {
"url": "https://github.com/jlmakes/scrollreveal/issues"
},
"dependencies": {
"miniraf": "1.0.0",
"rematrix": "0.3.0",
"tealight": "0.3.6"
},
"devDependencies": {
"chai": "^4.1.2",
"cross-env": "^5.1.3",
"eslint": "^4.16.0",
"karma": "^2.0.0",
"karma-chrome-launcher": "^2.0.0",
"karma-coverage": "^1.1.1",
"karma-coveralls": "^1.1.2",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.5",
"karma-rollup-preprocessor": "^5.1.1",
"karma-sauce-launcher": "^1.1.0",
"karma-sinon-chai": "^1.3.3",
"live-server": "jlmakes/live-server",
"mocha": "^4.0.1",
"rollup": "^0.55.0",
"rollup-plugin-buble": "^0.x",
"rollup-plugin-istanbul": "^1.1.0",
"rollup-plugin-json": "^2.1.0",
"rollup-plugin-node-resolve": "^3.0.0",
"rollup-plugin-strip": "^1.1.1",
"rollup-plugin-uglify": "^2.0.1",
"rollup-watch": "^4.3.1",
"sinon": "^4.2.0",
"sinon-chai": "^2.8.0"
},
"author": "Julian Lloyd",
"license": "GPL-3.0"
}
================================================
FILE: src/index.js
================================================
import Constructor from './instance/constructor'
Constructor()
export default Constructor
================================================
FILE: src/instance/constructor.js
================================================
import defaults from './defaults'
import mount from './mount'
import clean from './methods/clean'
import destroy from './methods/destroy'
import reveal from './methods/reveal'
import sync from './methods/sync'
import delegate from './functions/delegate'
import isMobile from '../utils/is-mobile'
import isTransformSupported from '../utils/is-transform-supported'
import isTransitionSupported from '../utils/is-transition-supported'
import deepAssign from '../utils/deep-assign'
import logger from '../utils/logger'
import $ from 'tealight'
import { version } from '../../package.json'
let boundDelegate
let boundDestroy
let boundReveal
let boundClean
let boundSync
let config
let debug
let instance
export default function ScrollReveal(options = {}) {
const invokedWithoutNew =
typeof this === 'undefined' ||
Object.getPrototypeOf(this) !== ScrollReveal.prototype
if (invokedWithoutNew) {
return new ScrollReveal(options)
}
if (!ScrollReveal.isSupported()) {
logger.call(this, 'Instantiation failed.', 'This browser is not supported.')
return mount.failure()
}
let buffer
try {
buffer = config
? deepAssign({}, config, options)
: deepAssign({}, defaults, options)
} catch (e) {
logger.call(this, 'Invalid configuration.', e.message)
return mount.failure()
}
try {
const container = $(buffer.container)[0]
if (!container) {
throw new Error('Invalid container.')
}
} catch (e) {
logger.call(this, e.message)
return mount.failure()
}
config = buffer
if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
logger.call(
this,
'This device is disabled.',
`desktop: ${config.desktop}`,
`mobile: ${config.mobile}`
)
return mount.failure()
}
mount.success()
this.store = {
containers: {},
elements: {},
history: [],
sequences: {}
}
this.pristine = true
boundDelegate = boundDelegate || delegate.bind(this)
boundDestroy = boundDestroy || destroy.bind(this)
boundReveal = boundReveal || reveal.bind(this)
boundClean = boundClean || clean.bind(this)
boundSync = boundSync || sync.bind(this)
Object.defineProperty(this, 'delegate', { get: () => boundDelegate })
Object.defineProperty(this, 'destroy', { get: () => boundDestroy })
Object.defineProperty(this, 'reveal', { get: () => boundReveal })
Object.defineProperty(this, 'clean', { get: () => boundClean })
Object.defineProperty(this, 'sync', { get: () => boundSync })
Object.defineProperty(this, 'defaults', { get: () => config })
Object.defineProperty(this, 'version', { get: () => version })
Object.defineProperty(this, 'noop', { get: () => false })
return instance ? instance : (instance = this)
}
ScrollReveal.isSupported = () =>
isTransformSupported() && isTransitionSupported()
Object.defineProperty(ScrollReveal, 'debug', {
get: () => debug || false,
set: value => (debug = typeof value === 'boolean' ? value : debug)
})
================================================
FILE: src/instance/defaults.js
================================================
export default {
delay: 0,
distance: '0',
duration: 600,
easing: 'cubic-bezier(0.5, 0, 0, 1)',
interval: 0,
opacity: 0,
origin: 'bottom',
rotate: {
x: 0,
y: 0,
z: 0
},
scale: 1,
cleanup: false,
container: document.documentElement,
desktop: true,
mobile: true,
reset: false,
useDelay: 'always',
viewFactor: 0.0,
viewOffset: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
afterReset() {},
afterReveal() {},
beforeReset() {},
beforeReveal() {}
}
================================================
FILE: src/instance/functions/animate.js
================================================
import { applyStyle } from '../functions/style'
import clean from '../methods/clean'
export default function animate(element, force = {}) {
const pristine = force.pristine || this.pristine
const delayed =
element.config.useDelay === 'always' ||
(element.config.useDelay === 'onload' && pristine) ||
(element.config.useDelay === 'once' && !element.seen)
const shouldReveal = element.visible && !element.revealed
const shouldReset = !element.visible && element.revealed && element.config.reset
if (force.reveal || shouldReveal) {
return triggerReveal.call(this, element, delayed)
}
if (force.reset || shouldReset) {
return triggerReset.call(this, element)
}
}
function triggerReveal(element, delayed) {
const styles = [
element.styles.inline.generated,
element.styles.opacity.computed,
element.styles.transform.generated.final
]
if (delayed) {
styles.push(element.styles.transition.generated.delayed)
} else {
styles.push(element.styles.transition.generated.instant)
}
element.revealed = element.seen = true
applyStyle(element.node, styles.filter((s) => s !== '').join(' '))
registerCallbacks.call(this, element, delayed)
}
function triggerReset(element) {
const styles = [
element.styles.inline.generated,
element.styles.opacity.generated,
element.styles.transform.generated.initial,
element.styles.transition.generated.instant
]
element.revealed = false
applyStyle(element.node, styles.filter((s) => s !== '').join(' '))
registerCallbacks.call(this, element)
}
function registerCallbacks(element, isDelayed) {
const duration = isDelayed
? element.config.duration + element.config.delay
: element.config.duration
const beforeCallback = element.revealed
? element.config.beforeReveal
: element.config.beforeReset
const afterCallback = element.revealed
? element.config.afterReveal
: element.config.afterReset
let elapsed = 0
if (element.callbackTimer) {
elapsed = Date.now() - element.callbackTimer.start
window.clearTimeout(element.callbackTimer.clock)
}
beforeCallback(element.node)
element.callbackTimer = {
start: Date.now(),
clock: window.setTimeout(() => {
afterCallback(element.node)
element.callbackTimer = null
if (element.revealed && !element.config.reset && element.config.cleanup) {
clean.call(this, element.node)
}
}, duration - elapsed)
}
}
================================================
FILE: src/instance/functions/delegate.js
================================================
import animate from './animate'
import sequence from './sequence'
import mathSign from '../../polyfills/math-sign'
import raf from 'miniraf'
import each from '../../utils/each'
import getGeometry from '../../utils/get-geometry'
import getScrolled from '../../utils/get-scrolled'
import isElementVisible from '../../utils/is-element-visible'
export default function delegate(
event = { type: 'init' },
elements = this.store.elements
) {
raf(() => {
const stale = event.type === 'init' || event.type === 'resize'
each(this.store.containers, container => {
if (stale) {
container.geometry = getGeometry.call(this, container, true)
}
const scroll = getScrolled.call(this, container)
if (container.scroll) {
container.direction = {
x: mathSign(scroll.left - container.scroll.left),
y: mathSign(scroll.top - container.scroll.top)
}
}
container.scroll = scroll
})
/**
* Due to how the sequencer is implemented, it’s
* important that we update the state of all
* elements, before any animation logic is
* evaluated (in the second loop below).
*/
each(elements, element => {
if (stale || element.geometry === undefined) {
element.geometry = getGeometry.call(this, element)
}
element.visible = isElementVisible.call(this, element)
})
each(elements, element => {
if (element.sequence) {
sequence.call(this, element)
} else {
animate.call(this, element)
}
})
this.pristine = false
})
}
================================================
FILE: src/instance/functions/initialize.js
================================================
import each from '../../utils/each'
import { applyStyle } from '../functions/style'
import rinse from './rinse'
export default function initialize() {
rinse.call(this)
each(this.store.elements, element => {
let styles = [element.styles.inline.generated]
if (element.visible) {
styles.push(element.styles.opacity.computed)
styles.push(element.styles.transform.generated.final)
element.revealed = true
} else {
styles.push(element.styles.opacity.generated)
styles.push(element.styles.transform.generated.initial)
element.revealed = false
}
applyStyle(element.node, styles.filter((s) => s !== '').join(' '))
})
each(this.store.containers, container => {
const target =
container.node === document.documentElement ? window : container.node
target.addEventListener('scroll', this.delegate)
target.addEventListener('resize', this.delegate)
})
/**
* Manually invoke delegate once to capture
* element and container dimensions, container
* scroll position, and trigger any valid reveals
*/
this.delegate()
/**
* Wipe any existing `setTimeout` now
* that initialization has completed.
*/
this.initTimeout = null
}
================================================
FILE: src/instance/functions/rinse.js
================================================
import $ from 'tealight'
import each from '../../utils/each'
export default function rinse() {
const struct = () => ({
active: [],
stale: []
})
const elementIds = struct()
const sequenceIds = struct()
const containerIds = struct()
/**
* Take stock of active element IDs.
*/
try {
each($('[data-sr-id]'), node => {
const id = parseInt(node.getAttribute('data-sr-id'))
elementIds.active.push(id)
})
} catch (e) {
throw e
}
/**
* Destroy stale elements.
*/
each(this.store.elements, element => {
if (elementIds.active.indexOf(element.id) === -1) {
elementIds.stale.push(element.id)
}
})
each(elementIds.stale, staleId => delete this.store.elements[staleId])
/**
* Take stock of active container and sequence IDs.
*/
each(this.store.elements, element => {
if (containerIds.active.indexOf(element.containerId) === -1) {
containerIds.active.push(element.containerId)
}
if (element.hasOwnProperty('sequence')) {
if (sequenceIds.active.indexOf(element.sequence.id) === -1) {
sequenceIds.active.push(element.sequence.id)
}
}
})
/**
* Destroy stale containers.
*/
each(this.store.containers, container => {
if (containerIds.active.indexOf(container.id) === -1) {
containerIds.stale.push(container.id)
}
})
each(containerIds.stale, staleId => {
const stale = this.store.containers[staleId].node
stale.removeEventListener('scroll', this.delegate)
stale.removeEventListener('resize', this.delegate)
delete this.store.containers[staleId]
})
/**
* Destroy stale sequences.
*/
each(this.store.sequences, sequence => {
if (sequenceIds.active.indexOf(sequence.id) === -1) {
sequenceIds.stale.push(sequence.id)
}
})
each(sequenceIds.stale, staleId => delete this.store.sequences[staleId])
}
================================================
FILE: src/instance/functions/sequence.js
================================================
import animate from './animate'
import each from '../../utils/each'
import nextUniqueId from '../../utils/next-unique-id'
export default function sequence(element, pristine = this.pristine) {
/**
* We first check if the element should reset.
*/
if (!element.visible && element.revealed && element.config.reset) {
return animate.call(this, element, { reset: true })
}
const seq = this.store.sequences[element.sequence.id]
const i = element.sequence.index
if (seq) {
const visible = new SequenceModel(seq, 'visible', this.store)
const revealed = new SequenceModel(seq, 'revealed', this.store)
seq.models = { visible, revealed }
/**
* If the sequence has no revealed members,
* then we reveal the first visible element
* within that sequence.
*
* The sequence then cues a recursive call
* in both directions.
*/
if (!revealed.body.length) {
const nextId = seq.members[visible.body[0]]
const nextElement = this.store.elements[nextId]
if (nextElement) {
cue.call(this, seq, visible.body[0], -1, pristine)
cue.call(this, seq, visible.body[0], +1, pristine)
return animate.call(this, nextElement, { reveal: true, pristine })
}
}
/**
* If our element isn’t resetting, we check the
* element sequence index against the head, and
* then the foot of the sequence.
*/
if (
!seq.blocked.head &&
i === [...revealed.head].pop() &&
i >= [...visible.body].shift()
) {
cue.call(this, seq, i, -1, pristine)
return animate.call(this, element, { reveal: true, pristine })
}
if (
!seq.blocked.foot &&
i === [...revealed.foot].shift() &&
i <= [...visible.body].pop()
) {
cue.call(this, seq, i, +1, pristine)
return animate.call(this, element, { reveal: true, pristine })
}
}
}
export function Sequence(interval) {
const i = Math.abs(interval)
if (!isNaN(i)) {
this.id = nextUniqueId()
this.interval = Math.max(i, 16)
this.members = []
this.models = {}
this.blocked = {
head: false,
foot: false
}
} else {
throw new RangeError('Invalid sequence interval.')
}
}
function SequenceModel(seq, prop, store) {
this.head = []
this.body = []
this.foot = []
each(seq.members, (id, index) => {
const element = store.elements[id]
if (element && element[prop]) {
this.body.push(index)
}
})
if (this.body.length) {
each(seq.members, (id, index) => {
const element = store.elements[id]
if (element && !element[prop]) {
if (index < this.body[0]) {
this.head.push(index)
} else {
this.foot.push(index)
}
}
})
}
}
function cue(seq, i, direction, pristine) {
const blocked = ['head', null, 'foot'][1 + direction]
const nextId = seq.members[i + direction]
const nextElement = this.store.elements[nextId]
seq.blocked[blocked] = true
setTimeout(() => {
seq.blocked[blocked] = false
if (nextElement) {
sequence.call(this, nextElement, pristine)
}
}, seq.interval)
}
================================================
FILE: src/instance/functions/style.js
================================================
import {
multiply,
parse,
rotateX,
rotateY,
rotateZ,
scale,
translateX,
translateY
} from 'rematrix'
import getPrefixedCssProp from '../../utils/get-prefixed-css-prop'
export default function style(element) {
const computed = window.getComputedStyle(element.node)
const position = computed.position
const config = element.config
/**
* Generate inline styles
*/
const inline = {}
const inlineStyle = element.node.getAttribute('style') || ''
const inlineMatch = inlineStyle.match(/[\w-]+\s*:\s*[^;]+\s*/gi) || []
inline.computed = inlineMatch ? inlineMatch.map(m => m.trim()).join('; ') + ';' : ''
inline.generated = inlineMatch.some(m => m.match(/visibility\s?:\s?visible/i))
? inline.computed
: [...inlineMatch, 'visibility: visible'].map(m => m.trim()).join('; ') + ';'
/**
* Generate opacity styles
*/
const computedOpacity = parseFloat(computed.opacity)
const configOpacity = !isNaN(parseFloat(config.opacity))
? parseFloat(config.opacity)
: parseFloat(computed.opacity)
const opacity = {
computed: computedOpacity !== configOpacity ? `opacity: ${computedOpacity};` : '',
generated: computedOpacity !== configOpacity ? `opacity: ${configOpacity};` : ''
}
/**
* Generate transformation styles
*/
const transformations = []
if (parseFloat(config.distance)) {
const axis = config.origin === 'top' || config.origin === 'bottom' ? 'Y' : 'X'
/**
* Let’s make sure our our pixel distances are negative for top and left.
* e.g. { origin: 'top', distance: '25px' } starts at `top: -25px` in CSS.
*/
let distance = config.distance
if (config.origin === 'top' || config.origin === 'left') {
distance = /^-/.test(distance) ? distance.substr(1) : `-${distance}`
}
const [value, unit] = distance.match(/(^-?\d+\.?\d?)|(em$|px$|%$)/g)
switch (unit) {
case 'em':
distance = parseInt(computed.fontSize) * value
break
case 'px':
distance = value
break
case '%':
/**
* Here we use `getBoundingClientRect` instead of
* the existing data attached to `element.geometry`
* because only the former includes any transformations
* current applied to the element.
*
* If that behavior ends up being unintuitive, this
* logic could instead utilize `element.geometry.height`
* and `element.geoemetry.width` for the distance calculation
*/
distance =
axis === 'Y'
? (element.node.getBoundingClientRect().height * value) / 100
: (element.node.getBoundingClientRect().width * value) / 100
break
default:
throw new RangeError('Unrecognized or missing distance unit.')
}
if (axis === 'Y') {
transformations.push(translateY(distance))
} else {
transformations.push(translateX(distance))
}
}
if (config.rotate.x) transformations.push(rotateX(config.rotate.x))
if (config.rotate.y) transformations.push(rotateY(config.rotate.y))
if (config.rotate.z) transformations.push(rotateZ(config.rotate.z))
if (config.scale !== 1) {
if (config.scale === 0) {
/**
* The CSS Transforms matrix interpolation specification
* basically disallows transitions of non-invertible
* matrixes, which means browsers won't transition
* elements with zero scale.
*
* That’s inconvenient for the API and developer
* experience, so we simply nudge their value
* slightly above zero; this allows browsers
* to transition our element as expected.
*
* `0.0002` was the smallest number
* that performed across browsers.
*/
transformations.push(scale(0.0002))
} else {
transformations.push(scale(config.scale))
}
}
const transform = {}
if (transformations.length) {
transform.property = getPrefixedCssProp('transform')
/**
* The default computed transform value should be one of:
* undefined || 'none' || 'matrix()' || 'matrix3d()'
*/
transform.computed = {
raw: computed[transform.property],
matrix: parse(computed[transform.property])
}
transformations.unshift(transform.computed.matrix)
const product = transformations.reduce(multiply)
transform.generated = {
initial: `${transform.property}: matrix3d(${product.join(', ')});`,
final: `${transform.property}: matrix3d(${transform.computed.matrix.join(', ')});`
}
} else {
transform.generated = {
initial: '',
final: ''
}
}
/**
* Generate transition styles
*/
let transition = {}
if (opacity.generated || transform.generated.initial) {
transition.property = getPrefixedCssProp('transition')
transition.computed = computed[transition.property]
transition.fragments = []
const { delay, duration, easing } = config
if (opacity.generated) {
transition.fragments.push({
delayed: `opacity ${duration / 1000}s ${easing} ${delay / 1000}s`,
instant: `opacity ${duration / 1000}s ${easing} 0s`
})
}
if (transform.generated.initial) {
transition.fragments.push({
delayed: `${transform.property} ${duration / 1000}s ${easing} ${delay / 1000}s`,
instant: `${transform.property} ${duration / 1000}s ${easing} 0s`
})
}
/**
* The default computed transition property should be undefined, or one of:
* '' || 'none 0s ease 0s' || 'all 0s ease 0s' || 'all 0s 0s cubic-bezier()'
*/
let hasCustomTransition =
transition.computed && !transition.computed.match(/all 0s|none 0s/)
if (hasCustomTransition) {
transition.fragments.unshift({
delayed: transition.computed,
instant: transition.computed
})
}
const composed = transition.fragments.reduce(
(composition, fragment, i) => {
composition.delayed += i === 0 ? fragment.delayed : `, ${fragment.delayed}`
composition.instant += i === 0 ? fragment.instant : `, ${fragment.instant}`
return composition
},
{
delayed: '',
instant: ''
}
)
transition.generated = {
delayed: `${transition.property}: ${composed.delayed};`,
instant: `${transition.property}: ${composed.instant};`
}
} else {
transition.generated = {
delayed: '',
instant: ''
}
}
return {
inline,
opacity,
position,
transform,
transition
}
}
/**
* apply a CSS string to an element using the CSSOM (element.style) rather
* than setAttribute, which may violate the content security policy.
*
* @param {Node} [el] Element to receive styles.
* @param {string} [declaration] Styles to apply.
*/
export function applyStyle (el, declaration) {
declaration.split(';').forEach(pair => {
const [property, ...value] = pair.split(':')
if (property && value) {
el.style[property.trim()] = value.join(':')
}
})
}
================================================
FILE: src/instance/methods/clean.js
================================================
import $ from 'tealight'
import each from '../../utils/each'
import logger from '../../utils/logger'
import rinse from '../functions/rinse'
import { applyStyle } from '../functions/style'
export default function clean(target) {
let dirty
try {
each($(target), node => {
const id = node.getAttribute('data-sr-id')
if (id !== null) {
dirty = true
const element = this.store.elements[id]
if (element.callbackTimer) {
window.clearTimeout(element.callbackTimer.clock)
}
applyStyle(element.node, element.styles.inline.generated)
node.removeAttribute('data-sr-id')
delete this.store.elements[id]
}
})
} catch (e) {
return logger.call(this, 'Clean failed.', e.message)
}
if (dirty) {
try {
rinse.call(this)
} catch (e) {
return logger.call(this, 'Clean failed.', e.message)
}
}
}
================================================
FILE: src/instance/methods/destroy.js
================================================
import each from '../../utils/each'
import { applyStyle } from '../functions/style'
export default function destroy() {
/**
* Remove all generated styles and element ids
*/
each(this.store.elements, element => {
applyStyle(element.node, element.styles.inline.generated)
element.node.removeAttribute('data-sr-id')
})
/**
* Remove all event listeners.
*/
each(this.store.containers, container => {
const target =
container.node === document.documentElement ? window : container.node
target.removeEventListener('scroll', this.delegate)
target.removeEventListener('resize', this.delegate)
})
/**
* Clear all data from the store
*/
this.store = {
containers: {},
elements: {},
history: [],
sequences: {}
}
}
================================================
FILE: src/instance/methods/reveal.js
================================================
import tealight from 'tealight'
import deepAssign from '../../utils/deep-assign'
import each from '../../utils/each'
import isMobile from '../../utils/is-mobile'
import logger from '../../utils/logger'
import nextUniqueId from '../../utils/next-unique-id'
import defaults from '../defaults'
import initialize from '../functions/initialize'
import { Sequence } from '../functions/sequence'
import style, { applyStyle } from '../functions/style'
import clean from '../methods/clean'
export default function reveal(target, options = {}, syncing = false) {
const containerBuffer = []
let sequence
let interval = options.interval || defaults.interval
try {
if (interval) {
sequence = new Sequence(interval)
}
const nodes = tealight(target)
if (!nodes.length) {
throw new Error('Invalid reveal target.')
}
const elements = nodes.reduce((elementBuffer, elementNode) => {
const element = {}
const existingId = elementNode.getAttribute('data-sr-id')
if (existingId) {
deepAssign(element, this.store.elements[existingId])
/**
* In order to prevent previously generated styles
* from throwing off the new styles, the style tag
* has to be reverted to its pre-reveal state.
*/
applyStyle(element.node, element.styles.inline.computed)
} else {
element.id = nextUniqueId()
element.node = elementNode
element.seen = false
element.revealed = false
element.visible = false
}
const config = deepAssign({}, element.config || this.defaults, options)
if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
if (existingId) {
clean.call(this, element)
}
return elementBuffer // skip elements that are disabled
}
const containerNode = tealight(config.container)[0]
if (!containerNode) {
throw new Error('Invalid container.')
}
if (!containerNode.contains(elementNode)) {
return elementBuffer // skip elements found outside the container
}
let containerId
{
containerId = getContainerId(
containerNode,
containerBuffer,
this.store.containers
)
if (containerId === null) {
containerId = nextUniqueId()
containerBuffer.push({ id: containerId, node: containerNode })
}
}
element.config = config
element.containerId = containerId
element.styles = style(element)
if (sequence) {
element.sequence = {
id: sequence.id,
index: sequence.members.length
}
sequence.members.push(element.id)
}
elementBuffer.push(element)
return elementBuffer
}, [])
/**
* Modifying the DOM via setAttribute needs to be handled
* separately from reading computed styles in the map above
* for the browser to batch DOM changes (limiting reflows)
*/
each(elements, element => {
this.store.elements[element.id] = element
element.node.setAttribute('data-sr-id', element.id)
})
} catch (e) {
return logger.call(this, 'Reveal failed.', e.message)
}
/**
* Now that element set-up is complete...
* Let’s commit any container and sequence data we have to the store.
*/
each(containerBuffer, container => {
this.store.containers[container.id] = {
id: container.id,
node: container.node
}
})
if (sequence) {
this.store.sequences[sequence.id] = sequence
}
/**
* If reveal wasn't invoked by sync, we want to
* make sure to add this call to the history.
*/
if (syncing !== true) {
this.store.history.push({ target, options })
/**
* Push initialization to the event queue, giving
* multiple reveal calls time to be interpreted.
*/
if (this.initTimeout) {
window.clearTimeout(this.initTimeout)
}
this.initTimeout = window.setTimeout(initialize.bind(this), 0)
}
}
function getContainerId(node, ...collections) {
let id = null
each(collections, collection => {
each(collection, container => {
if (id === null && container.node === node) {
id = container.id
}
})
})
return id
}
================================================
FILE: src/instance/methods/sync.js
================================================
import initialize from '../functions/initialize'
import each from '../../utils/each'
import reveal from './reveal'
/**
* Re-runs the reveal method for each record stored in history,
* for capturing new content asynchronously loaded into the DOM.
*/
export default function sync() {
each(this.store.history, record => {
reveal.call(this, record.target, record.options, true)
})
initialize.call(this)
}
================================================
FILE: src/instance/mount.js
================================================
function failure() {
document.documentElement.classList.remove('sr')
return {
clean() {},
destroy() {},
reveal() {},
sync() {},
get noop() {
return true
}
}
}
function success() {
document.documentElement.classList.add('sr')
if (document.body) {
document.body.style.height = '100%'
} else {
document.addEventListener('DOMContentLoaded', () => {
document.body.style.height = '100%'
})
}
}
export default { success, failure }
================================================
FILE: src/polyfills/math-sign.js
================================================
export const polyfill = x => (x > 0) - (x < 0) || +x
export default Math.sign || polyfill
================================================
FILE: src/utils/deep-assign.js
================================================
import isObject from './is-object'
import each from './each'
export default function deepAssign(target, ...sources) {
if (isObject(target)) {
each(sources, source => {
each(source, (data, key) => {
if (isObject(data)) {
if (!target[key] || !isObject(target[key])) {
target[key] = {}
}
deepAssign(target[key], data)
} else {
target[key] = data
}
})
})
return target
} else {
throw new TypeError('Target must be an object literal.')
}
}
================================================
FILE: src/utils/each.js
================================================
import isObject from './is-object'
export default function each(collection, callback) {
if (isObject(collection)) {
const keys = Object.keys(collection)
return keys.forEach(key => callback(collection[key], key, collection))
}
if (collection instanceof Array) {
return collection.forEach((item, i) => callback(item, i, collection))
}
throw new TypeError('Expected either an array or object literal.')
}
================================================
FILE: src/utils/get-geometry.js
================================================
export default function getGeometry(target, isContainer) {
/**
* We want to ignore padding and scrollbars for container elements.
* More information here: https://goo.gl/vOZpbz
*/
const height = isContainer ? target.node.clientHeight : target.node.offsetHeight
const width = isContainer ? target.node.clientWidth : target.node.offsetWidth
let offsetTop = 0
let offsetLeft = 0
let node = target.node
do {
if (!isNaN(node.offsetTop)) {
offsetTop += node.offsetTop
}
if (!isNaN(node.offsetLeft)) {
offsetLeft += node.offsetLeft
}
node = node.offsetParent
} while (node)
return {
bounds: {
top: offsetTop,
right: offsetLeft + width,
bottom: offsetTop + height,
left: offsetLeft
},
height,
width
}
}
================================================
FILE: src/utils/get-prefixed-css-prop.js
================================================
const getPrefixedCssProp = (() => {
let properties = {}
const style = document.documentElement.style
function getPrefixedCssProperty(name, source = style) {
if (name && typeof name === 'string') {
if (properties[name]) {
return properties[name]
}
if (typeof source[name] === 'string') {
return (properties[name] = name)
}
if (typeof source[`-webkit-${name}`] === 'string') {
return (properties[name] = `-webkit-${name}`)
}
throw new RangeError(`Unable to find "${name}" style property.`)
}
throw new TypeError('Expected a string.')
}
getPrefixedCssProperty.clearCache = () => (properties = {})
return getPrefixedCssProperty
})()
export default getPrefixedCssProp
================================================
FILE: src/utils/get-scrolled.js
================================================
export default function getScrolled(container) {
let top, left
if (container.node === document.documentElement) {
top = window.pageYOffset
left = window.pageXOffset
} else {
top = container.node.scrollTop
left = container.node.scrollLeft
}
return { top, left }
}
================================================
FILE: src/utils/is-element-visible.js
================================================
export default function isElementVisible(element = {}) {
const container = this.store.containers[element.containerId]
if (!container) return
const viewFactor = Math.max(0, Math.min(1, element.config.viewFactor))
const viewOffset = element.config.viewOffset
const elementBounds = {
top: element.geometry.bounds.top + element.geometry.height * viewFactor,
right: element.geometry.bounds.right - element.geometry.width * viewFactor,
bottom: element.geometry.bounds.bottom - element.geometry.height * viewFactor,
left: element.geometry.bounds.left + element.geometry.width * viewFactor
}
const containerBounds = {
top: container.geometry.bounds.top + container.scroll.top + viewOffset.top,
right: container.geometry.bounds.right + container.scroll.left - viewOffset.right,
bottom:
container.geometry.bounds.bottom + container.scroll.top - viewOffset.bottom,
left: container.geometry.bounds.left + container.scroll.left + viewOffset.left
}
return (
(elementBounds.top < containerBounds.bottom &&
elementBounds.right > containerBounds.left &&
elementBounds.bottom > containerBounds.top &&
elementBounds.left < containerBounds.right) ||
element.styles.position === 'fixed'
)
}
================================================
FILE: src/utils/is-mobile.js
================================================
export default function isMobile(agent = navigator.userAgent) {
return /Android|iPhone|iPad|iPod/i.test(agent)
}
================================================
FILE: src/utils/is-object.js
================================================
export default function isObject(x) {
return (
x !== null &&
x instanceof Object &&
(x.constructor === Object ||
Object.prototype.toString.call(x) === '[object Object]')
)
}
================================================
FILE: src/utils/is-transform-supported.js
================================================
export default function isTransformSupported() {
const style = document.documentElement.style
return 'transform' in style || 'WebkitTransform' in style
}
================================================
FILE: src/utils/is-transition-supported.js
================================================
export default function isTransitionSupported() {
const style = document.documentElement.style
return 'transition' in style || 'WebkitTransition' in style
}
================================================
FILE: src/utils/logger.js
================================================
export default function logger(message, ...details) {
if (this.constructor.debug && console) {
let report = `%cScrollReveal: ${message}`
details.forEach(detail => (report += `\n — ${detail}`))
console.log(report, 'color: #ea654b;') // eslint-disable-line no-console
}
}
================================================
FILE: src/utils/next-unique-id.js
================================================
const nextUniqueId = (() => {
let uid = 0
return () => uid++
})()
export default nextUniqueId
================================================
FILE: test/instance/constructor.spec.js
================================================
import ScrollReveal from '../../src/instance/constructor'
import isMobile from '../../src/utils/is-mobile'
import { version } from '../../package.json'
describe('ScrollReveal', () => {
describe('Constructor', () => {
it('should return a new instance with `new` keyword', () => {
const sr = new ScrollReveal()
expect(sr).to.exist
})
it('should return a new instance without `new` keyword', () => {
const sr = ScrollReveal()
expect(sr).to.exist
})
it('should add the class `sr` to `<html>` element', () => {
document.documentElement.classList.remove('sr')
ScrollReveal()
const result = document.documentElement.classList.contains('sr')
expect(result).to.be.true
})
it('should add `height: 100%` to `<body>` element', () => {
document.body.style.height = 'auto'
ScrollReveal()
const result = document.body.style.height === '100%'
expect(result).to.be.true
})
it('should return a noop instance when not supported', () => {
const stubs = [
sinon.stub(console, 'log'),
sinon.stub(ScrollReveal, 'isSupported')
]
const sr = ScrollReveal()
stubs.forEach(stub => stub.restore())
expect(sr.noop).to.be.true
})
it('should return a noop instance when device is disabled', () => {
isMobile()
? expect(ScrollReveal({ mobile: false }).noop).to.be.true
: expect(ScrollReveal({ desktop: false }).noop).to.be.true
ScrollReveal({ desktop: true, mobile: true })
})
it('should return a noop instance when container is invalid', () => {
const stub = sinon.stub(console, 'log')
const sr = ScrollReveal({ container: null })
stub.restore()
expect(sr.noop).to.be.true
})
it('should return a noop instance when passed non-object options', () => {
const stub = sinon.stub(console, 'log')
let sr
{
sr = ScrollReveal(null)
expect(sr.noop).to.be.true
sr = ScrollReveal('foo')
expect(sr.noop).to.be.true
}
stub.restore()
})
it('should return a singleton', () => {
const A = ScrollReveal()
const B = ScrollReveal()
expect(A === B).to.be.true
})
it('should not update the defaults when re-invoked with invalid options', () => {
ScrollReveal({ duration: 1000 })
ScrollReveal(null)
expect(ScrollReveal().defaults.duration).to.equal(1000)
})
it('should update the defaults when re-invoked with valid options', () => {
ScrollReveal({ duration: 1000 })
ScrollReveal({ duration: 5000 })
expect(ScrollReveal().defaults.duration).to.equal(5000)
})
it('should have a static `debug` property', () => {
expect(ScrollReveal.debug).to.exist
expect(ScrollReveal.debug).to.be.a('boolean')
})
it('should accept boolean value for static `debug` property', () => {
ScrollReveal.debug = true
expect(ScrollReveal.debug).to.be.true
})
it('should ignore non-boolean values assigned to static `debug` property', () => {
ScrollReveal.debug = null
expect(ScrollReveal.debug).to.exist
expect(ScrollReveal.debug).to.be.a('boolean')
})
})
describe('Instance', () => {
const sr = new ScrollReveal()
it('should have a `clean` method', () => {
expect(sr.clean).to.exist
expect(sr.clean).to.be.a('function')
})
it('should have a `destroy` method', () => {
expect(sr.destroy).to.exist
expect(sr.destroy).to.be.a('function')
})
it('should have a `reveal` method', () => {
expect(sr.reveal).to.exist
expect(sr.reveal).to.be.a('function')
})
it('should have a `sync` method', () => {
expect(sr.sync).to.exist
expect(sr.sync).to.be.a('function')
})
it('should have a `delegate` property', () => {
expect(sr.delegate).to.exist
expect(sr.delegate).to.be.a('function')
})
it('should have a `version` property', () => {
expect(sr.version).to.exist
expect(sr.version).to.be.equal(version)
})
it('should have a `noop` property set to `false`', () => {
expect(sr.noop).to.exist
expect(sr.noop).to.be.false
})
})
describe('Non-operational Instance', () => {
const stubs = [
sinon.stub(console, 'log'),
sinon.stub(ScrollReveal, 'isSupported')
]
const sr = ScrollReveal()
stubs.forEach(stub => stub.restore())
it('should have a `clean` method', () => {
expect(sr.clean).to.exist
expect(sr.clean).to.be.a('function')
})
it('should have a `destroy` method', () => {
expect(sr.destroy).to.exist
expect(sr.destroy).to.be.a('function')
})
it('should have a `reveal` method', () => {
expect(sr.reveal).to.exist
expect(sr.reveal).to.be.a('function')
})
it('should have a `sync` method', () => {
expect(sr.sync).to.exist
expect(sr.sync).to.be.a('function')
})
it('should have a `noop` property set to `true`', () => {
expect(sr.noop).to.exist
expect(sr.noop).to.be.true
})
})
})
================================================
FILE: test/karma.conf.js
================================================
const rollupPlugins = [
require('rollup-plugin-json')(),
require('rollup-plugin-node-resolve')({ jsnext: true, main: true }),
require('rollup-plugin-buble')()
]
if (process.env.COVERAGE) {
rollupPlugins.push(
require('rollup-plugin-istanbul')({
exclude: [
'../package.json',
'../src/index.js',
'./**/*.spec.js',
'**/node_modules/**'
],
instrumenterConfig: {
embedSource: true
}
})
)
}
module.exports = function(karma) {
karma.set({
frameworks: ['mocha', 'sinon-chai'],
preprocessors: {
'./**/*.spec.js': ['rollup']
},
files: [{ pattern: './**/*.spec.js', watched: false }],
rollupPreprocessor: {
plugins: rollupPlugins,
output: {
format: 'iife',
name: 'ScrollReveal',
sourcemap: 'inline'
}
},
colors: true,
concurrency: 10,
logLevel: karma.LOG_ERROR,
singleRun: true,
browserDisconnectTolerance: 1,
browserDisconnectTimeout: 60 * 1000,
browserNoActivityTimeout: 60 * 1000,
// browserNoActivityTimeout: 60 * 1000 * 10 * 6, // dev tools debugging
captureTimeout: 4 * 60 * 1000
})
if (process.env.TRAVIS) {
if (process.env.COVERAGE) {
karma.set({
autoWatch: false,
browsers: ['ChromeHeadless'],
coverageReporter: {
type: 'lcovonly',
dir: 'coverage/'
},
reporters: ['mocha', 'coverage', 'coveralls']
})
} else {
const customLaunchers = require('./sauce.conf')
karma.set({
autoWatch: false,
browsers: Object.keys(customLaunchers),
customLaunchers,
reporters: ['dots', 'saucelabs'],
hostname: 'localsauce',
sauceLabs: {
testName: 'ScrollReveal',
build: process.env.TRAVIS_BUILD_NUMBER || 'manual',
tunnelIdentifier: process.env.TRAVIS_BUILD_NUMBER || 'autoGeneratedTunnelID',
recordVideo: true,
connectOptions: {
tunnelDomains: 'localsauce' // because Android 8 has an SSL error?
}
}
})
}
} else {
karma.set({
browsers: ['ChromeHeadless'],
// browsers: ['Chrome'], // dev tools debugging
coverageReporter: {
type: 'lcov',
dir: '../.ignore/coverage/'
},
reporters: ['mocha', 'coverage']
})
}
}
================================================
FILE: test/polyfills/math-sign.spec.js
================================================
import { polyfill } from '../../src/polyfills/math-sign'
describe('Polyfills', () => {
describe('mathSign()', () => {
it('should be a function', () => {
expect(polyfill).to.be.a('function')
})
it('should return -1 when passed values smaller than 0', () => {
expect(polyfill(-500)).to.equal(-1)
})
it('should return 1 when passed values larger than 0', () => {
expect(polyfill(500)).to.equal(1)
})
it('should return 1 when passed true', () => {
expect(polyfill(true)).to.equal(1)
})
it('should return -0 when passed -0', () => {
expect(polyfill(-0)).to.equal(-0)
})
it('should return 0 when passed 0', () => {
expect(polyfill(0)).to.equal(0)
})
it('should return 0 when passed falsey values', () => {
expect(polyfill(false)).to.equal(0)
expect(polyfill('')).to.equal(0)
expect(polyfill([])).to.equal(0)
expect(polyfill(null)).to.equal(0)
})
it('should return NaN when passed non-falsey non-numbers', () => {
expect(polyfill('foo')).to.be.NaN
expect(polyfill({})).to.be.NaN
expect(polyfill([1, 2, 3])).to.be.NaN
})
})
})
================================================
FILE: test/sauce.conf.js
================================================
let launchers = {}
let mobileLaunchers = [
['iOS', '10.3', 'Safari', 'iPhone 7 Simulator', '1.9.1'],
['iOS', '11.3', 'Safari', 'iPhone 7 Simulator', '1.9.1'],
['iOS', '12.2', 'Safari', 'iPhone 7 Simulator', '1.13.0'],
['iOS', '13.0', 'Safari', 'iPhone 7 Simulator', '1.15.0'],
['Android', '5.1', 'Browser', 'Android Emulator', '1.15.0'],
['Android', '6.0', 'Chrome', 'Android Emulator', '1.15.0'],
['Android', '8.0', 'Chrome', 'Android Emulator', '1.15.0']
]
for (let [platform, version, browser, device, appium] of mobileLaunchers) {
let launcher = `sl_${platform}_${version}_${browser}`
.replace(/[^a-z0-9]/gi, '_')
.toLowerCase()
launchers[launcher] = {
name: `${browser}, ${platform} ${version}`,
platformName: platform,
platformVersion: version,
browserName: browser,
deviceName: device,
deviceOrientation: 'portrait',
appiumVersion: appium
}
}
let desktopLaunchers = [
['Windows 8.1', 'Internet Explorer', '11.0'],
['Windows 8', 'Internet Explorer', '10.0'],
['macOS 10.12', 'Safari', '11.0'],
['OS X 10.11', 'Safari', '10.0'],
['OS X 10.11', 'Safari', '9.0']
]
for (let [platform, browser, version] of desktopLaunchers) {
let launcher = `sl_${platform}_${browser}_${version}`
.replace(/[^a-z0-9]/gi, '_')
.toLowerCase()
launchers[launcher] = {
name: `${browser} ${version}, ${platform}`,
browserName: browser,
version,
platform
}
}
for (let browser of ['Chrome', 'Firefox', 'MicrosoftEdge']) {
let pastVersions = 3
do {
pastVersions--
let postfix = pastVersions > 0 ? `-${pastVersions}` : ''
let version = 'latest' + postfix
let browserName = browser === 'MicrosoftEdge' ? 'Edge' : browser
let launcher = `sl_win10_${browser}_latest${postfix}`.replace(/-/g, '_').toLowerCase()
launchers[launcher] = {
name: `${browserName} ${version}, Windows 10`,
browserName: browser,
version,
platform: 'Windows 10'
}
} while (pastVersions)
}
for (let launcher in launchers) {
launchers[launcher].base = 'SauceLabs'
}
module.exports = launchers
================================================
FILE: test/timeout.spec.js
================================================
// describe('suite delay for DOM inspection', function () {
// it('should delay by 10 minutes', function (done) {
// document.documentElement.style = 'background-color: #eee; height: 100%'
// const time = 1000 * 60 * 10 * 6
// this.timeout(time)
// setTimeout(done, time - 500)
// })
// })
================================================
FILE: test/utils/deep-assign.spec.js
================================================
import deepAssign from '../../src/utils/deep-assign'
describe('Utilities', () => {
describe('deepAssign()', () => {
it('should assign source values to target object', () => {
const target = { foo: 'bar', bun: 'baz' }
const source = { foo: 'bonk!', bif: 'baff' }
const goal = { foo: 'bonk!', bun: 'baz', bif: 'baff' }
deepAssign(target, source)
expect(target).to.deep.equal(goal)
})
it('should assign nested source values to target object', () => {
// each property tests a
// different execution path
const target = {
foo: 'initial',
bar: 'initial',
kel: { pow: 'pop' },
zad: null
}
const source = {
foo: 'bonk!',
bar: { baz: 'baff' },
kel: { pow: 'lol' },
zad: { min: 'max' }
}
const goal = {
foo: 'bonk!',
bar: { baz: 'baff' },
kel: { pow: 'lol' },
zad: { min: 'max' }
}
deepAssign(target, source)
expect(target).to.deep.equal(goal)
})
it('should accept multiple sources', () => {
const target = { foo: 'bar', bun: 'baz' }
const source1 = { foo: 'bonk!', bif: 'baff' }
const source2 = { foo: 'pow!' }
const goal = { foo: 'pow!', bun: 'baz', bif: 'baff' }
deepAssign(target, source1, source2)
expect(target).to.deep.equal(goal)
})
it('should throw a type error when not passed an object literal', () => {
let caught
try {
deepAssign(null, null)
} catch (error) {
caught = error
}
expect(caught).to.exist
expect(caught).to.be.an.instanceof(TypeError)
})
})
})
================================================
FILE: test/utils/each.spec.js
================================================
import each from '../../src/utils/each'
describe('Utilities', () => {
describe('each()', () => {
function Fixture() {
this.foo = 'bar'
this.baz = 'bun'
}
describe('if passed an object literal...', () => {
it('should invoke callback for each property', () => {
const fixture = new Fixture()
const spy = sinon.spy()
each(fixture, spy)
expect(spy).to.have.been.calledTwice
})
it('should ignore properties on the prototype chain', () => {
Fixture.prototype.biff = 'baff'
const fixture = new Fixture()
const spy = sinon.spy()
each(fixture, spy)
expect(spy).to.have.been.calledTwice
})
it('should pass the value, key and collection to the callback', () => {
const fixture = new Fixture()
let _value, _key, _collection
each(fixture, (value, key, collection) => {
_value = value
_key = key
_collection = collection
})
expect(_value).to.equal('bun')
expect(_key).to.equal('baz')
expect(_collection).to.deep.equal(fixture)
})
})
describe('if passed an array...', () => {
const fixture = ['apple', 'orange', 'banana']
it('should invoke callback for each value', () => {
const spy = sinon.spy()
each(fixture, spy)
expect(spy).to.have.been.calledThrice
})
it('should pass the value, index and collection to the callback', () => {
let _value, _index, _collection
each(fixture, (value, index, collection) => {
_value = value
_index = index
_collection = collection
})
expect(_value).to.equal('banana')
expect(_index).to.equal(2)
expect(_collection).to.deep.equal(fixture)
})
})
describe('else', () => {
it('should throw a type error when passed an invalid collection', () => {
let caught
try {
each(null, () => {})
} catch (error) {
caught = error
}
expect(caught).to.exist
expect(caught).to.be.an.instanceof(TypeError)
})
})
})
})
================================================
FILE: test/utils/get-prefixed-css-prop.spec.js
================================================
import getPrefixedCssProp from '../../src/utils/get-prefixed-css-prop'
describe('Utilities', () => {
describe('getPrefixedCssProp()', () => {
beforeEach('clear cache', () => {
getPrefixedCssProp.clearCache()
})
it('should return unprefixed properties before prefixed', () => {
const source = {
transform: '',
'-webkit-transform': ''
}
const result = getPrefixedCssProp('transform', source)
expect(result).to.equal('transform')
})
it('should return prefixed property names', () => {
const source = { '-webkit-transform': '' }
const result = getPrefixedCssProp('transform', source)
expect(result).to.equal('-webkit-transform')
})
it('should return property names from cache when available', () => {
const source = { '-webkit-transform': '' }
getPrefixedCssProp('transform', source)
const result = getPrefixedCssProp('transform', {})
expect(result).to.equal('-webkit-transform')
})
it('should throw a range error when no property is found', () => {
let caught
try {
getPrefixedCssProp('transform', {})
} catch (error) {
caught = error
}
expect(caught).to.exist
expect(caught).to.be.an.instanceof(RangeError)
})
it('should throw a type error if not passed a string', () => {
let caught
try {
getPrefixedCssProp(null)
} catch (error) {
caught = error
}
expect(caught).to.exist
expect(caught).to.be.an.instanceof(TypeError)
})
})
})
================================================
FILE: test/utils/is-mobile.spec.js
================================================
import isMobile from '../../src/utils/is-mobile'
describe('Utilities', () => {
describe('isMobile()', () => {
it('should return true when passed a mobile user agent', () => {
const android = `Mozilla/5.0 (Linux; U; Android 4.2; en-us;
Android SDK built for x86 Build/JOP40C) AppleWebKit/534.30
(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
const iPhone = `Mozilla/5.0 (iPhone; CPU iPhone OS 10_10_5 like Mac OS X)
AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B411 Safari/600.1.4`
expect(isMobile(android)).to.be.true
expect(isMobile(iPhone)).to.be.true
})
it('should return false when passed a desktop user agent', () => {
const chrome = `Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/50.0.2661.75 Safari/537.36`
const firefox =
'Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101 Firefox/45.0'
const ie10 = `Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1;
WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET4.0C; .NET4.0E)`
expect(isMobile(chrome)).to.be.false
expect(isMobile(firefox)).to.be.false
expect(isMobile(ie10)).to.be.false
})
it('should work when not passed an explicit user agent', () => {
expect(isMobile()).to.be.a('boolean')
})
})
})
================================================
FILE: test/utils/is-object.spec.js
================================================
import isObject from '../../src/utils/is-object'
describe('Utilities', () => {
describe('isObject()', () => {
it('should return true when passed an object literal', () => {
const result = isObject({})
expect(result).to.be.true
})
it('should return false when passed a function', () => {
const result = isObject(() => {})
expect(result).to.be.false
})
it('should return false when passed an array', () => {
const result = isObject([])
expect(result).to.be.false
})
it('should return false when passed null', () => {
const result = isObject(null)
expect(result).to.be.false
})
it('should return false when passed undefined', () => {
const result = isObject(undefined)
expect(result).to.be.false
})
})
})
================================================
FILE: test/utils/is-transform-supported.spec.js
================================================
import isTransformSupported from '../../src/utils/is-transform-supported'
describe('Utilities', () => {
describe('isTransformSupported()', () => {
it('should return true', () => {
expect(isTransformSupported()).to.be.true
})
})
})
================================================
FILE: test/utils/is-transition-supported.spec.js
================================================
import isTransitionSupported from '../../src/utils/is-transition-supported'
describe('Utilities', () => {
describe('isTransitionSupported()', () => {
it('should return true', () => {
expect(isTransitionSupported()).to.be.true
})
})
})
================================================
FILE: test/utils/logger.spec.js
================================================
import logger from '../../src/utils/logger'
describe('Utilities', () => {
describe('logger()', () => {
const mock = { constructor: { debug: true } }
let spy
let stub
beforeEach('stub console log', () => {
spy = sinon.spy()
stub = sinon.stub(console, 'log').callsFake(spy)
})
it('should invoke console.log', () => {
logger.call(mock)
expect(spy).to.have.been.called
})
it('should prepend output with `ScrollReveal: `', () => {
logger.call(mock, 'test')
const result = '%cScrollReveal: test'
const style = 'color: #ea654b;'
expect(spy).to.have.been.calledWith(result, style)
})
it('should accept multiple arguments as message details', () => {
logger.call(mock, 'message', 'detail one', 'detail two')
const result = '%cScrollReveal: message\n — detail one\n — detail two'
const style = 'color: #ea654b;'
expect(spy).to.have.been.calledWith(result, style)
})
afterEach('restore console log', () => stub.restore())
})
})
================================================
FILE: test/utils/next-unique-id.spec.js
================================================
import nextUniqueId from '../../src/utils/next-unique-id'
describe('Utilities', () => {
describe('nextUniqueId()', () => {
it('should start at 0', () => {
const result = nextUniqueId()
expect(result).to.equal(0)
})
it('should increment by 1', () => {
const result = nextUniqueId()
expect(result).to.equal(1)
})
it('should return a number', () => {
const result = nextUniqueId()
expect(result).to.be.a('number')
})
})
})
gitextract_vf2236hi/
├── .eslintrc.json
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── build/
│ ├── rollup.conf.banner.js
│ └── rollup.conf.js
├── dist/
│ ├── scrollreveal.es.js
│ └── scrollreveal.js
├── package.json
├── src/
│ ├── index.js
│ ├── instance/
│ │ ├── constructor.js
│ │ ├── defaults.js
│ │ ├── functions/
│ │ │ ├── animate.js
│ │ │ ├── delegate.js
│ │ │ ├── initialize.js
│ │ │ ├── rinse.js
│ │ │ ├── sequence.js
│ │ │ └── style.js
│ │ ├── methods/
│ │ │ ├── clean.js
│ │ │ ├── destroy.js
│ │ │ ├── reveal.js
│ │ │ └── sync.js
│ │ └── mount.js
│ ├── polyfills/
│ │ └── math-sign.js
│ └── utils/
│ ├── deep-assign.js
│ ├── each.js
│ ├── get-geometry.js
│ ├── get-prefixed-css-prop.js
│ ├── get-scrolled.js
│ ├── is-element-visible.js
│ ├── is-mobile.js
│ ├── is-object.js
│ ├── is-transform-supported.js
│ ├── is-transition-supported.js
│ ├── logger.js
│ └── next-unique-id.js
└── test/
├── instance/
│ └── constructor.spec.js
├── karma.conf.js
├── polyfills/
│ └── math-sign.spec.js
├── sauce.conf.js
├── timeout.spec.js
└── utils/
├── deep-assign.spec.js
├── each.spec.js
├── get-prefixed-css-prop.spec.js
├── is-mobile.spec.js
├── is-object.spec.js
├── is-transform-supported.spec.js
├── is-transition-supported.spec.js
├── logger.spec.js
└── next-unique-id.spec.js
SYMBOL INDEX (114 symbols across 27 files)
FILE: dist/scrollreveal.es.js
function failure (line 49) | function failure() {
function success (line 63) | function success() {
function isObject (line 77) | function isObject(x) {
function each (line 86) | function each(collection, callback) {
function logger (line 97) | function logger(message) {
function rinse (line 108) | function rinse() {
function getPrefixedCssProperty (line 188) | function getPrefixedCssProperty(name, source) {
function style (line 211) | function style(element) {
function applyStyle (line 428) | function applyStyle (el, declaration) {
function clean (line 439) | function clean(target) {
function destroy (line 470) | function destroy() {
function deepAssign (line 502) | function deepAssign(target) {
function isMobile (line 525) | function isMobile(agent) {
function initialize (line 536) | function initialize() {
function animate (line 578) | function animate(element, force) {
function triggerReveal (line 599) | function triggerReveal(element, delayed) {
function triggerReset (line 615) | function triggerReset(element) {
function registerCallbacks (line 627) | function registerCallbacks(element, isDelayed) {
function sequence (line 662) | function sequence(element, pristine) {
function Sequence (line 725) | function Sequence(interval) {
function SequenceModel (line 741) | function SequenceModel(seq, prop, store) {
function cue (line 769) | function cue(seq, i, direction, pristine) {
function reveal (line 786) | function reveal(target, options, syncing) {
function getContainerId (line 917) | function getContainerId(node) {
function sync (line 936) | function sync() {
function getGeometry (line 949) | function getGeometry(target, isContainer) {
function getScrolled (line 983) | function getScrolled(container) {
function isElementVisible (line 995) | function isElementVisible(element) {
function delegate (line 1028) | function delegate(
function isTransformSupported (line 1078) | function isTransformSupported() {
function isTransitionSupported (line 1083) | function isTransitionSupported() {
function ScrollReveal (line 1099) | function ScrollReveal(options) {
FILE: dist/scrollreveal.js
function failure (line 51) | function failure() {
function success (line 65) | function success() {
function isDomNode (line 102) | function isDomNode(x) {
function isDomNodeList (line 135) | function isDomNodeList(x) {
function tealight (line 172) | function tealight(target, context) {
function isObject (line 189) | function isObject(x) {
function each (line 198) | function each(collection, callback) {
function logger (line 209) | function logger(message) {
function rinse (line 220) | function rinse() {
function format (line 334) | function format(source) {
function identity (line 362) | function identity() {
function multiply (line 383) | function multiply(m, x) {
function parse (line 414) | function parse(source) {
function rotateX (line 431) | function rotateX(angle) {
function rotateY (line 448) | function rotateY(angle) {
function rotateZ (line 465) | function rotateZ(angle) {
function scale (line 485) | function scale(scalar, scalarY) {
function translateX (line 500) | function translateX(distance) {
function translateY (line 512) | function translateY(distance) {
function getPrefixedCssProperty (line 522) | function getPrefixedCssProperty(name, source) {
function style (line 545) | function style(element) {
function applyStyle (line 762) | function applyStyle (el, declaration) {
function clean (line 773) | function clean(target) {
function destroy (line 804) | function destroy() {
function deepAssign (line 836) | function deepAssign(target) {
function isMobile (line 859) | function isMobile(agent) {
function initialize (line 870) | function initialize() {
function animate (line 912) | function animate(element, force) {
function triggerReveal (line 933) | function triggerReveal(element, delayed) {
function triggerReset (line 949) | function triggerReset(element) {
function registerCallbacks (line 961) | function registerCallbacks(element, isDelayed) {
function sequence (line 996) | function sequence(element, pristine) {
function Sequence (line 1059) | function Sequence(interval) {
function SequenceModel (line 1075) | function SequenceModel(seq, prop, store) {
function cue (line 1103) | function cue(seq, i, direction, pristine) {
function reveal (line 1120) | function reveal(target, options, syncing) {
function getContainerId (line 1251) | function getContainerId(node) {
function sync (line 1270) | function sync() {
function getGeometry (line 1325) | function getGeometry(target, isContainer) {
function getScrolled (line 1359) | function getScrolled(container) {
function isElementVisible (line 1371) | function isElementVisible(element) {
function delegate (line 1404) | function delegate(
function isTransformSupported (line 1454) | function isTransformSupported() {
function isTransitionSupported (line 1459) | function isTransitionSupported() {
function ScrollReveal (line 1475) | function ScrollReveal(options) {
FILE: src/instance/constructor.js
function ScrollReveal (line 30) | function ScrollReveal(options = {}) {
FILE: src/instance/defaults.js
method afterReset (line 28) | afterReset() {}
method afterReveal (line 29) | afterReveal() {}
method beforeReset (line 30) | beforeReset() {}
method beforeReveal (line 31) | beforeReveal() {}
FILE: src/instance/functions/animate.js
function animate (line 4) | function animate(element, force = {}) {
function triggerReveal (line 23) | function triggerReveal(element, delayed) {
function triggerReset (line 39) | function triggerReset(element) {
function registerCallbacks (line 51) | function registerCallbacks(element, isDelayed) {
FILE: src/instance/functions/delegate.js
function delegate (line 10) | function delegate(
FILE: src/instance/functions/initialize.js
function initialize (line 5) | function initialize() {
FILE: src/instance/functions/rinse.js
function rinse (line 4) | function rinse() {
FILE: src/instance/functions/sequence.js
function sequence (line 5) | function sequence(element, pristine = this.pristine) {
function Sequence (line 66) | function Sequence(interval) {
function SequenceModel (line 82) | function SequenceModel(seq, prop, store) {
function cue (line 108) | function cue(seq, i, direction, pristine) {
FILE: src/instance/functions/style.js
function style (line 13) | function style(element) {
function applyStyle (line 226) | function applyStyle (el, declaration) {
FILE: src/instance/methods/clean.js
function clean (line 7) | function clean(target) {
FILE: src/instance/methods/destroy.js
function destroy (line 4) | function destroy() {
FILE: src/instance/methods/reveal.js
function reveal (line 13) | function reveal(target, options = {}, syncing = false) {
function getContainerId (line 140) | function getContainerId(node, ...collections) {
FILE: src/instance/methods/sync.js
function sync (line 9) | function sync() {
FILE: src/instance/mount.js
function failure (line 1) | function failure() {
function success (line 15) | function success() {
FILE: src/utils/deep-assign.js
function deepAssign (line 4) | function deepAssign(target, ...sources) {
FILE: src/utils/each.js
function each (line 3) | function each(collection, callback) {
FILE: src/utils/get-geometry.js
function getGeometry (line 1) | function getGeometry(target, isContainer) {
FILE: src/utils/get-prefixed-css-prop.js
function getPrefixedCssProperty (line 5) | function getPrefixedCssProperty(name, source = style) {
FILE: src/utils/get-scrolled.js
function getScrolled (line 1) | function getScrolled(container) {
FILE: src/utils/is-element-visible.js
function isElementVisible (line 1) | function isElementVisible(element = {}) {
FILE: src/utils/is-mobile.js
function isMobile (line 1) | function isMobile(agent = navigator.userAgent) {
FILE: src/utils/is-object.js
function isObject (line 1) | function isObject(x) {
FILE: src/utils/is-transform-supported.js
function isTransformSupported (line 1) | function isTransformSupported() {
FILE: src/utils/is-transition-supported.js
function isTransitionSupported (line 1) | function isTransitionSupported() {
FILE: src/utils/logger.js
function logger (line 1) | function logger(message, ...details) {
FILE: test/utils/each.spec.js
function Fixture (line 5) | function Fixture() {
Condensed preview — 54 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (174K chars).
[
{
"path": ".eslintrc.json",
"chars": 476,
"preview": "{\n\t\"env\": {\n\t\t\"es6\": true,\n\t\t\"amd\": true,\n\t\t\"browser\": true,\n\t\t\"mocha\": true,\n\t\t\"node\": true\n\t},\n\t\"extends\": \"eslint:rec"
},
{
"path": ".gitattributes",
"chars": 12,
"preview": "*.js eol=lf\n"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 474,
"preview": "<!--\n\nThanks for raising an issue! To help us help you, if you've found a bug please consider the following:\n\n* If you c"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 449,
"preview": "<!--\n\nThank you for creating a pull request. Before submitting, please note the following:\n\n* If your pull request imple"
},
{
"path": ".gitignore",
"chars": 70,
"preview": ".DS_Store\n.ignore/\n.vscode/\nnode_modules/\nyarn.lock\npackage-lock.json\n"
},
{
"path": ".travis.yml",
"chars": 271,
"preview": "language: node_js\ndist: trusty\nnode_js:\n - '9'\naddons:\n chrome: stable\n hosts: localsauce\nsudo: required\nbefore_scrip"
},
{
"path": "CHANGELOG.md",
"chars": 20576,
"preview": "# Change Log\n\n## [4.0.9] - 2021-03-04\n\n### Fixed\n\n- Styles applied using CSSOM don't drop `:` characters.\n\n## [4.0.8] - "
},
{
"path": "README.md",
"chars": 3425,
"preview": "<p align=\"center\">\n\t<a href=\"https://scrollrevealjs.org\" title=\"Visit ScrollReveal home page\">\n\t\t<img src=\"https://scrol"
},
{
"path": "build/rollup.conf.banner.js",
"chars": 445,
"preview": "const { version } = require('../package.json')\n\nconst banner = `/*! @license ScrollReveal v${version}\n\n\tCopyright 2021 F"
},
{
"path": "build/rollup.conf.js",
"chars": 623,
"preview": "import buble from 'rollup-plugin-buble'\nimport json from 'rollup-plugin-json'\nimport pkg from '../package.json'\nimport n"
},
{
"path": "dist/scrollreveal.es.js",
"chars": 31300,
"preview": "/*! @license ScrollReveal v4.0.9\n\n\tCopyright 2021 Fisssion LLC.\n\n\tLicensed under the GNU General Public License 3.0 for\n"
},
{
"path": "dist/scrollreveal.js",
"chars": 44627,
"preview": "/*! @license ScrollReveal v4.0.9\n\n\tCopyright 2021 Fisssion LLC.\n\n\tLicensed under the GNU General Public License 3.0 for\n"
},
{
"path": "package.json",
"chars": 2445,
"preview": "{\n\t\"name\": \"scrollreveal\",\n\t\"version\": \"4.0.9\",\n\t\"description\": \"Animate elements as they scroll into view\",\n\t\"homepage\""
},
{
"path": "src/index.js",
"chars": 92,
"preview": "import Constructor from './instance/constructor'\n\nConstructor()\n\nexport default Constructor\n"
},
{
"path": "src/instance/constructor.js",
"chars": 2907,
"preview": "import defaults from './defaults'\nimport mount from './mount'\n\nimport clean from './methods/clean'\nimport destroy from '"
},
{
"path": "src/instance/defaults.js",
"chars": 475,
"preview": "export default {\n\tdelay: 0,\n\tdistance: '0',\n\tduration: 600,\n\teasing: 'cubic-bezier(0.5, 0, 0, 1)',\n\tinterval: 0,\n\topacit"
},
{
"path": "src/instance/functions/animate.js",
"chars": 2361,
"preview": "import { applyStyle } from '../functions/style'\nimport clean from '../methods/clean'\n\nexport default function animate(el"
},
{
"path": "src/instance/functions/delegate.js",
"chars": 1486,
"preview": "import animate from './animate'\nimport sequence from './sequence'\nimport mathSign from '../../polyfills/math-sign'\nimpor"
},
{
"path": "src/instance/functions/initialize.js",
"chars": 1172,
"preview": "import each from '../../utils/each'\nimport { applyStyle } from '../functions/style'\nimport rinse from './rinse'\n\nexport "
},
{
"path": "src/instance/functions/rinse.js",
"chars": 1792,
"preview": "import $ from 'tealight'\nimport each from '../../utils/each'\n\nexport default function rinse() {\n\tconst struct = () => ({"
},
{
"path": "src/instance/functions/sequence.js",
"chars": 2951,
"preview": "import animate from './animate'\nimport each from '../../utils/each'\nimport nextUniqueId from '../../utils/next-unique-id"
},
{
"path": "src/instance/functions/style.js",
"chars": 6591,
"preview": "import {\n\tmultiply,\n\tparse,\n\trotateX,\n\trotateY,\n\trotateZ,\n\tscale,\n\ttranslateX,\n\ttranslateY\n} from 'rematrix'\nimport getP"
},
{
"path": "src/instance/methods/clean.js",
"chars": 840,
"preview": "import $ from 'tealight'\nimport each from '../../utils/each'\nimport logger from '../../utils/logger'\nimport rinse from '"
},
{
"path": "src/instance/methods/destroy.js",
"chars": 748,
"preview": "import each from '../../utils/each'\nimport { applyStyle } from '../functions/style'\n\nexport default function destroy() {"
},
{
"path": "src/instance/methods/reveal.js",
"chars": 3974,
"preview": "import tealight from 'tealight'\nimport deepAssign from '../../utils/deep-assign'\nimport each from '../../utils/each'\nimp"
},
{
"path": "src/instance/methods/sync.js",
"chars": 411,
"preview": "import initialize from '../functions/initialize'\nimport each from '../../utils/each'\nimport reveal from './reveal'\n\n/**\n"
},
{
"path": "src/instance/mount.js",
"chars": 459,
"preview": "function failure() {\n\tdocument.documentElement.classList.remove('sr')\n\n\treturn {\n\t\tclean() {},\n\t\tdestroy() {},\n\t\treveal("
},
{
"path": "src/polyfills/math-sign.js",
"chars": 90,
"preview": "export const polyfill = x => (x > 0) - (x < 0) || +x\nexport default Math.sign || polyfill\n"
},
{
"path": "src/utils/deep-assign.js",
"chars": 491,
"preview": "import isObject from './is-object'\nimport each from './each'\n\nexport default function deepAssign(target, ...sources) {\n\t"
},
{
"path": "src/utils/each.js",
"chars": 414,
"preview": "import isObject from './is-object'\n\nexport default function each(collection, callback) {\n\tif (isObject(collection)) {\n\t\t"
},
{
"path": "src/utils/get-geometry.js",
"chars": 750,
"preview": "export default function getGeometry(target, isContainer) {\n\t/**\n\t * We want to ignore padding and scrollbars for contain"
},
{
"path": "src/utils/get-prefixed-css-prop.js",
"chars": 712,
"preview": "const getPrefixedCssProp = (() => {\n\tlet properties = {}\n\tconst style = document.documentElement.style\n\n\tfunction getPre"
},
{
"path": "src/utils/get-scrolled.js",
"chars": 276,
"preview": "export default function getScrolled(container) {\n\tlet top, left\n\tif (container.node === document.documentElement) {\n\t\tto"
},
{
"path": "src/utils/is-element-visible.js",
"chars": 1216,
"preview": "export default function isElementVisible(element = {}) {\n\tconst container = this.store.containers[element.containerId]\n\t"
},
{
"path": "src/utils/is-mobile.js",
"chars": 114,
"preview": "export default function isMobile(agent = navigator.userAgent) {\n\treturn /Android|iPhone|iPad|iPod/i.test(agent)\n}\n"
},
{
"path": "src/utils/is-object.js",
"chars": 185,
"preview": "export default function isObject(x) {\n\treturn (\n\t\tx !== null &&\n\t\tx instanceof Object &&\n\t\t(x.constructor === Object ||\n"
},
{
"path": "src/utils/is-transform-supported.js",
"chars": 156,
"preview": "export default function isTransformSupported() {\n\tconst style = document.documentElement.style\n\treturn 'transform' in st"
},
{
"path": "src/utils/is-transition-supported.js",
"chars": 159,
"preview": "export default function isTransitionSupported() {\n\tconst style = document.documentElement.style\n\treturn 'transition' in "
},
{
"path": "src/utils/logger.js",
"chars": 278,
"preview": "export default function logger(message, ...details) {\n\tif (this.constructor.debug && console) {\n\t\tlet report = `%cScroll"
},
{
"path": "src/utils/next-unique-id.js",
"chars": 97,
"preview": "const nextUniqueId = (() => {\n\tlet uid = 0\n\treturn () => uid++\n})()\n\nexport default nextUniqueId\n"
},
{
"path": "test/instance/constructor.spec.js",
"chars": 4780,
"preview": "import ScrollReveal from '../../src/instance/constructor'\nimport isMobile from '../../src/utils/is-mobile'\nimport { vers"
},
{
"path": "test/karma.conf.js",
"chars": 2137,
"preview": "const rollupPlugins = [\n\trequire('rollup-plugin-json')(),\n\trequire('rollup-plugin-node-resolve')({ jsnext: true, main: t"
},
{
"path": "test/polyfills/math-sign.spec.js",
"chars": 1100,
"preview": "import { polyfill } from '../../src/polyfills/math-sign'\n\ndescribe('Polyfills', () => {\n\tdescribe('mathSign()', () => {\n"
},
{
"path": "test/sauce.conf.js",
"chars": 2027,
"preview": "let launchers = {}\n\nlet mobileLaunchers = [\n\t['iOS', '10.3', 'Safari', 'iPhone 7 Simulator', '1.9.1'],\n\t['iOS', '11.3', "
},
{
"path": "test/timeout.spec.js",
"chars": 300,
"preview": "// describe('suite delay for DOM inspection', function () {\n// \tit('should delay by 10 minutes', function (done) {\n// \t\t"
},
{
"path": "test/utils/deep-assign.spec.js",
"chars": 1519,
"preview": "import deepAssign from '../../src/utils/deep-assign'\n\ndescribe('Utilities', () => {\n\tdescribe('deepAssign()', () => {\n\t\t"
},
{
"path": "test/utils/each.spec.js",
"chars": 1952,
"preview": "import each from '../../src/utils/each'\n\ndescribe('Utilities', () => {\n\tdescribe('each()', () => {\n\t\tfunction Fixture() "
},
{
"path": "test/utils/get-prefixed-css-prop.spec.js",
"chars": 1455,
"preview": "import getPrefixedCssProp from '../../src/utils/get-prefixed-css-prop'\n\ndescribe('Utilities', () => {\n\tdescribe('getPref"
},
{
"path": "test/utils/is-mobile.spec.js",
"chars": 1301,
"preview": "import isMobile from '../../src/utils/is-mobile'\n\ndescribe('Utilities', () => {\n\tdescribe('isMobile()', () => {\n\t\tit('sh"
},
{
"path": "test/utils/is-object.spec.js",
"chars": 759,
"preview": "import isObject from '../../src/utils/is-object'\n\ndescribe('Utilities', () => {\n\tdescribe('isObject()', () => {\n\t\tit('sh"
},
{
"path": "test/utils/is-transform-supported.spec.js",
"chars": 241,
"preview": "import isTransformSupported from '../../src/utils/is-transform-supported'\n\ndescribe('Utilities', () => {\n\tdescribe('isTr"
},
{
"path": "test/utils/is-transition-supported.spec.js",
"chars": 245,
"preview": "import isTransitionSupported from '../../src/utils/is-transition-supported'\n\ndescribe('Utilities', () => {\n\tdescribe('is"
},
{
"path": "test/utils/logger.spec.js",
"chars": 986,
"preview": "import logger from '../../src/utils/logger'\n\ndescribe('Utilities', () => {\n\tdescribe('logger()', () => {\n\t\tconst mock = "
},
{
"path": "test/utils/next-unique-id.spec.js",
"chars": 455,
"preview": "import nextUniqueId from '../../src/utils/next-unique-id'\n\ndescribe('Utilities', () => {\n\tdescribe('nextUniqueId()', () "
}
]
About this extraction
This page contains the full source code of the jlmakes/scrollreveal GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 54 files (152.0 KB), approximately 43.4k tokens, and a symbol index with 114 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.