diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 15c68e78..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "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"] - } -} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 4964429a..00000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.js eol=lf diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 30d294d8..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,16 +0,0 @@ - - -### Environment - -* Operating System: -* Browser Version: -* ScrollReveal Version: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 38bc6e19..00000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/.gitignore b/.gitignore index c3faa557..934c601a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -.DS_Store -.ignore/ -.vscode/ -node_modules/ -yarn.lock -package-lock.json +## generic files to ignore +*~ +*.lock +*.DS_Store +node_modules +test/bundle.js diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4d5e39f4..00000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ac6307..8075a473 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,566 +1,108 @@ -# Change Log +###### scrollReveal.js Changelog -## [4.0.9] - 2021-03-04 +v0.1.3 May 26th, 2014 +------------------------- -### Fixed +- Add support for customizing starting element opacity (from kierzniak, see issue [#33](https://github.com/julianlloyd/scrollReveal.js/pull/33)) +- Implement RequestAnimationFrame (from pazguille, see issue [#46](https://github.com/julianlloyd/scrollReveal.js/pull/46)) +- Refactor how `styleBank` functions (from georgelee1, see issue [#38](https://github.com/julianlloyd/scrollReveal.js/pull/38)) -- Styles applied using CSSOM don't drop `:` characters. -## [4.0.8] - 2021-03-02 +v0.1.2 March 13th, 2014 +------------------------- -### Fixed +- Add support for elements with `position: fixed` (See issue [#35](https://github.com/julianlloyd/scrollReveal.js/pull/35)) +- Revise `genCSS()` method to create less greedy styles. (See issue [#37](https://github.com/julianlloyd/scrollReveal.js/pull/37)) -- Avoid Content Security Policy (CSP) violations. [@lambdacasserole](https://github.com/lambdacasserole) [#431](https://github.com/jlmakes/scrollreveal/pull/431) +v0.1.1 March 6th, 2014 +------------------------ -## [4.0.7] - 2020-07-15 +- Fixed a serious bug with `enter top` and `enter left` not correctly recognizing the pixel distance declared with the `move` keyword. **Fixes #13 and #31** (Thanks for catching this [Sherban](https://github.com/sherban1988) and [Danycerone](https://github.com/damycerone).) -### Fixed +v0.1.0 march 5th, 2014 +------------------------ -- Ensure element geometry exists. [#437](https://github.com/jlmakes/scrollreveal/issues/437) +- scrollReveal.js now has a `dist` folder containing the AMD/CommonJS compatibile library. +- [Gulp](http://gulpjs.com/) has been integrated, facilitating the build process. +- Basic testing using [Testling](https://ci.testling.com/) has been put in place. -## [4.0.6] - 2020-03-15 +### Breaking Changes -### Fixed +- scrollReveal is now implemented using the `data-scroll-reveal` attribute, **NOT** `data-scrollReveal`. -- Default transition values of `none` are now correctly ignored. [#231](https://github.com/jlmakes/scrollreveal/issues/231) +v0.0.4 February 28th, 2014 +--------------------------- -### Fixed +- scrollReveal no longer destroys the existing `style` attribute on revealed elements, but instead, now appends the necessary reveal animation styles after any existing styles. **(Fixes #18)** -## [4.0.5] - 2018-10-20 +>**Note:** scrollReveal will still override any existing transition or transform in the `style` attribute. -### Fixed +v0.0.3 February 22th, 2014 +--------------------------- -- Calling `reveal()` on the same `target` breaking animation. [#468](https://github.com/jlmakes/scrollreveal/issues/468) +- removed unnecessary styles (with `-moz-` & `-o-`) from css transitions & transforms +- added top-line comment, intending it to be kept after minification -## [4.0.4] - 2018-09-22 +v0.0.2 February 13th, 2014 +--------------------------- -### Fixed +- Added CHANGELOG +- Improved README -- Malformed `package.json` +### What’s New -## [4.0.3] - 2018-09-21 +#### Manual Instantiation +scrollReveal no longer automatically instantiates on the `DOMContentLoaded` event. It now requires that you instantiate it manually. -### Fixed +```html + -– `options.cleanup` is now correctly set to `false` by default. [#457](https://github.com/jlmakes/scrollreveal/issues/457) + + +``` +#### Defaults Object -- Null property assignment regression in mount function. [#456](https://github.com/jlmakes/scrollreveal/issues/456) +You can now pass your own starting defaults object to the scrollReveal constructor. -## [4.0.1] - 2018-09-09 +```html + +``` +#### Replay Reveal Animations +Due to popular demand, the `reset` keyword was added. Now, you can configure your animations to replay every time they enter the viewport: -### Added +*example*: +```html + +``` -- ScrollReveal can be enabled/disabled on desktops using `options.desktop`. -- The class `sr` is added to `` during instantiation when supported. [#294](https://github.com/jlmakes/scrollreveal/issues/294) -- `height: 100%` is added to `` 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. +>**See it in action:** The [demo page](http://julianlloyd.me/scrollreveal) has been updated with the `reset: true` property. -### Changed +#### Easing Control +Now you can replace the `move` keyword with easing keywords to control the easing of your reveal animation. -- **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 +*example*: +```html +
Foo
+``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3e2e8ff3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,8 @@ +Contributing +------------ + +### Running the Tests + +1. Make sure [nodejs](http://nodejs.org) is installed +2. Run `npm install -g browserify testling; npm i` from your command line +3. Run `npm test` from your command line and check the console diff --git a/Gulpfile.js b/Gulpfile.js new file mode 100644 index 00000000..08c2a231 --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,20 @@ +var gulp = require('gulp'), + uglify = require('gulp-uglify'), + umd = require('gulp-wrap-umd'), + rename = require('gulp-rename'); + +gulp.task('wrap', function(){ + gulp.src('scrollReveal.js') + .pipe(umd({ namespace: 'scrollReveal', exports: 'scrollReveal' })) + .pipe(gulp.dest('dist')) +}); + +gulp.task('minify', function(){ + gulp.src('scrollReveal.js') + .pipe(umd({ namespace: 'scrollReveal', exports: 'scrollReveal' })) + .pipe(uglify()) + .pipe(rename('scrollReveal.min.js')) + .pipe(gulp.dest('dist')) +}); + +gulp.task('default', ['wrap', 'minify']); diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..92673a9f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,12 @@ +License +------- + +The MIT License (MIT) + +Copyright © 2014 [Julian Lloyd](https://twitter.com/julianlloyd) + +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. \ No newline at end of file diff --git a/README.md b/README.md index fe586674..915cb8a2 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,250 @@ -

- - ScrollReveal - -

-
-

- - ScrollReveal - -

-

Animate elements as they scroll into view.

- -

- - Build status - - - Monthly downloads - - - Version - - 5.7 kB min+gzip - - GPLv3 License - -

- -
- -# Installation - -## Browser - -A simple and fast way to get started is to include this script on your page: +#scrollReveal.js +A simple way to create and maintain how elements fade in, triggered when they enter the viewport. An open-source experiment from [@JulianLloyd](https://twitter.com/julianlloyd). +[![scrollReveal version](https://badge.fury.io/gh/julianlloyd%2FscrollReveal.js.png)](http://badge.fury.io/gh/julianlloyd%2FscrollReveal.js) +*** +####**[→ View Demo ←](http://scrollrevealjs.org/)** +*** +Installation +------------ +> **Disclaimer:** scrollReveal.js is in early development, and breaking changes will likely occur in future updates. Before updating, please refer to the [CHANGELOG](https://github.com/julianlloyd/scrollReveal.js/blob/master/CHANGELOG.md) for details. + +#### Download +[master.zip](https://github.com/julianlloyd/scrollReveal.js/archive/master.zip) + +#### GitHub +`git clone https://github.com/julianlloyd/scrollReveal.js.git` + +#### Bower +`bower install scrollReveal.js` + +*** +Once you have a copy of `scrollReveal.js`, place it in your JavaScript folder and include the following JavaScript just before the closing `` tag. + + +```html + + +``` +Basic Usage +----------- +By adding a `data-scroll-reveal` attribute to an element, it will automatically be revealed (using default values) as soon as the element is within the viewport: ```html - + +
Hello world!
+``` +**But wait!** It’s more fun if you define your own reveal animation parameters, which you can do using using natural, declarative language: +```html + +
Foo
+
Bar
+
Baz
``` -This will create the global variable `ScrollReveal` +Getting Started +--------------- +What you enter into the `data-scroll-reveal` attribute is parsed for specific words: -> 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) +- **keywords** that expect to be followed by a **value**.

+- **fillers** as natural language sugar. (optional) -## Module +#### Keywords and Values +These specific **keyword** / **value** pairs allow you to describe basic reveal animation behavior. +*** +**keyword:** `enter` — Controls the vector origin of your reveal animation.
+**value:** `top` | `right` | `bottom` | `left`

+*Example:* +```html + +
Foo
+``` +*** +**keyword:** `move` — The distance your revealing element travels.
+**value:** [ integer ]px. -```bash -$ npm install scrollreveal +*Example:* +```html +
Bar
``` +*** +**keyword:** `over` — The duration of your reveal animation.
+**value:** [ decimal ]s -#### CommonJS -```js -const ScrollReveal = require('scrollreveal') +*Example:* +```html +
Baz
``` +*** +**keyword:** `after/wait` — The duration before your reveal begins.
+**value:** [ decimal ]s -#### ES2015 -```js -import ScrollReveal from 'scrollreveal' +*Example:* +```html + +
Mel
+
Mel
``` -
+####Combining Keyword/Value Pairs +You can easily combine the above pairs to create more dynamic reveal animations. -# Usage +*Example:* +```html +
Foo
+
Bar
+
Baz
+
Mel
+``` -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. +#### Fillers (optional) +You can use conjoining filler words for more readable language. -> 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. +- `from` +- `the` +- `and` +- `then` +- `but` +- `with` +- `,` -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: +*Example*: +```html + +
Foo
+
Bar
+
Baz
+
Mel
+``` +Advanced Usage +-------------- +####Custom defaults +You can pass an object to the constructor with your desired default configuration. ```html -

- Widget Inc. -

+ + + + + ``` -```js -ScrollReveal().reveal('.headline') +#### Generated HTML + +The `scrollReveal.init()` method checks the DOM for all elements with `data-scroll-reveal` attributes, and initializes their reveal animations. By default, this method fires on instantiation, but by amending our config object with `init: false`, you can then choose when `scrollReveal.init()` is first fired. (Say, after your DOM is updated.) + +>**Note:** It’s worth noting, you can also call this method at any time to re-check the DOM. + +*Example:* +```html + + + + + ``` -**🔎 See this demo live on [JSBin](http://jsbin.com/jufohaxonu/edit?html,output)** -
+#### Viewport Factor +If set to **0**, the element is considered in the viewport as soon as it enters.
+If set to **1**, the element is considered in the viewport when it is fully visible. + +*Example:* +```javascript + var config = { + viewportFactor: 0.33 + }; + + // Your reveal animation triggers after 33% of + // your element is visible within the viewport. +``` + +#### Replaying animations +**Keyword:** `reset` — replay reveal animations every time elements enter the viewport. +```html + +
Foo
+``` ---- +#### Controlling Easing +The `move` keyword can be replaced with any one of the following: -### The full documentation can be found at [https://scrollrevealjs.org](https://scrollrevealjs.org) +- `ease` +- `ease-in` +- `ease-out` +- `ease-in-out` -> If you’re using an older version of ScrollReveal, you can find legacy documentation in the [wiki](https://github.com/jlmakes/scrollreveal/wiki) +*Example:* +```html +
Foo
+``` ---- +Browser Support +--------------- +scrollReveal.js does not require jQuery, but does rely on [CSS3 transitions](http://caniuse.com/#search=transition) to power its reveal animations; it has been developed exclusively for **modern browser use only**. -
+Contributions +------------- +Community feedback and involvement is highly encouraged. +*** +#### [→ Open Issues ←](https://github.com/julianlloyd/scrollReveal.js/issues?state=open) +*** +#### Special Thanks +scrollReveal.js was inspired by the awesome [cbpScroller.js](http://tympanus.net/codrops/2013/07/18/on-scroll-effect-layout/) by [Mary Lou](https://twitter.com/crnacura). Copyright © 2014 [Codrops](http://tympanus.net/codrops/). - - Commercial License Badge - +Also, a special thanks to Jeff Escalante ([Jenius](https://github.com/jenius)) for setting up the build process, and his ongoing help with testing and the JavaScript API. -
+License +------- -# License +The MIT License (MIT) -**For commercial sites, themes, projects, and applications, keep your source code private/proprietary by purchasing a [Commercial License](https://scrollrevealjs.org/pricing/).** +Copyright © 2014 [Julian Lloyd](https://twitter.com/julianlloyd) -Licensed under the GNU General Public License 3.0 for compatible open source projects and non-commercial use. +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. -Copyright 2023 Fisssion LLC +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. diff --git a/bower.json b/bower.json new file mode 100644 index 00000000..2f86b0ca --- /dev/null +++ b/bower.json @@ -0,0 +1,28 @@ +{ + "name": "scrollReveal.js", + "version": "0.1.3", + "homepage": "https://github.com/julianlloyd/scrollReveal.js", + "authors": [ + "Julian Lloyd " + ], + "description": "A simple way to create and maintain how elements fade in, triggered when they enter the viewport.", + "main": ["scrollReveal.js"], + "keywords": [ + "scrollReveal", + "scroll", + "reveal", + "animation", + "CSS", + "transition", + "JavaScript" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "app/bower_components", + "test", + "tests" + ] +} diff --git a/build/rollup.conf.banner.js b/build/rollup.conf.banner.js deleted file mode 100644 index 5f944627..00000000 --- a/build/rollup.conf.banner.js +++ /dev/null @@ -1,15 +0,0 @@ -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 diff --git a/build/rollup.conf.js b/build/rollup.conf.js deleted file mode 100644 index 31ea527b..00000000 --- a/build/rollup.conf.js +++ /dev/null @@ -1,26 +0,0 @@ -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] diff --git a/build/rollup.conf.min.js b/build/rollup.conf.min.js deleted file mode 100644 index 6d4a7814..00000000 --- a/build/rollup.conf.min.js +++ /dev/null @@ -1,30 +0,0 @@ -import buble from 'rollup-plugin-buble' -import json from 'rollup-plugin-json' -import nodeResolve from 'rollup-plugin-node-resolve' -import strip from 'rollup-plugin-strip' -import { uglify } from 'rollup-plugin-uglify' -import banner from './rollup.conf.banner' - -export default { - input: 'src/index.js', - plugins: [ - json(), - nodeResolve(), - buble(), - strip({ - functions: ['logger'], - sourceMaps: false - }), - uglify({ - output: { - comments: /@license ScrollReveal/ - } - }) - ], - output: { - banner, - format: 'iife', - file: 'dist/scrollreveal.min.js', - name: 'ScrollReveal' - } -} diff --git a/dist/scrollReveal.js b/dist/scrollReveal.js new file mode 100644 index 00000000..9e3bd17e --- /dev/null +++ b/dist/scrollReveal.js @@ -0,0 +1,413 @@ + +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(require, exports, module); + } else { + root.scrollReveal = factory(); + } +}(this, function(require, exports, module) { + +/* + _ _ _____ _ _ + | | | __ \ | | (_) + ___ ___ _ __ ___ | | | |__) |_____ _____ __ _| | _ ___ + / __|/ __| '__/ _ \| | | _ // _ \ \ / / _ \/ _` | | | / __| + \__ \ (__| | | (_) | | | | \ \ __/\ V / __/ (_| | |_| \__ \ + |___/\___|_| \___/|_|_|_| \_\___| \_/ \___|\__,_|_(_) |___/ v.0.1.3 + _/ | + |__/ + + "Declarative on-scroll reveal animations." + +/*============================================================================= + + scrollReveal.js was inspired by cbpScroller.js (c) 2014 Codrops. + + Licensed under the MIT license. + http://www.opensource.org/licenses/mit-license.php + +=============================================================================*/ + +/*! scrollReveal.js v0.1.3 (c) 2014 Julian Lloyd | MIT license */ + +/*===========================================================================*/ + + +window.scrollReveal = (function (window) { + + 'use strict'; + + // generator (increments) for the next scroll-reveal-id + var nextId = 1; + + /** + * RequestAnimationFrame polyfill + * @function + * @private + */ + var requestAnimFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; + }()); + + function scrollReveal(options) { + + this.docElem = window.document.documentElement; + this.options = this.extend(this.defaults, options); + this.styleBank = {}; + + if (this.options.init == true) this.init(); + } + + scrollReveal.prototype = { + + defaults: { + after: '0s', + enter: 'bottom', + move: '24px', + over: '0.66s', + easing: 'ease-in-out', + opacity: 0, + + // if 0, the element is considered in the viewport as soon as it enters + // if 1, the element is considered in the viewport when it's fully visible + viewportFactor: 0.33, + + // if false, animations occur only once + // if true, animations occur each time an element enters the viewport + reset: false, + + // if true, scrollReveal.init() is automaticaly called upon instantiation + init: true + }, + + /*=============================================================================*/ + + init: function () { + + this.scrolled = false; + + var self = this; + + // Check DOM for the data-scrollReveal attribute + // and initialize all found elements. + this.elems = Array.prototype.slice.call(this.docElem.querySelectorAll('[data-scroll-reveal]')); + this.elems.forEach(function (el, i) { + + // Capture original style attribute + var id = el.getAttribute("data-scroll-reveal-id"); + if (!id) { + id = nextId++; + el.setAttribute("data-scroll-reveal-id", id); + } + if (!self.styleBank[id]) { + self.styleBank[id] = el.getAttribute('style'); + } + + self.update(el); + }); + + var scrollHandler = function (e) { + // No changing, exit + if (!self.scrolled) { + self.scrolled = true; + requestAnimFrame(function () { + self._scrollPage(); + }); + } + }; + + var resizeHandler = function () { + + // If we’re still waiting for settimeout, reset the timer. + if (self.resizeTimeout) { + clearTimeout(self.resizeTimeout); + } + function delayed() { + self._scrollPage(); + self.resizeTimeout = null; + } + self.resizeTimeout = setTimeout(delayed, 200); + }; + + // captureScroll + window.addEventListener('scroll', scrollHandler, false); + window.addEventListener('resize', resizeHandler, false); + }, + + /*=============================================================================*/ + + _scrollPage: function () { + var self = this; + + this.elems.forEach(function (el, i) { + self.update(el); + }); + this.scrolled = false; + }, + + /*=============================================================================*/ + + parseLanguage: function (el) { + + // Splits on a sequence of one or more commas or spaces. + var words = el.getAttribute('data-scroll-reveal').split(/[, ]+/), + parsed = {}; + + function filter (words) { + var ret = [], + + blacklist = [ + "from", + "the", + "and", + "then", + "but", + "with" + ]; + + words.forEach(function (word, i) { + if (blacklist.indexOf(word) > -1) { + return; + } + ret.push(word); + }); + + return ret; + } + + words = filter(words); + + words.forEach(function (word, i) { + + switch (word) { + case "enter": + parsed.enter = words[i + 1]; + return; + + case "after": + parsed.after = words[i + 1]; + return; + + case "wait": + parsed.after = words[i + 1]; + return; + + case "move": + parsed.move = words[i + 1]; + return; + + case "ease": + parsed.move = words[i + 1]; + parsed.ease = "ease"; + return; + + case "ease-in": + parsed.move = words[i + 1]; + parsed.easing = "ease-in"; + return; + + case "ease-in-out": + parsed.move = words[i + 1]; + parsed.easing = "ease-in-out"; + return; + + case "ease-out": + parsed.move = words[i + 1]; + parsed.easing = "ease-out"; + return; + + case "over": + parsed.over = words[i + 1]; + return; + + default: + return; + } + }); + + return parsed; + }, + + + /*=============================================================================*/ + + update: function (el) { + + var css = this.genCSS(el); + var style = this.styleBank[el.getAttribute("data-scroll-reveal-id")]; + + if (style != null) style += ";"; else style = ""; + + if (!el.getAttribute('data-scroll-reveal-initialized')) { + el.setAttribute('style', style + css.initial); + el.setAttribute('data-scroll-reveal-initialized', true); + } + + if (!this.isElementInViewport(el, this.options.viewportFactor)) { + if (this.options.reset) { + el.setAttribute('style', style + css.initial + css.reset); + } + return; + } + + if (el.getAttribute('data-scroll-reveal-complete')) return; + + if (this.isElementInViewport(el, this.options.viewportFactor)) { + el.setAttribute('style', style + css.target + css.transition); + // Without reset enabled, we can safely remove the style tag + // to prevent CSS specificy wars with authored CSS. + if (!this.options.reset) { + setTimeout(function () { + if (style != "") { + el.setAttribute('style', style); + } else { + el.removeAttribute('style'); + } + el.setAttribute('data-scroll-reveal-complete',true); + }, css.totalDuration); + } + return; + } + }, + + /*=============================================================================*/ + + genCSS: function (el) { + var parsed = this.parseLanguage(el), + enter, + axis; + + if (parsed.enter) { + + if (parsed.enter == "top" || parsed.enter == "bottom") { + enter = parsed.enter; + axis = "y"; + } + + if (parsed.enter == "left" || parsed.enter == "right") { + enter = parsed.enter; + axis = "x"; + } + + } else { + + if (this.options.enter == "top" || this.options.enter == "bottom") { + enter = this.options.enter + axis = "y"; + } + + if (this.options.enter == "left" || this.options.enter == "right") { + enter = this.options.enter + axis = "x"; + } + } + + // After all values are parsed, let’s make sure our our + // pixel distance is negative for top and left entrances. + // + // ie. "move 25px from top" starts at 'top: -25px' in CSS. + + if (enter == "top" || enter == "left") { + if (parsed.move) { + parsed.move = "-" + parsed.move; + } + else { + parsed.move = "-" + this.options.move; + } + } + + var dist = parsed.move || this.options.move, + dur = parsed.over || this.options.over, + delay = parsed.after || this.options.after, + easing = parsed.easing || this.options.easing, + opacity = parsed.opacity || this.options.opacity; + + var transition = "-webkit-transition: -webkit-transform " + dur + " " + easing + " " + delay + ", opacity " + dur + " " + easing + " " + delay + ";" + + "transition: transform " + dur + " " + easing + " " + delay + ", opacity " + dur + " " + easing + " " + delay + ";" + + "-webkit-perspective: 1000;" + + "-webkit-backface-visibility: hidden;"; + + // The same as transition, but removing the delay for elements fading out. + var reset = "-webkit-transition: -webkit-transform " + dur + " " + easing + " 0s, opacity " + dur + " " + easing + " " + delay + ";" + + "transition: transform " + dur + " " + easing + " 0s, opacity " + dur + " " + easing + " " + delay + ";" + + "-webkit-perspective: 1000;" + + "-webkit-backface-visibility: hidden;"; + + var initial = "-webkit-transform: translate" + axis + "(" + dist + ");" + + "transform: translate" + axis + "(" + dist + ");" + + "opacity: " + opacity + ";"; + + var target = "-webkit-transform: translate" + axis + "(0);" + + "transform: translate" + axis + "(0);" + + "opacity: 1;"; + return { + transition: transition, + initial: initial, + target: target, + reset: reset, + totalDuration: ((parseFloat(dur) + parseFloat(delay)) * 1000) + }; + }, + + getViewportH : function () { + var client = this.docElem['clientHeight'], + inner = window['innerHeight']; + + return (client < inner) ? inner : client; + }, + + getOffset : function(el) { + var offsetTop = 0, + offsetLeft = 0; + + do { + if (!isNaN(el.offsetTop)) { + offsetTop += el.offsetTop; + } + if (!isNaN(el.offsetLeft)) { + offsetLeft += el.offsetLeft; + } + } while (el = el.offsetParent) + + return { + top: offsetTop, + left: offsetLeft + } + }, + + isElementInViewport : function(el, h) { + var scrolled = window.pageYOffset, + viewed = scrolled + this.getViewportH(), + elH = el.offsetHeight, + elTop = this.getOffset(el).top, + elBottom = elTop + elH, + h = h || 0; + + return (elTop + elH * h) <= viewed + && (elBottom) >= scrolled + || (el.currentStyle? el.currentStyle : window.getComputedStyle(el, null)).position == 'fixed'; + }, + + extend: function (a, b){ + for (var key in b) { + if (b.hasOwnProperty(key)) { + a[key] = b[key]; + } + } + return a; + } + }; // end scrollReveal.prototype + + return scrollReveal; +})(window); + +return scrollReveal; + +})); diff --git a/dist/scrollReveal.min.js b/dist/scrollReveal.min.js new file mode 100644 index 00000000..2e4e3138 --- /dev/null +++ b/dist/scrollReveal.min.js @@ -0,0 +1 @@ +!function(t,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e(require,exports,module):t.scrollReveal=e()}(this,function(){return window.scrollReveal=function(t){"use strict";function e(e){this.docElem=t.document.documentElement,this.options=this.extend(this.defaults,e),this.styleBank={},1==this.options.init&&this.init()}var i=1,o=function(){return t.requestAnimationFrame||t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||function(e){t.setTimeout(e,1e3/60)}}();return e.prototype={defaults:{after:"0s",enter:"bottom",move:"24px",over:"0.66s",easing:"ease-in-out",opacity:0,viewportFactor:.33,reset:!1,init:!0},init:function(){this.scrolled=!1;var e=this;this.elems=Array.prototype.slice.call(this.docElem.querySelectorAll("[data-scroll-reveal]")),this.elems.forEach(function(t){var o=t.getAttribute("data-scroll-reveal-id");o||(o=i++,t.setAttribute("data-scroll-reveal-id",o)),e.styleBank[o]||(e.styleBank[o]=t.getAttribute("style")),e.update(t)});var r=function(){e.scrolled||(e.scrolled=!0,o(function(){e._scrollPage()}))},n=function(){function t(){e._scrollPage(),e.resizeTimeout=null}e.resizeTimeout&&clearTimeout(e.resizeTimeout),e.resizeTimeout=setTimeout(t,200)};t.addEventListener("scroll",r,!1),t.addEventListener("resize",n,!1)},_scrollPage:function(){var t=this;this.elems.forEach(function(e){t.update(e)}),this.scrolled=!1},parseLanguage:function(t){function e(t){var e=[],i=["from","the","and","then","but","with"];return t.forEach(function(t){i.indexOf(t)>-1||e.push(t)}),e}var i=t.getAttribute("data-scroll-reveal").split(/[, ]+/),o={};return i=e(i),i.forEach(function(t,e){switch(t){case"enter":return void(o.enter=i[e+1]);case"after":return void(o.after=i[e+1]);case"wait":return void(o.after=i[e+1]);case"move":return void(o.move=i[e+1]);case"ease":return o.move=i[e+1],void(o.ease="ease");case"ease-in":return o.move=i[e+1],void(o.easing="ease-in");case"ease-in-out":return o.move=i[e+1],void(o.easing="ease-in-out");case"ease-out":return o.move=i[e+1],void(o.easing="ease-out");case"over":return void(o.over=i[e+1]);default:return}}),o},update:function(t){var e=this.genCSS(t),i=this.styleBank[t.getAttribute("data-scroll-reveal-id")];return null!=i?i+=";":i="",t.getAttribute("data-scroll-reveal-initialized")||(t.setAttribute("style",i+e.initial),t.setAttribute("data-scroll-reveal-initialized",!0)),this.isElementInViewport(t,this.options.viewportFactor)?t.getAttribute("data-scroll-reveal-complete")?void 0:this.isElementInViewport(t,this.options.viewportFactor)?(t.setAttribute("style",i+e.target+e.transition),void(this.options.reset||setTimeout(function(){""!=i?t.setAttribute("style",i):t.removeAttribute("style"),t.setAttribute("data-scroll-reveal-complete",!0)},e.totalDuration))):void 0:void(this.options.reset&&t.setAttribute("style",i+e.initial+e.reset))},genCSS:function(t){var e,i,o=this.parseLanguage(t);o.enter?(("top"==o.enter||"bottom"==o.enter)&&(e=o.enter,i="y"),("left"==o.enter||"right"==o.enter)&&(e=o.enter,i="x")):(("top"==this.options.enter||"bottom"==this.options.enter)&&(e=this.options.enter,i="y"),("left"==this.options.enter||"right"==this.options.enter)&&(e=this.options.enter,i="x")),("top"==e||"left"==e)&&(o.move=o.move?"-"+o.move:"-"+this.options.move);var r=o.move||this.options.move,n=o.over||this.options.over,s=o.after||this.options.after,a=o.easing||this.options.easing,l=o.opacity||this.options.opacity,u="-webkit-transition: -webkit-transform "+n+" "+a+" "+s+", opacity "+n+" "+a+" "+s+";transition: transform "+n+" "+a+" "+s+", opacity "+n+" "+a+" "+s+";-webkit-perspective: 1000;-webkit-backface-visibility: hidden;",c="-webkit-transition: -webkit-transform "+n+" "+a+" 0s, opacity "+n+" "+a+" "+s+";transition: transform "+n+" "+a+" 0s, opacity "+n+" "+a+" "+s+";-webkit-perspective: 1000;-webkit-backface-visibility: hidden;",f="-webkit-transform: translate"+i+"("+r+");transform: translate"+i+"("+r+");opacity: "+l+";",p="-webkit-transform: translate"+i+"(0);transform: translate"+i+"(0);opacity: 1;";return{transition:u,initial:f,target:p,reset:c,totalDuration:1e3*(parseFloat(n)+parseFloat(s))}},getViewportH:function(){var e=this.docElem.clientHeight,i=t.innerHeight;return i>e?i:e},getOffset:function(t){var e=0,i=0;do isNaN(t.offsetTop)||(e+=t.offsetTop),isNaN(t.offsetLeft)||(i+=t.offsetLeft);while(t=t.offsetParent);return{top:e,left:i}},isElementInViewport:function(e,i){var o=t.pageYOffset,r=o+this.getViewportH(),n=e.offsetHeight,s=this.getOffset(e).top,a=s+n,i=i||0;return r>=s+n*i&&a>=o||"fixed"==(e.currentStyle?e.currentStyle:t.getComputedStyle(e,null)).position},extend:function(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}},e}(window),scrollReveal}); \ No newline at end of file diff --git a/dist/scrollreveal.es.js b/dist/scrollreveal.es.js deleted file mode 100644 index fcd284c8..00000000 --- a/dist/scrollreveal.es.js +++ /dev/null @@ -1,1186 +0,0 @@ -/*! @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; diff --git a/dist/scrollreveal.js b/dist/scrollreveal.js deleted file mode 100644 index 99b46d30..00000000 --- a/dist/scrollreveal.js +++ /dev/null @@ -1,1564 +0,0 @@ -/*! @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; - -})); diff --git a/dist/scrollreveal.min.js b/dist/scrollreveal.min.js deleted file mode 100644 index 5428bbd7..00000000 --- a/dist/scrollreveal.min.js +++ /dev/null @@ -1,12 +0,0 @@ -/*! @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/ -*/ -var ScrollReveal=function(){"use strict";var r={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:!1,container:document.documentElement,desktop:!0,mobile:!0,reset:!1,useDelay:"always",viewFactor:0,viewOffset:{top:0,right:0,bottom:0,left:0},afterReset:function(){},afterReveal:function(){},beforeReset:function(){},beforeReveal:function(){}};var n={success:function(){document.documentElement.classList.add("sr"),document.body?document.body.style.height="100%":document.addEventListener("DOMContentLoaded",function(){document.body.style.height="100%"})},failure:function(){return document.documentElement.classList.remove("sr"),{clean:function(){},destroy:function(){},reveal:function(){},sync:function(){},get noop(){return!0}}}};function o(e){return"object"==typeof window.Node?e instanceof window.Node:null!==e&&"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName}function u(e,t){if(void 0===t&&(t=document),e instanceof Array)return e.filter(o);if(o(e))return[e];if(n=e,i=Object.prototype.toString.call(n),"object"==typeof window.NodeList?n instanceof window.NodeList:null!==n&&"object"==typeof n&&"number"==typeof n.length&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(i)&&(0===n.length||o(n[0])))return Array.prototype.slice.call(e);var n,i;if("string"==typeof e)try{var r=t.querySelectorAll(e);return Array.prototype.slice.call(r)}catch(e){return[]}return[]}function s(e){return null!==e&&e instanceof Object&&(e.constructor===Object||"[object Object]"===Object.prototype.toString.call(e))}function f(n,i){if(s(n))return Object.keys(n).forEach(function(e){return i(n[e],e,n)});if(n instanceof Array)return n.forEach(function(e,t){return i(e,t,n)});throw new TypeError("Expected either an array or object literal.")}function h(e){for(var t=[],n=arguments.length-1;0=[].concat(r.body).shift())return j.call(this,n,i,-1,t),c.call(this,e,{reveal:!0,pristine:t});if(!n.blocked.foot&&i===[].concat(o.foot).shift()&&i<=[].concat(r.body).pop())return j.call(this,n,i,1,t),c.call(this,e,{reveal:!0,pristine:t})}}function E(e){var t=Math.abs(e);if(isNaN(t))throw new RangeError("Invalid sequence interval.");this.id=b(),this.interval=Math.max(t,16),this.members=[],this.models={},this.blocked={head:!1,foot:!1}}function d(e,i,r){var o=this;this.head=[],this.body=[],this.foot=[],f(e.members,function(e,t){var n=r.elements[e];n&&n[i]&&o.body.push(t)}),this.body.length&&f(e.members,function(e,t){var n=r.elements[e];n&&!n[i]&&(t> \\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" + "name": "scrollReveal.js", + "version": "0.1.3", + "description": "Declarative on-scroll reveal animations", + "main": "scrollReveal.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "browserify test/test.js > test/bundle.js; testling", + "browser": "browserify test/test.js > test/bundle.js; open test/test.html" + }, + "repository": { + "type": "git", + "url": "https://github.com/julianlloyd/scrollReveal.js.git" + }, + "author": "Julian Lloyd", + "license": "MIT", + "bugs": { + "url": "https://github.com/julianlloyd/scrollReveal.js/issues" + }, + "testling": { + "html": "test/test.html", + "browsers": [ + "ie/8..latest", + "chrome/latest", + "firefox/latest", + "safari/latest", + "iphone/6", + "ipad/6", + "android-browser/latest" + ] + }, + "homepage": "https://github.com/julianlloyd/scrollReveal.js", + "devDependencies": { + "tape": "*", + "testling": "*", + "browserify": "*", + "gulp": "*", + "gulp-uglify": "*", + "gulp-wrap-umd": "*", + "gulp-rename": "~1.1.0" + } } diff --git a/scrollReveal.js b/scrollReveal.js new file mode 100644 index 00000000..fc81431a --- /dev/null +++ b/scrollReveal.js @@ -0,0 +1,398 @@ +/* + _ _ _____ _ _ + | | | __ \ | | (_) + ___ ___ _ __ ___ | | | |__) |_____ _____ __ _| | _ ___ + / __|/ __| '__/ _ \| | | _ // _ \ \ / / _ \/ _` | | | / __| + \__ \ (__| | | (_) | | | | \ \ __/\ V / __/ (_| | |_| \__ \ + |___/\___|_| \___/|_|_|_| \_\___| \_/ \___|\__,_|_(_) |___/ v.0.1.3 + _/ | + |__/ + + "Declarative on-scroll reveal animations." + +/*============================================================================= + + scrollReveal.js was inspired by cbpScroller.js (c) 2014 Codrops. + + Licensed under the MIT license. + http://www.opensource.org/licenses/mit-license.php + +=============================================================================*/ + +/*! scrollReveal.js v0.1.3 (c) 2014 Julian Lloyd | MIT license */ + +/*===========================================================================*/ + + +window.scrollReveal = (function (window) { + + 'use strict'; + + // generator (increments) for the next scroll-reveal-id + var nextId = 1; + + /** + * RequestAnimationFrame polyfill + * @function + * @private + */ + var requestAnimFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; + }()); + + function scrollReveal(options) { + + this.docElem = window.document.documentElement; + this.options = this.extend(this.defaults, options); + this.styleBank = {}; + + if (this.options.init == true) this.init(); + } + + scrollReveal.prototype = { + + defaults: { + after: '0s', + enter: 'bottom', + move: '24px', + over: '0.66s', + easing: 'ease-in-out', + opacity: 0, + + // if 0, the element is considered in the viewport as soon as it enters + // if 1, the element is considered in the viewport when it's fully visible + viewportFactor: 0.33, + + // if false, animations occur only once + // if true, animations occur each time an element enters the viewport + reset: false, + + // if true, scrollReveal.init() is automaticaly called upon instantiation + init: true + }, + + /*=============================================================================*/ + + init: function () { + + this.scrolled = false; + + var self = this; + + // Check DOM for the data-scrollReveal attribute + // and initialize all found elements. + this.elems = Array.prototype.slice.call(this.docElem.querySelectorAll('[data-scroll-reveal]')); + this.elems.forEach(function (el, i) { + + // Capture original style attribute + var id = el.getAttribute("data-scroll-reveal-id"); + if (!id) { + id = nextId++; + el.setAttribute("data-scroll-reveal-id", id); + } + if (!self.styleBank[id]) { + self.styleBank[id] = el.getAttribute('style'); + } + + self.update(el); + }); + + var scrollHandler = function (e) { + // No changing, exit + if (!self.scrolled) { + self.scrolled = true; + requestAnimFrame(function () { + self._scrollPage(); + }); + } + }; + + var resizeHandler = function () { + + // If we’re still waiting for settimeout, reset the timer. + if (self.resizeTimeout) { + clearTimeout(self.resizeTimeout); + } + function delayed() { + self._scrollPage(); + self.resizeTimeout = null; + } + self.resizeTimeout = setTimeout(delayed, 200); + }; + + // captureScroll + window.addEventListener('scroll', scrollHandler, false); + window.addEventListener('resize', resizeHandler, false); + }, + + /*=============================================================================*/ + + _scrollPage: function () { + var self = this; + + this.elems.forEach(function (el, i) { + self.update(el); + }); + this.scrolled = false; + }, + + /*=============================================================================*/ + + parseLanguage: function (el) { + + // Splits on a sequence of one or more commas or spaces. + var words = el.getAttribute('data-scroll-reveal').split(/[, ]+/), + parsed = {}; + + function filter (words) { + var ret = [], + + blacklist = [ + "from", + "the", + "and", + "then", + "but", + "with" + ]; + + words.forEach(function (word, i) { + if (blacklist.indexOf(word) > -1) { + return; + } + ret.push(word); + }); + + return ret; + } + + words = filter(words); + + words.forEach(function (word, i) { + + switch (word) { + case "enter": + parsed.enter = words[i + 1]; + return; + + case "after": + parsed.after = words[i + 1]; + return; + + case "wait": + parsed.after = words[i + 1]; + return; + + case "move": + parsed.move = words[i + 1]; + return; + + case "ease": + parsed.move = words[i + 1]; + parsed.ease = "ease"; + return; + + case "ease-in": + parsed.move = words[i + 1]; + parsed.easing = "ease-in"; + return; + + case "ease-in-out": + parsed.move = words[i + 1]; + parsed.easing = "ease-in-out"; + return; + + case "ease-out": + parsed.move = words[i + 1]; + parsed.easing = "ease-out"; + return; + + case "over": + parsed.over = words[i + 1]; + return; + + default: + return; + } + }); + + return parsed; + }, + + + /*=============================================================================*/ + + update: function (el) { + + var css = this.genCSS(el); + var style = this.styleBank[el.getAttribute("data-scroll-reveal-id")]; + + if (style != null) style += ";"; else style = ""; + + if (!el.getAttribute('data-scroll-reveal-initialized')) { + el.setAttribute('style', style + css.initial); + el.setAttribute('data-scroll-reveal-initialized', true); + } + + if (!this.isElementInViewport(el, this.options.viewportFactor)) { + if (this.options.reset) { + el.setAttribute('style', style + css.initial + css.reset); + } + return; + } + + if (el.getAttribute('data-scroll-reveal-complete')) return; + + if (this.isElementInViewport(el, this.options.viewportFactor)) { + el.setAttribute('style', style + css.target + css.transition); + // Without reset enabled, we can safely remove the style tag + // to prevent CSS specificy wars with authored CSS. + if (!this.options.reset) { + setTimeout(function () { + if (style != "") { + el.setAttribute('style', style); + } else { + el.removeAttribute('style'); + } + el.setAttribute('data-scroll-reveal-complete',true); + }, css.totalDuration); + } + return; + } + }, + + /*=============================================================================*/ + + genCSS: function (el) { + var parsed = this.parseLanguage(el), + enter, + axis; + + if (parsed.enter) { + + if (parsed.enter == "top" || parsed.enter == "bottom") { + enter = parsed.enter; + axis = "y"; + } + + if (parsed.enter == "left" || parsed.enter == "right") { + enter = parsed.enter; + axis = "x"; + } + + } else { + + if (this.options.enter == "top" || this.options.enter == "bottom") { + enter = this.options.enter + axis = "y"; + } + + if (this.options.enter == "left" || this.options.enter == "right") { + enter = this.options.enter + axis = "x"; + } + } + + // After all values are parsed, let’s make sure our our + // pixel distance is negative for top and left entrances. + // + // ie. "move 25px from top" starts at 'top: -25px' in CSS. + + if (enter == "top" || enter == "left") { + if (parsed.move) { + parsed.move = "-" + parsed.move; + } + else { + parsed.move = "-" + this.options.move; + } + } + + var dist = parsed.move || this.options.move, + dur = parsed.over || this.options.over, + delay = parsed.after || this.options.after, + easing = parsed.easing || this.options.easing, + opacity = parsed.opacity || this.options.opacity; + + var transition = "-webkit-transition: -webkit-transform " + dur + " " + easing + " " + delay + ", opacity " + dur + " " + easing + " " + delay + ";" + + "transition: transform " + dur + " " + easing + " " + delay + ", opacity " + dur + " " + easing + " " + delay + ";" + + "-webkit-perspective: 1000;" + + "-webkit-backface-visibility: hidden;"; + + // The same as transition, but removing the delay for elements fading out. + var reset = "-webkit-transition: -webkit-transform " + dur + " " + easing + " 0s, opacity " + dur + " " + easing + " " + delay + ";" + + "transition: transform " + dur + " " + easing + " 0s, opacity " + dur + " " + easing + " " + delay + ";" + + "-webkit-perspective: 1000;" + + "-webkit-backface-visibility: hidden;"; + + var initial = "-webkit-transform: translate" + axis + "(" + dist + ");" + + "transform: translate" + axis + "(" + dist + ");" + + "opacity: " + opacity + ";"; + + var target = "-webkit-transform: translate" + axis + "(0);" + + "transform: translate" + axis + "(0);" + + "opacity: 1;"; + return { + transition: transition, + initial: initial, + target: target, + reset: reset, + totalDuration: ((parseFloat(dur) + parseFloat(delay)) * 1000) + }; + }, + + getViewportH : function () { + var client = this.docElem['clientHeight'], + inner = window['innerHeight']; + + return (client < inner) ? inner : client; + }, + + getOffset : function(el) { + var offsetTop = 0, + offsetLeft = 0; + + do { + if (!isNaN(el.offsetTop)) { + offsetTop += el.offsetTop; + } + if (!isNaN(el.offsetLeft)) { + offsetLeft += el.offsetLeft; + } + } while (el = el.offsetParent) + + return { + top: offsetTop, + left: offsetLeft + } + }, + + isElementInViewport : function(el, h) { + var scrolled = window.pageYOffset, + viewed = scrolled + this.getViewportH(), + elH = el.offsetHeight, + elTop = this.getOffset(el).top, + elBottom = elTop + elH, + h = h || 0; + + return (elTop + elH * h) <= viewed + && (elBottom) >= scrolled + || (el.currentStyle? el.currentStyle : window.getComputedStyle(el, null)).position == 'fixed'; + }, + + extend: function (a, b){ + for (var key in b) { + if (b.hasOwnProperty(key)) { + a[key] = b[key]; + } + } + return a; + } + }; // end scrollReveal.prototype + + return scrollReveal; +})(window); diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 04c1b080..00000000 --- a/src/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import Constructor from './instance/constructor' - -Constructor() - -export default Constructor diff --git a/src/instance/constructor.js b/src/instance/constructor.js deleted file mode 100644 index 95ed60c4..00000000 --- a/src/instance/constructor.js +++ /dev/null @@ -1,112 +0,0 @@ -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) -}) diff --git a/src/instance/defaults.js b/src/instance/defaults.js deleted file mode 100644 index f792196a..00000000 --- a/src/instance/defaults.js +++ /dev/null @@ -1,32 +0,0 @@ -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() {} -} diff --git a/src/instance/functions/animate.js b/src/instance/functions/animate.js deleted file mode 100644 index 9e5c1304..00000000 --- a/src/instance/functions/animate.js +++ /dev/null @@ -1,82 +0,0 @@ -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) - } -} diff --git a/src/instance/functions/delegate.js b/src/instance/functions/delegate.js deleted file mode 100644 index ea14bfc3..00000000 --- a/src/instance/functions/delegate.js +++ /dev/null @@ -1,54 +0,0 @@ -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 - }) -} diff --git a/src/instance/functions/initialize.js b/src/instance/functions/initialize.js deleted file mode 100644 index fe4fc63c..00000000 --- a/src/instance/functions/initialize.js +++ /dev/null @@ -1,43 +0,0 @@ -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 -} diff --git a/src/instance/functions/rinse.js b/src/instance/functions/rinse.js deleted file mode 100644 index 3cc55358..00000000 --- a/src/instance/functions/rinse.js +++ /dev/null @@ -1,76 +0,0 @@ -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]) -} diff --git a/src/instance/functions/sequence.js b/src/instance/functions/sequence.js deleted file mode 100644 index 9cd11c6e..00000000 --- a/src/instance/functions/sequence.js +++ /dev/null @@ -1,121 +0,0 @@ -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) -} diff --git a/src/instance/functions/style.js b/src/instance/functions/style.js deleted file mode 100644 index 0c48149a..00000000 --- a/src/instance/functions/style.js +++ /dev/null @@ -1,234 +0,0 @@ -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(':') - } - }) -} - diff --git a/src/instance/methods/clean.js b/src/instance/methods/clean.js deleted file mode 100644 index 0fe71322..00000000 --- a/src/instance/methods/clean.js +++ /dev/null @@ -1,34 +0,0 @@ -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) - } - } -} diff --git a/src/instance/methods/destroy.js b/src/instance/methods/destroy.js deleted file mode 100644 index d2008fe4..00000000 --- a/src/instance/methods/destroy.js +++ /dev/null @@ -1,32 +0,0 @@ -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: {} - } -} diff --git a/src/instance/methods/reveal.js b/src/instance/methods/reveal.js deleted file mode 100644 index 6cd0bf17..00000000 --- a/src/instance/methods/reveal.js +++ /dev/null @@ -1,150 +0,0 @@ -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 -} diff --git a/src/instance/methods/sync.js b/src/instance/methods/sync.js deleted file mode 100644 index 23d2f69f..00000000 --- a/src/instance/methods/sync.js +++ /dev/null @@ -1,15 +0,0 @@ -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) -} diff --git a/src/instance/mount.js b/src/instance/mount.js deleted file mode 100644 index 69c84fe5..00000000 --- a/src/instance/mount.js +++ /dev/null @@ -1,27 +0,0 @@ -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 } diff --git a/src/polyfills/math-sign.js b/src/polyfills/math-sign.js deleted file mode 100644 index 474d67e0..00000000 --- a/src/polyfills/math-sign.js +++ /dev/null @@ -1,2 +0,0 @@ -export const polyfill = x => (x > 0) - (x < 0) || +x -export default Math.sign || polyfill diff --git a/src/utils/deep-assign.js b/src/utils/deep-assign.js deleted file mode 100644 index 0e70fc2c..00000000 --- a/src/utils/deep-assign.js +++ /dev/null @@ -1,22 +0,0 @@ -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.') - } -} diff --git a/src/utils/each.js b/src/utils/each.js deleted file mode 100644 index 825abbd0..00000000 --- a/src/utils/each.js +++ /dev/null @@ -1,12 +0,0 @@ -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.') -} diff --git a/src/utils/get-geometry.js b/src/utils/get-geometry.js deleted file mode 100644 index 79fc451b..00000000 --- a/src/utils/get-geometry.js +++ /dev/null @@ -1,33 +0,0 @@ -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 - } -} diff --git a/src/utils/get-prefixed-css-prop.js b/src/utils/get-prefixed-css-prop.js deleted file mode 100644 index 3e425f5d..00000000 --- a/src/utils/get-prefixed-css-prop.js +++ /dev/null @@ -1,26 +0,0 @@ -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 diff --git a/src/utils/get-scrolled.js b/src/utils/get-scrolled.js deleted file mode 100644 index f284059c..00000000 --- a/src/utils/get-scrolled.js +++ /dev/null @@ -1,11 +0,0 @@ -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 } -} diff --git a/src/utils/is-element-visible.js b/src/utils/is-element-visible.js deleted file mode 100644 index 611192a5..00000000 --- a/src/utils/is-element-visible.js +++ /dev/null @@ -1,30 +0,0 @@ -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' - ) -} diff --git a/src/utils/is-mobile.js b/src/utils/is-mobile.js deleted file mode 100644 index bd72c0a3..00000000 --- a/src/utils/is-mobile.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function isMobile(agent = navigator.userAgent) { - return /Android|iPhone|iPad|iPod/i.test(agent) -} diff --git a/src/utils/is-object.js b/src/utils/is-object.js deleted file mode 100644 index 9a39e331..00000000 --- a/src/utils/is-object.js +++ /dev/null @@ -1,8 +0,0 @@ -export default function isObject(x) { - return ( - x !== null && - x instanceof Object && - (x.constructor === Object || - Object.prototype.toString.call(x) === '[object Object]') - ) -} diff --git a/src/utils/is-transform-supported.js b/src/utils/is-transform-supported.js deleted file mode 100644 index d15b86ce..00000000 --- a/src/utils/is-transform-supported.js +++ /dev/null @@ -1,4 +0,0 @@ -export default function isTransformSupported() { - const style = document.documentElement.style - return 'transform' in style || 'WebkitTransform' in style -} diff --git a/src/utils/is-transition-supported.js b/src/utils/is-transition-supported.js deleted file mode 100644 index d15a23a9..00000000 --- a/src/utils/is-transition-supported.js +++ /dev/null @@ -1,4 +0,0 @@ -export default function isTransitionSupported() { - const style = document.documentElement.style - return 'transition' in style || 'WebkitTransition' in style -} diff --git a/src/utils/logger.js b/src/utils/logger.js deleted file mode 100644 index 3b79126d..00000000 --- a/src/utils/logger.js +++ /dev/null @@ -1,7 +0,0 @@ -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 - } -} diff --git a/src/utils/next-unique-id.js b/src/utils/next-unique-id.js deleted file mode 100644 index a043eff4..00000000 --- a/src/utils/next-unique-id.js +++ /dev/null @@ -1,6 +0,0 @@ -const nextUniqueId = (() => { - let uid = 0 - return () => uid++ -})() - -export default nextUniqueId diff --git a/test/instance/constructor.spec.js b/test/instance/constructor.spec.js deleted file mode 100644 index f4456417..00000000 --- a/test/instance/constructor.spec.js +++ /dev/null @@ -1,175 +0,0 @@ -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 `` 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 `` 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 - }) - }) -}) diff --git a/test/karma.conf.js b/test/karma.conf.js deleted file mode 100644 index 7c3b0167..00000000 --- a/test/karma.conf.js +++ /dev/null @@ -1,95 +0,0 @@ -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'] - }) - } -} diff --git a/test/polyfills/math-sign.spec.js b/test/polyfills/math-sign.spec.js deleted file mode 100644 index 6e59eefa..00000000 --- a/test/polyfills/math-sign.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -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 - }) - }) -}) diff --git a/test/sauce.conf.js b/test/sauce.conf.js deleted file mode 100644 index 37052252..00000000 --- a/test/sauce.conf.js +++ /dev/null @@ -1,73 +0,0 @@ -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 diff --git a/test/test.html b/test/test.html new file mode 100644 index 00000000..eb2e4779 --- /dev/null +++ b/test/test.html @@ -0,0 +1,5 @@ +
test 1
+ + + + diff --git a/test/test.js b/test/test.js new file mode 100644 index 00000000..4317ec3b --- /dev/null +++ b/test/test.js @@ -0,0 +1,7 @@ +var test = require('tape'); + +test('basic', function(t){ + var test_styles = document.querySelector('div').style; + t.equal(test_styles.cssText, '-webkit-transform: translateX(0px); opacity: 1; -webkit-transition-property: all; -webkit-transition-duration: 1.33s; -webkit-transition-timing-function: ease-in-out; -webkit-transition-delay: 0s; -webkit-perspective: 1000; -webkit-backface-visibility: hidden; '); + t.end(); +}); diff --git a/test/timeout.spec.js b/test/timeout.spec.js deleted file mode 100644 index 92c32611..00000000 --- a/test/timeout.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -// 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) -// }) -// }) diff --git a/test/utils/deep-assign.spec.js b/test/utils/deep-assign.spec.js deleted file mode 100644 index 8c8552c6..00000000 --- a/test/utils/deep-assign.spec.js +++ /dev/null @@ -1,58 +0,0 @@ -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) - }) - }) -}) diff --git a/test/utils/each.spec.js b/test/utils/each.spec.js deleted file mode 100644 index 8c47d293..00000000 --- a/test/utils/each.spec.js +++ /dev/null @@ -1,75 +0,0 @@ -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) - }) - }) - }) -}) diff --git a/test/utils/get-prefixed-css-prop.spec.js b/test/utils/get-prefixed-css-prop.spec.js deleted file mode 100644 index e3bf9c34..00000000 --- a/test/utils/get-prefixed-css-prop.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -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) - }) - }) -}) diff --git a/test/utils/is-mobile.spec.js b/test/utils/is-mobile.spec.js deleted file mode 100644 index 37dd9389..00000000 --- a/test/utils/is-mobile.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -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') - }) - }) -}) diff --git a/test/utils/is-object.spec.js b/test/utils/is-object.spec.js deleted file mode 100644 index 869b53bc..00000000 --- a/test/utils/is-object.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -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 - }) - }) -}) diff --git a/test/utils/is-transform-supported.spec.js b/test/utils/is-transform-supported.spec.js deleted file mode 100644 index 4dcc5c20..00000000 --- a/test/utils/is-transform-supported.spec.js +++ /dev/null @@ -1,9 +0,0 @@ -import isTransformSupported from '../../src/utils/is-transform-supported' - -describe('Utilities', () => { - describe('isTransformSupported()', () => { - it('should return true', () => { - expect(isTransformSupported()).to.be.true - }) - }) -}) diff --git a/test/utils/is-transition-supported.spec.js b/test/utils/is-transition-supported.spec.js deleted file mode 100644 index e0484e52..00000000 --- a/test/utils/is-transition-supported.spec.js +++ /dev/null @@ -1,9 +0,0 @@ -import isTransitionSupported from '../../src/utils/is-transition-supported' - -describe('Utilities', () => { - describe('isTransitionSupported()', () => { - it('should return true', () => { - expect(isTransitionSupported()).to.be.true - }) - }) -}) diff --git a/test/utils/logger.spec.js b/test/utils/logger.spec.js deleted file mode 100644 index 75b7ca4f..00000000 --- a/test/utils/logger.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -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()) - }) -}) diff --git a/test/utils/next-unique-id.spec.js b/test/utils/next-unique-id.spec.js deleted file mode 100644 index d31446d5..00000000 --- a/test/utils/next-unique-id.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -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') - }) - }) -})