[
  {
    "path": ".eslintignore",
    "content": "test/coverage\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"es6\": true\n  },\n  \"plugins\": [\"prettier\"],\n  \"extends\": [\"eslint:recommended\", \"prettier\"],\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 2017\n  },\n  \"globals\": {\n    \"module\": false\n  },\n  \"rules\": {\n    \"prettier/prettier\": \"error\",\n    \"linebreak-style\": [\"error\", \"unix\"],\n    \"quotes\": [\"error\", \"single\"],\n    \"semi\": [\"error\", \"always\"]\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# System Files\n*.swp\n.DS_Store\n\n# Node Files\n/node_modules/\nnpm-debug.log\nbower_components\n\n## Test Coverage Files\ntest/coverage/\n"
  },
  {
    "path": ".jestrc.json",
    "content": "{\n  \"collectCoverage\": true,\n  \"coverageDirectory\": \"test/coverage\",\n  \"collectCoverageFrom\": [\"src/**/*.js\"],\n  \"coveragePathIgnorePatterns\": [\"/node_modules\", \"/src/scrollNav.v2-7-3.js\"],\n  \"transform\": {\n    \"^.+\\\\.js$\": \"<rootDir>/jest.transform.js\"\n  },\n  \"setupFiles\": [\"<rootDir>/jest.setup.js\"]\n}\n"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"8\"\n  - \"10\"\ninstall:\n  - npm install\n  - npm install codecov\n  - codecov\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nReleases are be numbered in the semantic versioning format:\n\n`<major>.<minor>.<patch>`\n\nAnd constructed with these guidelines:\n\n* Breaking backwards compatibility bumps the major\n* New additions without breaking backwards compatibility bumps the minor\n* Bug fixes and misc changes bump the patch\n\nFor more information on semantic versioning, please visit http://semver.org/.\n\n## v3.0.2 - December 25, 2018\n\nImproved the documentation for browser support and removed polyfills covered by\npolyfills.io.\n\n## v3.0.1 - December 22, 2018\n\nUpdate file name casing for consistency.\n\n## v3.0.0 - December 22, 2018\n\nVersion 3 is a complete re-write of scrollnav to move the project away from\njQuery, update to the latest es6 features, and remove all production\ndependencies. This update also removes a number of user options to simplify\nthe API and reduces the amount of DOM manipulation to reduce likelihood of\nbrowser bugs. Includes a full set of test suites for all modules (unless\nnoted). Please read through the [readme](https://github.com/jimmynotjim/scrollnav/blob/master/README.md) for further\ndetails.\n\n## v2.7.3 - March 19, 2018\n\n* Added BEM-style active classes\n\n## v2.7.2 - March 18, 2018\n\n* Fixed grunt:jshint error\n* Fixed readme typo\n* Replaced andSelf() calls with addBack()\n* Updated semver version to fix vulnerability\n\n## v2.7.1 - February 18, 2015\n\n* Fixed package.json\n\n## v2.7.0 - February 11, 2015\n\n* Add `activeClass` setting for custom styles\n* Add `__focused-section` when a section is scrolled to\n\n## v2.6.0 - February 19, 2015\n\n* Added in-view and active logic to sub-sections\n\n## v2.5.0 - January 18, 2015\n\n* Add scrollToHash setting\n\n## v2.4.0 - November 13, 2014\n\n* Adds a new resetPos public function for updating the section positions when the page's content changes. Refer to the Readme for more details.\n\n## v2.3.1 - October 30, 2014\n\n* Patched an active section bug where no sections were active when a section matched the exact pixel offset calculated\n\n## v2.3.0 - October 9, 2014\n\n* Updated min Node to 0.10.\\*\n* Updated all Node plugins to latest\n\n## v2.2.0 - March 14, 2014\n\n* Added option to change the classname used throughout the plugin.\n* Updated jQuery dep to 1.9.\\*\n\n## v2.1.1 - December 13, 2013\n\nAdded `scrollnav` namespace to event listeners to avoid crashing in to user defined event listeners.\n\n## v2.1.0 - November 15, 2013\n\nv2.1 adds support for public methods, a new destroy method, custom callbacks for init, render and destroy, and small bugfixes.\n\n* Reorganized core to allow for new public methods\n* Added destroy method to core to allow for breaking down the plugin and it's dom \\* changes\n* Added the option to add callback functions to init, render and destroy\n* Squashed a bug when wrapping sub-sections in their div\n* Updated core init in test for easier reuse\n* Fixed Grunt test:browser task to only run in the browser\n* Added destroy tests to core module and a new module for the callbacks\n\n## v2.0.2 - October 31, 2013\n\nDouble releasing to include changelog and readme updates in Bower. One day I'll figure all this out :)\n\n## v2.0.1 - October 31, 2013\n\nBugfixes for the following:\n\n* package.json now includes semver: Ooops, sorry\n* Moved jQuery dependency from package.json to bower.json and updated jQuery dep to v1.8.\\*\n* Removed unneeded build dirs from bower install\n* Updated gitignore to ignore bower_components\n\n## v2.0.0 - October 11, 2013\n\nv2 is a complete re-write of scrollnav and includes better code organization, new options, updated options, grunt integration, and bugfixes. This update will most like break your options and markup from v1, please read through the following changes and reference the [readme](https://github.com/jimmynotjim/scrollnav/blob/master/README.md) for further details.\n\n* Updated to wrap each section in a `section` tag and support for sub-sections (as well as an option to change the wrapping element).\n* Fixed the active state when scrolling, no longer switches to the next nav item as the page finishes scrolling.\n* There are now two classes added to the nav item when scrolling, `active` and `in-view`. All sections within the view bounds are marked as `in-view` and the topmost section is marked as `active`.\n* Added grunt workflow. Edits to the plugin should be made in `/src/scrollnav.js` with tests to support them and jshint, qunit, concat and uglify should be run before submitting PRs.\n* Added parsing of url for an element hash. This fixes inbound urls with a hash not scrolling to the section if plugin loading is delayed.\n* Added arrow key navigation option. When enabled, users can jump from section to section using the up/down arrow keys.\n* Added option to change insertion target.\n* Added Bower support for easy project dependency management.\n* Updated the class names of scrollnav elements to follow BEM conventions.\n* Updated existing settings option naming for the following (_this may break your current setups_):\n  * `titleText` -> `headlineText`\n  * `location` -> `insertLocation`\n\n## v1.2.0\n\nAdded topLinkText option to customize the top link text (Thanks to [Wizcover][11]).\n\n## v1.1.4\n\nUpdated Readme to include new options.\n\n## v1.1.3\n\nAdded option for insertion location.\n\n## v1.1.2\n\nAdded option to remove Top Link from the nav.\n\n## v1.1.1\n\nFixed html rather than text being added to nav elements where heading contains nested elems.\n\n## v1.1.0\n\nAdded options for showing Headline Text and animation speed.\n\n## v1.0.1\n\nAdded ARIA support to nav elem (Thanks to [Jeff Coburn][10]).\n\n## v1.0.0\n\nInitial Release of scrollnav\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guideline\n\nThanks for contributing to scrollnav. Without you and the Open Source community\nthis project wouldn't be possible. Before you get started, please familiarize\nyourself with the project and its standards.\n\n## Issue Reports\n\nIf you're looking for a way to contribute to this project, your best bet is\nto [start with the current issues][1]. Whether it's a new feature you'd like\nadded, a bug you've found, or a question about using the plugin, there's always\na chance you're not alone. Take a look through both the open and closed issues\nbefore creating a new one yourself.\n\n### Improvements/features\n\nBefore working on a new feature please submit an issue suggesting the feature\nand ensure that it is greenlighted. I'd hate for you to spend your time\nbuilding a new feature that doesn't fit the project's scope or intentions.\n\n### Bugs\n\nBefore submitting a bug report, please create a reproducable test, either as a\nfailing unit test on your own fork, or by forking the starter demo from Codepen\nand adjusting as necessary. It's a whole lot easier to address issues if I can\nunderstand the situation. If it's not obvious what's broken in your demo,\ninclude step-by-step instructions on how to reproduce the problem within the\nissue description.\n\n## Pull Requests\n\nYou've found a bug or had a feature greenlighted and you're ready to dig in and\nget to work. Congrats! This project was built and continues to improve from\ndirect contributions from folks like you. Before you get too far, please be\nsure to read through and understand the steps below.\n\n### Important notes\n\nPlease don't edit files in the `dist` subdirectory as they are generated via\nNPM script. You'll find source code in the `src` subdirectory.\n\n### Code style\n\nThis project utilizes [Prettier][11] to ensure a consistent code style. Write\nyour changes however you prefer but be sure to run `npm run format` prior to\nsubmitting a Pull Request. Don't worry about making a lot of small commits,\nyour changes will be squashed within Github when the Pull Request is merged.\n\n### Testing\n\nThis project uses [Jest][12] for its unit tests. Prior to submitting a\nPull Request, please ensure new functionality is tested and adjustments to\nprevious functionality continue to pass. A failing test is better than no\ntest at all and gives me a place to start reviewing.\n\nAdditionally, it's a good idea to ensure your changes work in actual browsers.\nIncluding a demo on [Codepen][13] is a great way to ensure your changes are\nreviewed and merged in a timely manner. I've [created a base Pen][2] for you\nto fork and edit.\n\n### Modifying the code\n\nFirst, ensure that you are working with [Node.js][14] version 8 or later and\n[npm][15] version 5 or later. Older versions of Node are supported but it's\nbest to work with the Long Term Support (LTS), currently version 8.\n\n1. Fork and clone the repo.\n1. Run `npm install` to install the development dependencies.\n1. Create a new branch that's appropriately named. Please don't work directly\n   in your `master` branch, it makes testing locally difficult.\n1. Add failing tests for the change you want to make in the `test/tests`\n   directory. Run `npm test` to see the tests fail.\n1. Edit the source files in the `src` directory.\n1. Run `npm test` to see if the tests pass. Repeat steps 2-4 until done.\n1. Run `npm run build` to generate the transpiled files in the `dist`\n   directory.\n1. Update the documentation to reflect any changes in functionality.\n1. Commit and push your the branch to Github.\n1. Navigate to your branch on Github and then to the\n   `dist/scrollnav.min.js` file.\n1. Select the \"Raw\" button and copy the file URL.\n1. Fork the starter demo in Codepen and then click settings.\n1. Under \"Add External Scripts/Pens\" replace the existing\n   `dist/scrollnav.min.js` file URL with your raw file URL.\n1. Test the interactions and ensure your intended changes work as expected.\n1. Submit a Pull Request making sure to write a descriptive comment and\n   include your forked Codepen URL.\n\n**Note: If none of this makes sense, it's ok. Any contribution is a useful one,\neven if it's not accepted. Please feel free to reach out to me to ask for help\nor clarification, I'm [@jimmynotim on Twitter][21].**\n\nTo view all available build tasks run `npm run` without any arguments.\n\n[1]: https://github.com/jimmynotjim/scrollnav/issues\n[2]: https://codepen.io/jimmynotjim/pen/OZKeyd\n\n[11]: https://prettier.io/\n[12]: https://facebook.github.io/jest/\n[13]: https://codepen.io/\n[14]: http://nodejs.org/\n[15]: http://npmjs.org/\n\n[21]: https://twitter.com/jimmynotjim\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2018 James Wilson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "> This project is currently archived. I unfortunately don't have the time to maintain it. Thanks to everyone that contributed along the way, it wouldn't have been possible without you.\n\n# [scrollnav.js][1]\n\n[![npm](https://img.shields.io/npm/v/scrollnav.svg)](https://www.npmjs.com/package/scrollnav)\n[![Build Status](https://travis-ci.org/jimmynotjim/scrollnav.svg?branch=master)](https://travis-ci.org/jimmynotjim/scrollnav)\n[![Codecov](https://img.shields.io/codecov/c/github/codecov/example-python.svg)](https://codecov.io/gh/jimmynotjim/scrollnav)\n[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/jimmynotjim/scrollnav/blob/master/LICENSE-MIT)\n\n## Introduction\n\nscrollnav.js is a small (2.4kb gzipped), dependency free JavaScript plugin\nfor auto generating single page navigation with active highlighting. Useful\nfor creating a Table of Contents for a large document (think Wikis),\nnavigation for a single page website, or anything else you might think of.\n\nscrollnav works by scanning a block of content for section landmarks\n(typically heading elements) and generating a list of links from those\nlandmarks. It then tracks the scroll location of the document and highlights\nthe appropriate link. While previous versions injected wrappers within the\ncontent, the current version (ver 3) takes a much lighter approach, only\nchanging the DOM as necessary. Visit the live demo at [scrollnav.com][1]\nto see for yourself.\n\n## Browser Compatibility\n\nTo keep scrollnav small, default support starts with\n[ES6 compatible browsers](https://caniuse.com/#feat=arrow-functions). To support\n[ES5 compatible browsers](https://caniuse.com/#feat=es5) you must provide your\nown polyfills or rely on a third party library like pollyfills.io. I personally\nuse the following\n[polyfill.io](https://github.com/Financial-Times/polyfill-service) feature\nparameters to support scrollnav in IE 10 & 11.\n\n```html\n<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=default,NodeList.prototype.forEach,Array.prototype.includes\"></script>\n```\n\nTo add your own polyfills you will need to build the project from source.\n\n## Getting Started\n\nThe compiled, production ready plugin is available in the `dist` directory.\nPlease don't use the `src` directory unless you plan to build the entire\nsource.\n\n### Install\n\n#### Download\n\n[scrollnav@v3.0.2](https://unpkg.com/scrollnav@3.0.2/dist/scrollnav.min.umd.js)\n\n```html\n<script src=\"[your assets directory]/scrollnav.min.umd.js\"></script>\n```\n\n#### CDN\n\n```html\n<script src=\"https://unpkg.com/scrollnav@3.0.2/dist/scrollnav.min.umd.js\"></script>\n```\n\n#### Package manager\n\n[Yarn][13]: `yarn add scrollnav`\n\nIt's the new hotness, it's also better at managing dependencies than all it's predecesors.\n\n[NPM][12]: `npm install scrollnav`\n\nGood'ol NPM, it's always there, except when it isn't. Things have settled down a bit, but it was dicey there for a while. Even still, there's a reason even Yarn uses the NPM registry.\n\n[Bower][11]: `bower install scrollnav --save`\n\nThe folks from Bower no longer recommend using Bower. Luckily they've provided a guide on [how to migrate to Yarn](https://bower.io/blog/2017/how-to-migrate-away-from-bower/). If you don't want to or can't migrate, scrollnav will continue to be available on Bower as long as it continues to run.\n\n\n### Usage\n\nscrollnav works by scanning the given [HTML Node Element][25] for section\nlandmarks, by default `h2` elements, that it then uses to generate the nav.\nIf we were to look at a typical document, it might look like this:\n\n```html\n  <div class=\"main-content\">\n    <h2>First section</h2>\n    ...\n    <h2>Second section</h2>\n    ...\n    <h2>Third section</h2>\n    ...\n  </div>\n```\n\n#### Initialize\n\nFirst, initialize scrollnav with the  HTML Element. In this example we'll use\n`.querySelector()` but you could also use `.getElementByID()` or\n`.getElementByClassName()`.\n\n```js\nconst content = document.querySelector('.main-content');\nscrollnav.init(content);\n```\n\nscrollnav will then loop through the the `h2` elements, add an ID if they don't\nalready have one, build the nav, and then inject it just before the content\nNode. The result for our example document would look like this:\n\n```html\n<nav class=\"scroll-nav\">\n  <ol class=\"scroll-nav__list\">\n    <li class=\"scroll-nav__item\">\n      <a class=\"scroll-nav__link\" href=\"#scroll-nav__1\">\n        First heading\n      <a>\n    </li>\n    ...\n  </ol>\n</nav>\n<div class=\"main-content\">\n  <h2 id=\"scroll-nav__1\">First Heading</h2>\n  ...\n</div>\n```\n\n#### Styles\n\nTo keep the plugin simple there are no styles added to the navigation, that's\nall up to you ([view the demo site][1] for exmaples of the most common use\ncases). The nav structure provides [BEM Methodology][23] class names for each\nof the elements to provide consistent styling hooks (for a good overview read\n[MindBEMding - getting your head 'round BEM syntax][24]). As the user scrolls\nthe document, scrollnav adds a `scroll-nav__item--active` modifier for the\nitem's relative section that currently intersects with the activation\nthreshold ([enable `debug` mode](#additional-options) to highlight the\nthreshold).\n\n### Settings and options\n\nscrollnav includes some default settings that work for most situations, but if\nyour project requires a bit more customization, scrollnav can most likely meet\nthose. To modify either, pass in a single object (include settings and options\nas one object) as the second argument like this:\n\n```js\n  scrollnav.init(content, {\n    key: value\n  });\n```\n\n#### Default settings\n\nThe following settings are editable to overwrite the default.\n\n```js\n{\n  sections: 'selector',\n  // string\n  //\n  // Sets the querySelector for the content's section landmarks, by default\n  // it's 'h2'.\n\n  insertTarget: targetNode,\n  // HTML Node\n  //\n  // Sets the target Node for injecting the navigation, by default it's the\n  // content Node passed to scrollnav.\n\n  insertLocation: 'relativeLocation'\n  // string\n  //\n  // Sets the injection location relative to the insertTarget, by default it's\n  // 'before'.\n  //\n  // available options are 'append', 'prepend', 'after', or 'before'\n\n  easingStyle: 'easingName',\n  // string\n  //\n  // Sets the easing type for the scroll animation that is triggered by the\n  // click event on a nav item, by default it's 'easeOutQuad'.\n  //\n  // available options are 'linear' 'easeInQuad', 'easeOutQuad',\n  // 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic',\n  // 'easeInQuart', 'easeOutQuart', 'easeInOutQuart', 'easeInQuint',\n  // 'easeOutQuint', easeInOutQuint\n\n  updateHistory: true\n  // boolean\n  //\n  // Sets the history behavior when a nav item is clicked, by default it's true\n}\n```\n\n#### Additional options\n\nThese additional options are editable but are not set by default.\n\n```js\n{\n  subSections: '...',\n  // string\n  //\n  // Sets the querySelector for the content's sub-section landmarks.\n\n  onScroll: function() {...},\n  // function\n  //\n  // Sets the callback to be triggered after the window scrolls when triggered\n  // by the click event on a nav item.\n\n  onInit: function() {...},\n  // function\n  //\n  // Sets the callback to be triggered after the .init() method has completed.\n\n  onUpdatePositions: function() {...},\n  // function\n  //\n  // Sets the callback to be triggered after the .updatePositions() method\n  // has completed.\n\n  onDestroy: function() {...},\n  // function\n  //\n  // Sets the callback to be triggered after the .destroy() method has\n  // completed.\n\n  debug: false\n  // boolean\n  //\n  // Enables scrollnav's built in debug mode to log errors to the console and\n  // display the active area threshold on screen, helpful for when you've hit a\n  // snag you can't easily identify.\n}\n```\n\n#### Available methods\n\nIn addition to the `.init()` method scrollnav provides two additional public\nmethods.\n\n#### destroy()\n\nTo remove the current instance of scrollnav call the destroy method. If you\nneed to trigger a callback after scrollnav has been removed, use the\n`onDestroy` option described above (passed either in the original init or with\nthe destroy method).\n\n```js\nscrollnav.destroy();\n```\n\n#### updatePositions()\n\nscrollnav doesn't track outside DOM changes. If your page's content is dynamic\nand updates after scrollnav is initialized you'll need to recalcuate the\nposition data with the updatePositions method. If you need to trigger a\ncallback after the position data has been recalculated, use the\n`onUpdatePositions` option described above (passed either in the original init\nor with the updatePositions method).\n\n```js\nscrollnav.updatePositions();\n```\n\n## Issues\n\nPlease read and understand the [Contributing Guidelines][4] prior to [opening\nan issue][2]. Ensuring your issue conforms to the guidelines gives it a better\nchance I'll be able to help address it.\n\n## Questions\nFor questions about using scrollnav in your own project, your best bet is to\npost it to [Stack Overflow][21]. The community there is great at lending a hand\nand can often respond faster than I can, plus it becomes searchable for future\ndevelopers who may run into the same question. If you're still stuck, please\nfeel free to reach out to me to ask for help or clarification, I'm [@jimmynotim\non Twitter][22].\n\n## Changelog\n\nv3.0.2 is the current stable release. For detailed changes in each release\nplease refer to the [release notes][5]. Please be sure you understand the\nchanges before updating, v3 is a complete re-write of the plugin (as is v2\ncompared to v1 before it).\n\n## Contributions\n\nscrollnav is built and maintained by [James Wilson (@jimmynotjim)][31].\nI wouldn't be able to continue this project without a lot of help from the\nOpen Source community. I welcome feedback and enhancements, but first, please\nmake sure to read the [Contributing Guide][4].\n\nThank you to everyone who has already contributed to scrollnav!\n\n* [Chris Garcia (@pixelbandito)][43]\n* [Eric Clemmons (@ericclemmons)][32]\n* [Felix Borzik (@Borzik)][39]\n* [Jeff Byrnes (@jeffbyrnes)][33]\n* [Jeff Coburn (@coburnicus)][34]\n* [Jen Germann (@germanny)][35]\n* [Jim Schmid (@sheeep)][44]\n* [Marc Amos (@marcamos)][38]\n* [Masud Rahman (@frutiger)][40]\n* [Meghdad Hadidi (@MeghdadHadidi)][37]\n* [Michael Benin (@michael-benin-CN)][45]\n* [Rob Loach (@RobLoach)][41]\n* [Thomas Guillary @thomasguillory][46]\n* [Will Moore (@willthemoor)][42]\n* [Wizcover (@wizcover)][36]\n\n## License\n\nscrollnav is Copyright &copy; 2012-2018 James Wilson, released under the\n[MIT license][3]. This means you can re-create, edit or share the plugin as\nlong as you maintain the same open licensing.\n\n\n[1]: http://scrollnav.com\n[2]: https://github.com/jimmynotjim/scrollnav/issues\n[3]: https://github.com/jimmynotjim/scrollnav/blob/master/LICENSE-MIT\n[4]: https://github.com/jimmynotjim/scrollnav/blob/master/CONTRIBUTING.md\n[5]: https://github.com/jimmynotjim/scrollnav/blob/master/CHANGELOG.md\n\n[11]: https://bower.io/\n[12]: https://www.npmjs.com/package/scrollnav\n[13]: https://yarnpkg.com/en/package/scrollnav\n\n[21]: https://stackoverflow.com/questions\n[22]: https://twitter.com/jimmynotjim\n[23]: http://bem.info/method/\n[24]: http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/\n[25]: https://developer.mozilla.org/en-US/docs/Web/API/Element\n\n[31]: http://github.com/jimmynotjim\n[32]: https://github.com/ericclemmons\n[33]: https://github.com/jeffbyrnes\n[34]: https://github.com/coburnicus\n[35]: https://github.com/germanny\n[36]: https://github.com/wizcover\n[37]: https://github.com/MeghdadHadidi\n[38]: http://github.com/marcamos\n[39]: http://github.com/borzik\n[40]: http://github.com/frutiger\n[41]: http://github.com/RobLoach\n[42]: http://github.com/willthemoor\n[43]: http://github.com/pixelbandito\n[44]: http://github.com/sheeep\n[45]: http://github.com/michael-benin-CN\n[46]: http://github.com/thomasguillory\n\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"scrollnav\",\n  \"version\": \"3.0.2\",\n  \"author\": {\n    \"name\": \"James Wilson\",\n    \"email\": \"jimmynotjim@me.com\",\n    \"url\": \"http://jimmynotjim.com\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/jimmynotjim/scrollnav.git\"\n  },\n  \"main\": [\"./dist/scrollnav.min.umd.js\"],\n  \"dependencies\": {},\n  \"ignore\": [\n    \"node_modules\",\n    \"libs\",\n    \"src\",\n    \"test\",\n    \".*\",\n    \"*.json\",\n    \"CONTRIBUTING.md\",\n    \"*.txt\"\n  ],\n  \"keywords\": [\n    \"scrollnav\",\n    \"javascript\",\n    \"es6\",\n    \"jquery\",\n    \"navigation\",\n    \"scrolling\",\n    \"sticky nav\",\n    \"scrolling nav\"\n  ]\n}\n"
  },
  {
    "path": "dist/scrollnav.min.mjs",
    "content": "function f(f,h){var y,w={};for(y in f)Object.prototype.hasOwnProperty.call(f,y)&&(w[y]=f[y]);for(y in h)Object.prototype.hasOwnProperty.call(h,y)&&(w[y]=h[y]);return w}function h(f,h){if(\"object\"!=typeof f)return Promise.reject(new Error(\"First argument must be an object\"));if(\"object\"!=typeof(h=h||document.body))return Promise.reject(new Error(\"Second argument must be an object\"));var y=h.getBoundingClientRect();return f.getBoundingClientRect().top-y.top}function y(f,w,E){void 0===E&&(E=\"scroll-nav\");var L=[];return E+=\"__\",f.forEach(function(f,O){var x=[],j=function(f,h){if(\"object\"!=typeof f)return Promise.reject(new Error(\"First argument must be an object\"));var y=f.id;if(!y){if(\"string\"!=typeof h)return Promise.reject(new Error(\"Second argument must be a string\"));f.id=y=h}return y}(f,E+(O+1));w.subSections&&f.matches(w.sections)&&(x=y(function(f,h,y){var w=[];for(f=f.nextElementSibling;f&&!f.matches(h);)!y||f.matches(y)?(w.push(f),f=f.nextElementSibling):f=f.nextElementSibling;return w}(f,w.sections,w.subSections),w,j));L.push({id:j,text:f.innerText||f.textContent,offsetTop:h(f),subSections:x})}),L}function w(f){var h=document.createElement(\"nav\");return h.className=\"scroll-nav\",h.innerHTML=function f(h,y){void 0===y&&(y=!1);var w=\"scroll-nav\"+(y?\"__sub-\":\"__\"),E=\"\\n    \"+h.map(function(h){return'<li class=\"'+w+'item\" data-sn-section=\"'+h.id+'\">\\n            <a class=\"'+w+'link\" href=\"#'+h.id+'\">'+h.text+\"</a>\\n            \"+(h.subSections&&h.subSections.length?\"\"+f(h.subSections,!0):\"\")+\"\\n          </li>\"}).join(\"\")+\"\\n  \";return'\\n    <ol class=\"'+w+'list\">\\n      '+E+\"\\n    </ol>\\n  \"}(f),h}function E(f){return f.forEach(function(f){var y=document.querySelector(\"#\"+f.id);f.offsetTop=h(y),f.subSections.length&&(f.subSections=E(f.subSections))}),f}function L(f,h){var y=f.getAttribute(\"href\");return\"#\"===y.charAt(0)&&(y=y.substr(1)),function f(h,y){var w;h.forEach(function(h){h.id===y&&(w=h),h.subSections&&void 0===w&&(w=f(h.subSections,y))});return w}(h,y).offsetTop}var O,x,j,_=function(f){return function(h){return Math.pow(h,f)}},I=function(f){return function(h){return 1-Math.abs(Math.pow(h-1,f))}},Q=function(f){return function(h){return h<.5?_(f)(2*h)/2:I(f)(2*h-1)/2+.5}},C={linear:Q(1),easeInQuad:_(2),easeOutQuad:I(2),easeInOutQuad:Q(2),easeInCubic:_(3),easeOutCubic:I(3),easeInOutCubic:Q(3),easeInQuart:_(4),easeOutQuart:I(4),easeInOutQuart:Q(4),easeInQuint:_(5),easeOutQuint:I(5),easeInOutQuint:Q(5)};function M(f,h){return new Promise(function(y,w){if(\"number\"!=typeof f)return w(new Error(\"First argument must be a number\"));if(\"string\"!=typeof(h=h||\"linear\"))return w(new Error(\"Second argument must be a string\"));var E,L=window.pageYOffset,O=f-L,x=function(f){var h=Math.abs(f/2);return Math.min(Math.max(h,250),1200)}(O),j=20,_=0;!function f(){E=C[h]((_+=j)/x),window.scroll(0,E*O+L),_<x?setTimeout(f,j):y(window.pageYOffset)}()})}function q(f){function h(){var h=window.scrollY||window.pageYOffset||document.body.scrollTop,y=h+.4*window.innerHeight,w=function f(h,y,w){var E,L;h.forEach(function(f){f.offsetTop>w?!E&&f.offsetTop<y&&(E=f):E=f}),E&&E.subSections.length&&(L=f(E.subSections,y,w))&&(E=L);return E}(f.data,h,y);return function(f,h){var y=h.querySelector(\"[data-sn-active]\");if(f){var w=h.querySelector(\"[data-sn-section=\"+f.id+\"]\");w&&w!==y&&(y&&(y.classList.remove(\"scroll-nav__item--active\"),y.removeAttribute(\"data-sn-active\")),w.classList.add(\"scroll-nav__item--active\"),w.setAttribute(\"data-sn-active\",!0))}else y&&(y.classList.remove(\"scroll-nav__item--active\"),y.removeAttribute(\"data-sn-active\"))}(w,f.nav),w}return window.addEventListener(\"scroll\",h),h}function B(f){return f instanceof Element}export default{init:function(h,_){if(this.settings=f({sections:\"h2\",insertTarget:h,insertLocation:\"before\",easingStyle:\"easeOutQuad\",updateHistory:!0},_),B(h))if(!this.settings.insertTarget||B(this.settings.insertTarget))if([\"append\",\"prepend\",\"after\",\"before\"].includes(this.settings.insertLocation)){var I,Q,C,F,R=h.querySelectorAll(this.settings.sections);if(R.length)return this.data=y(R,this.settings),this.nav=w(this.data),Q=(I=this).settings.insertTarget,\"append\"===(C=I.settings.insertLocation)?Q.appendChild(I.nav):\"prepend\"===C?Q.insertBefore(I.nav,Q.firstChild):\"before\"===C?Q.parentNode.insertBefore(I.nav,Q):\"after\"===C&&Q.parentNode.insertBefore(I.nav,Q.nextSibling),O=function(f){var h=f.settings;function y(y){y.preventDefault();var w=.39*window.innerHeight;return M(L(y.target,f.data)-w,h.easingStyle).then(function(){h.updateHistory&&history.replaceState({},\"\",y.target.getAttribute(\"href\")),h.onScroll&&h.onScroll()})}return f.nav.querySelectorAll(\"a\").forEach(function(f){f.addEventListener(\"click\",y)}),y}(this),x=q(this),j=function(f){function h(){f.data=E(f.data)}return window.addEventListener(\"resize\",h),h}(this),this.settings.debug&&((F=document.createElement(\"div\")).className=\"snDebugger\",F.setAttribute(\"style\",\"\\n      position: fixed;\\n      top: 40%;\\n      height: 0px;\\n      border-bottom:5px solid red;\\n      border-top: 5px solid blue;\\n      width: 100%;\\n      opacity: .5;\\n      pointer-events: none;\\n    \"),document.body.appendChild(F)),this.settings.onInit?this.settings.onInit():void 0;this.settings.debug&&console.error('\\n        scrollnav build failed, could not find any \"'+this.settings.sections+'\"\\n        elements inside of \"'+h+'\"\\n      ')}else this.settings.debug&&console.error('\\n        scrollnav build failed, options.insertLocation \"'+this.settings.insertLocation+'\" is not a valid option\\n      ');else this.settings.debug&&console.error('\\n        scrollnav build failed, options.insertTarget \"'+h+'\" is not an HTML Element\\n      ');else this.settings.debug&&console.error('\\n        scrollnav build failed, content argument \"'+h+'\" is not an HTML Element\\n      ')},destroy:function(h){if(this.settings=f(this.settings,h),function(f,h){f.querySelectorAll(\"a\").forEach(function(f){f.removeEventListener(\"click\",h)})}(this.nav,O),function(f){window.removeEventListener(\"scroll\",f)}(x),function(f){window.removeEventListener(\"resize\",f)}(j),this.nav.remove(),this.settings.onDestroy)return this.settings.onDestroy()},updatePositions:function(h){if(this.settings=f(this.settings,h),this.data=E(this.data),this.settings.onUpdatePositions)return this.settings.onUpdatePositions()}};\n//# sourceMappingURL=scrollnav.min.mjs.map\n"
  },
  {
    "path": "dist/scrollnav.min.umd.js",
    "content": "!function(f,h){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=h():\"function\"==typeof define&&define.amd?define(h):f.scrollnav=h()}(this,function(){function f(f,h){var y,w={};for(y in f)Object.prototype.hasOwnProperty.call(f,y)&&(w[y]=f[y]);for(y in h)Object.prototype.hasOwnProperty.call(h,y)&&(w[y]=h[y]);return w}function h(f,h){if(\"object\"!=typeof f)return Promise.reject(new Error(\"First argument must be an object\"));if(\"object\"!=typeof(h=h||document.body))return Promise.reject(new Error(\"Second argument must be an object\"));var y=h.getBoundingClientRect();return f.getBoundingClientRect().top-y.top}function y(f,w,E){void 0===E&&(E=\"scroll-nav\");var L=[];return E+=\"__\",f.forEach(function(f,O){var x=[],j=function(f,h){if(\"object\"!=typeof f)return Promise.reject(new Error(\"First argument must be an object\"));var y=f.id;if(!y){if(\"string\"!=typeof h)return Promise.reject(new Error(\"Second argument must be a string\"));f.id=y=h}return y}(f,E+(O+1));w.subSections&&f.matches(w.sections)&&(x=y(function(f,h,y){var w=[];for(f=f.nextElementSibling;f&&!f.matches(h);)!y||f.matches(y)?(w.push(f),f=f.nextElementSibling):f=f.nextElementSibling;return w}(f,w.sections,w.subSections),w,j));L.push({id:j,text:f.innerText||f.textContent,offsetTop:h(f),subSections:x})}),L}function w(f){var h=document.createElement(\"nav\");return h.className=\"scroll-nav\",h.innerHTML=function f(h,y){void 0===y&&(y=!1);var w=\"scroll-nav\"+(y?\"__sub-\":\"__\"),E=\"\\n    \"+h.map(function(h){return'<li class=\"'+w+'item\" data-sn-section=\"'+h.id+'\">\\n            <a class=\"'+w+'link\" href=\"#'+h.id+'\">'+h.text+\"</a>\\n            \"+(h.subSections&&h.subSections.length?\"\"+f(h.subSections,!0):\"\")+\"\\n          </li>\"}).join(\"\")+\"\\n  \";return'\\n    <ol class=\"'+w+'list\">\\n      '+E+\"\\n    </ol>\\n  \"}(f),h}function E(f){return f.forEach(function(f){var y=document.querySelector(\"#\"+f.id);f.offsetTop=h(y),f.subSections.length&&(f.subSections=E(f.subSections))}),f}function L(f,h){var y=f.getAttribute(\"href\");return\"#\"===y.charAt(0)&&(y=y.substr(1)),function f(h,y){var w;h.forEach(function(h){h.id===y&&(w=h),h.subSections&&void 0===w&&(w=f(h.subSections,y))});return w}(h,y).offsetTop}var O,x,j,_=function(f){return function(h){return Math.pow(h,f)}},I=function(f){return function(h){return 1-Math.abs(Math.pow(h-1,f))}},Q=function(f){return function(h){return h<.5?_(f)(2*h)/2:I(f)(2*h-1)/2+.5}},C={linear:Q(1),easeInQuad:_(2),easeOutQuad:I(2),easeInOutQuad:Q(2),easeInCubic:_(3),easeOutCubic:I(3),easeInOutCubic:Q(3),easeInQuart:_(4),easeOutQuart:I(4),easeInOutQuart:Q(4),easeInQuint:_(5),easeOutQuint:I(5),easeInOutQuint:Q(5)};function M(f,h){return new Promise(function(y,w){if(\"number\"!=typeof f)return w(new Error(\"First argument must be a number\"));if(\"string\"!=typeof(h=h||\"linear\"))return w(new Error(\"Second argument must be a string\"));var E,L=window.pageYOffset,O=f-L,x=function(f){var h=Math.abs(f/2);return Math.min(Math.max(h,250),1200)}(O),j=20,_=0;!function f(){E=C[h]((_+=j)/x),window.scroll(0,E*O+L),_<x?setTimeout(f,j):y(window.pageYOffset)}()})}function q(f){function h(){var h=window.scrollY||window.pageYOffset||document.body.scrollTop,y=h+.4*window.innerHeight,w=function f(h,y,w){var E,L;h.forEach(function(f){f.offsetTop>w?!E&&f.offsetTop<y&&(E=f):E=f}),E&&E.subSections.length&&(L=f(E.subSections,y,w))&&(E=L);return E}(f.data,h,y);return function(f,h){var y=h.querySelector(\"[data-sn-active]\");if(f){var w=h.querySelector(\"[data-sn-section=\"+f.id+\"]\");w&&w!==y&&(y&&(y.classList.remove(\"scroll-nav__item--active\"),y.removeAttribute(\"data-sn-active\")),w.classList.add(\"scroll-nav__item--active\"),w.setAttribute(\"data-sn-active\",!0))}else y&&(y.classList.remove(\"scroll-nav__item--active\"),y.removeAttribute(\"data-sn-active\"))}(w,f.nav),w}return window.addEventListener(\"scroll\",h),h}function B(f){return f instanceof Element}return{init:function(h,_){if(this.settings=f({sections:\"h2\",insertTarget:h,insertLocation:\"before\",easingStyle:\"easeOutQuad\",updateHistory:!0},_),B(h))if(!this.settings.insertTarget||B(this.settings.insertTarget))if([\"append\",\"prepend\",\"after\",\"before\"].includes(this.settings.insertLocation)){var I,Q,C,F,R=h.querySelectorAll(this.settings.sections);if(R.length)return this.data=y(R,this.settings),this.nav=w(this.data),Q=(I=this).settings.insertTarget,\"append\"===(C=I.settings.insertLocation)?Q.appendChild(I.nav):\"prepend\"===C?Q.insertBefore(I.nav,Q.firstChild):\"before\"===C?Q.parentNode.insertBefore(I.nav,Q):\"after\"===C&&Q.parentNode.insertBefore(I.nav,Q.nextSibling),O=function(f){var h=f.settings;function y(y){y.preventDefault();var w=.39*window.innerHeight;return M(L(y.target,f.data)-w,h.easingStyle).then(function(){h.updateHistory&&history.replaceState({},\"\",y.target.getAttribute(\"href\")),h.onScroll&&h.onScroll()})}return f.nav.querySelectorAll(\"a\").forEach(function(f){f.addEventListener(\"click\",y)}),y}(this),x=q(this),j=function(f){function h(){f.data=E(f.data)}return window.addEventListener(\"resize\",h),h}(this),this.settings.debug&&((F=document.createElement(\"div\")).className=\"snDebugger\",F.setAttribute(\"style\",\"\\n      position: fixed;\\n      top: 40%;\\n      height: 0px;\\n      border-bottom:5px solid red;\\n      border-top: 5px solid blue;\\n      width: 100%;\\n      opacity: .5;\\n      pointer-events: none;\\n    \"),document.body.appendChild(F)),this.settings.onInit?this.settings.onInit():void 0;this.settings.debug&&console.error('\\n        scrollnav build failed, could not find any \"'+this.settings.sections+'\"\\n        elements inside of \"'+h+'\"\\n      ')}else this.settings.debug&&console.error('\\n        scrollnav build failed, options.insertLocation \"'+this.settings.insertLocation+'\" is not a valid option\\n      ');else this.settings.debug&&console.error('\\n        scrollnav build failed, options.insertTarget \"'+h+'\" is not an HTML Element\\n      ');else this.settings.debug&&console.error('\\n        scrollnav build failed, content argument \"'+h+'\" is not an HTML Element\\n      ')},destroy:function(h){if(this.settings=f(this.settings,h),function(f,h){f.querySelectorAll(\"a\").forEach(function(f){f.removeEventListener(\"click\",h)})}(this.nav,O),function(f){window.removeEventListener(\"scroll\",f)}(x),function(f){window.removeEventListener(\"resize\",f)}(j),this.nav.remove(),this.settings.onDestroy)return this.settings.onDestroy()},updatePositions:function(h){if(this.settings=f(this.settings,h),this.data=E(this.data),this.settings.onUpdatePositions)return this.settings.onUpdatePositions()}}});\n//# sourceMappingURL=scrollnav.min.umd.js.map\n"
  },
  {
    "path": "jest.setup.js",
    "content": "import '@babel/polyfill';\n\nwindow.scrollTo = window.scroll = (xVal, yVal) => {\n  window.pageXOffset = xVal;\n  window.pageYOffset = yVal;\n};\n\nwindow.scrollBy = (xVal, yVal) => {\n  const xStart = window.getPageXOffset;\n  const yStart = window.getPageYOffset;\n  window.pageXOffset = xStart + xVal;\n  window.pageYOffset = yStart + yVal;\n};\n"
  },
  {
    "path": "jest.transform.js",
    "content": "module.exports = require('babel-jest').createTransformer({\n  presets: ['@babel/preset-env']\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"scrollnav\",\n  \"version\": \"3.0.2\",\n  \"title\": \"scrollnav\",\n  \"author\": {\n    \"name\": \"James Wilson\",\n    \"email\": \"jimmynotjim@me.com\",\n    \"url\": \"http://jimmynotjim.com\"\n  },\n  \"description\": \"A small, dependency free JavaScript plugin for auto generating single page navigation\",\n  \"keywords\": [\n    \"scrollnav\",\n    \"javascript\",\n    \"es6\",\n    \"jquery\",\n    \"navigation\",\n    \"scrolling\",\n    \"sticky nav\",\n    \"scrolling nav\"\n  ],\n  \"homepage\": \"http://scrollnav.com\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/jimmynotjim/scrollnav.git\"\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"main\": \"dist/scrollnav.min.umd.js\",\n  \"bugs\": \"https://github.com/jimmynotjim/scrollnav/issues\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">= 10.0.0\"\n  },\n  \"scripts\": {\n    \"build\": \"microbundle --compress false --name scrollnav\",\n    \"format\": \"npm-run-all format:*\",\n    \"format:source\": \"prettier --write 'src/**/*.js'\",\n    \"format:test\": \"prettier --write 'test/**/*.js' !/coverage\",\n    \"lint\": \"npm-run-all lint:*\",\n    \"lint:source\": \"eslint 'src/**/*.js'\",\n    \"lint:test\": \"eslint 'test/**/*.js'\",\n    \"precommit\": \"npm run format && npm run lint && npm run build\",\n    \"test\": \"jest --config .jestrc.json --no-cache\",\n    \"watch\": \"microbundle --watch\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.2.2\",\n    \"@babel/polyfill\": \"^7.2.5\",\n    \"@babel/preset-env\": \"^7.2.3\",\n    \"@jarmee/jest-dom-custom-matchers\": \"^1.0.0\",\n    \"babel-core\": \"^7.0.0-bridge.0\",\n    \"babel-jest\": \"^23.6.0\",\n    \"eslint\": \"^5.10.0\",\n    \"eslint-config-prettier\": \"^3.3.0\",\n    \"eslint-plugin-prettier\": \"^3.0.0\",\n    \"husky\": \"^1.2.1\",\n    \"jest\": \"^23.6.0\",\n    \"microbundle\": \"^0.9.0\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"prettier\": \"^1.15.3\"\n  },\n  \"source\": \"src/scrollnav.js\",\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"npm run precommit\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/scrollNav.v2-7-3.js",
    "content": "/*\n * scrollNav\n * http://scrollnav.com\n *\n * Copyright (c) 2013-2016 James Wilson\n * Licensed under the MIT license.\n */\n\n/* eslint-disable */\n\n(function($) {\n  // Animate scrolling to section location\n  var scroll_to = function(value, speed, offset, animated) {\n    if ($(value).length > 0) {\n      var destination = $(value).offset().top;\n      speed = animated ? speed : 0;\n\n      // Add a class to the scrolled-to section\n      $('.' + S.settings.className + '__focused-section').removeClass(\n        S.settings.className + '__focused-section'\n      );\n      $(value).addClass(S.settings.className + '__focused-section');\n\n      $('html:not(:animated),body:not(:animated)').animate(\n        { scrollTop: destination - offset },\n        speed\n      );\n    }\n  };\n\n  // Get url hash if one exists\n  var get_hash = function() {\n    return window.location.hash;\n  };\n\n  var S = {\n    classes: {\n      loading: 'sn-loading',\n      failed: 'sn-failed',\n      success: 'sn-active'\n    },\n    defaults: {\n      sections: 'h2',\n      subSections: false,\n      sectionElem: 'section',\n      className: 'scroll-nav',\n      showHeadline: true,\n      headlineText: 'Scroll To',\n      showTopLink: true,\n      topLinkText: 'Top',\n      fixedMargin: 40,\n      scrollOffset: 40,\n      animated: true,\n      speed: 500,\n      insertLocation: 'insertBefore',\n      arrowKeys: false,\n      scrollToHash: true,\n      onInit: null,\n      onRender: null,\n      onDestroy: null,\n      onResetPos: null\n    },\n    _set_body_class: function(state) {\n      // Set and swap our loading hooks to the body\n\n      var $body = $('body');\n\n      if (state === 'loading') {\n        $body.addClass(S.classes.loading);\n      } else if (state === 'success') {\n        $body.removeClass(S.classes.loading).addClass(S.classes.success);\n      } else {\n        $body.removeClass(S.classes.loading).addClass(S.classes.failed);\n      }\n    },\n    _find_sections: function($el) {\n      // Find the html for each section\n\n      var target_elems = S.settings.sections;\n      var raw_html = [];\n\n      if (S.settings.showTopLink) {\n        var $firstElem = $el.children().first();\n\n        if (!$firstElem.is(target_elems)) {\n          raw_html.push($firstElem.nextUntil(target_elems).addBack());\n        }\n      }\n\n      $el.find(target_elems).each(function() {\n        raw_html.push(\n          $(this)\n            .nextUntil(target_elems)\n            .addBack()\n        );\n      });\n\n      S.sections = {\n        raw: raw_html\n      };\n    },\n    _setup_sections: function(sections) {\n      // Wrap each section and add it's details to the section array\n\n      var section_data = [];\n\n      $(sections).each(function(i) {\n        var sub_data = [];\n        var $this_section = $(this);\n        var section_id = 'scrollNav-' + (i + 1);\n        var isFirst = function() {\n          return i === 0;\n        };\n        var hasHeading = function() {\n          return !$this_section.eq(0).is(S.settings.sections);\n        };\n        var text =\n          S.settings.showTopLink && isFirst() && hasHeading()\n            ? S.settings.topLinkText\n            : $this_section.filter(S.settings.sections).text();\n\n        $this_section.wrapAll(\n          '<' +\n            S.settings.sectionElem +\n            ' id=\"' +\n            section_id +\n            '\" class=\"' +\n            S.settings.className +\n            '__section\" />'\n        );\n\n        if (S.settings.subSections) {\n          var $sub_sections = $this_section.filter(S.settings.subSections);\n\n          if ($sub_sections.length > 0) {\n            $sub_sections.each(function(i) {\n              var sub_id = section_id + '-' + (i + 1);\n              var sub_text = $(this).text();\n              var $this_sub = $this_section.filter(\n                $(this)\n                  .nextUntil($sub_sections)\n                  .addBack()\n              );\n\n              $this_sub.wrapAll(\n                '<div id=\"' +\n                  sub_id +\n                  '\" class=\"' +\n                  S.settings.className +\n                  '__sub-section\" />'\n              );\n              sub_data.push({ id: sub_id, text: sub_text });\n            });\n          }\n        }\n\n        section_data.push({\n          id: section_id,\n          text: text,\n          sub_sections: sub_data\n        });\n      });\n\n      S.sections.data = section_data;\n    },\n    _tear_down_sections: function(sections) {\n      $(sections).each(function() {\n        var sub_sections = this.sub_sections;\n\n        $('#' + this.id)\n          .children()\n          .unwrap();\n\n        if (sub_sections.length > 0) {\n          $(sub_sections).each(function() {\n            $('#' + this.id)\n              .children()\n              .unwrap();\n          });\n        }\n      });\n    },\n    _setup_nav: function(sections) {\n      // Populate an ordered list from the section array we built\n\n      var $headline = $('<span />', {\n        class: S.settings.className + '__heading',\n        text: S.settings.headlineText\n      });\n      var $wrapper = $('<div />', {\n        class: S.settings.className + '__wrapper'\n      });\n      var $nav = $('<nav />', {\n        class: S.settings.className,\n        role: 'navigation'\n      });\n      var $nav_list = $('<ol />', { class: S.settings.className + '__list' });\n\n      $.each(sections, function(i) {\n        var $item =\n          i === 0\n            ? $('<li />', {\n                class:\n                  S.settings.className +\n                  '__item ' +\n                  S.settings.className +\n                  '__item--active active'\n              })\n            : $('<li />', { class: S.settings.className + '__item' });\n        var $link = $('<a />', {\n          href: '#' + this.id,\n          class: S.settings.className + '__link',\n          text: this.text\n        });\n        var $sub_nav_list;\n\n        if (this.sub_sections.length > 0) {\n          $item.addClass('is-parent-item');\n          $sub_nav_list = $('<ol />', {\n            class: S.settings.className + '__sub-list'\n          });\n\n          $.each(this.sub_sections, function() {\n            var $sub_item = $('<li />', {\n              class: S.settings.className + '__sub-item'\n            });\n            var $sub_link = $('<a />', {\n              href: '#' + this.id,\n              class: S.settings.className + '__sub-link',\n              text: this.text\n            });\n\n            $sub_nav_list.append($sub_item.append($sub_link));\n          });\n        }\n\n        $nav_list.append($item.append($link).append($sub_nav_list));\n      });\n\n      if (S.settings.showHeadline) {\n        $nav.append($wrapper.append($headline).append($nav_list));\n      } else {\n        $nav.append($wrapper.append($nav_list));\n      }\n\n      S.nav = $nav;\n    },\n    _insert_nav: function() {\n      // Add the nav to our page\n\n      var insert_location = S.settings.insertLocation;\n      var $insert_target = S.settings.insertTarget;\n\n      S.nav[insert_location]($insert_target);\n    },\n    _setup_pos: function() {\n      // Find the offset positions of each section\n\n      var $nav = S.nav;\n      var vp_height = $(window).height();\n      var nav_offset = $nav.offset().top;\n\n      var set_offset = function(section) {\n        var $this_section = $('#' + section.id);\n        var this_height = $this_section.height();\n\n        section.top_offset = $this_section.offset().top;\n        section.bottom_offset = section.top_offset + this_height;\n      };\n\n      $.each(S.sections.data, function() {\n        set_offset(this);\n\n        $.each(this.sub_sections, function() {\n          set_offset(this);\n        });\n      });\n\n      S.dims = {\n        vp_height: vp_height,\n        nav_offset: nav_offset\n      };\n    },\n    _check_pos: function() {\n      // Set nav to fixed after scrolling past the header and add an in-view class to any\n      // sections currently within the bounds of our view and active class to the first\n      // in-view section\n\n      var $nav = S.nav;\n      var win_top = $(window).scrollTop();\n      var boundry_top = win_top + S.settings.scrollOffset;\n      var boundry_bottom = win_top + S.dims.vp_height - S.settings.scrollOffset;\n      var sections_active = [];\n      var sub_sections_active = [];\n\n      if (win_top > S.dims.nav_offset - S.settings.fixedMargin) {\n        $nav.addClass('fixed');\n      } else {\n        $nav.removeClass('fixed');\n      }\n\n      var in_view = function(section) {\n        return (\n          (section.top_offset >= boundry_top &&\n            section.top_offset <= boundry_bottom) ||\n          (section.bottom_offset > boundry_top &&\n            section.bottom_offset < boundry_bottom) ||\n          (section.top_offset < boundry_top &&\n            section.bottom_offset > boundry_bottom)\n        );\n      };\n\n      $.each(S.sections.data, function() {\n        if (in_view(this)) {\n          sections_active.push(this);\n        }\n        $.each(this.sub_sections, function() {\n          if (in_view(this)) {\n            sub_sections_active.push(this);\n          }\n        });\n      });\n\n      $nav\n        .find('.' + S.settings.className + '__item')\n        .removeClass(S.settings.className + '__item--active')\n        .removeClass('active')\n        .removeClass('in-view');\n      $nav\n        .find('.' + S.settings.className + '__sub-item')\n        .removeClass(S.settings.className + '__sub-item--active')\n        .removeClass('active')\n        .removeClass('in-view');\n\n      $.each(sections_active, function(i) {\n        if (i === 0) {\n          $nav\n            .find('a[href=\"#' + this.id + '\"]')\n            .parents('.' + S.settings.className + '__item')\n            .addClass(S.settings.className + '__item--active')\n            .addClass('active')\n            .addClass('in-view');\n        } else {\n          $nav\n            .find('a[href=\"#' + this.id + '\"]')\n            .parents('.' + S.settings.className + '__item')\n            .addClass('in-view');\n        }\n      });\n      S.sections.active = sections_active;\n\n      $.each(sub_sections_active, function(i) {\n        if (i === 0) {\n          $nav\n            .find('a[href=\"#' + this.id + '\"]')\n            .parents('.' + S.settings.className + '__sub-item')\n            .addClass(S.settings.className + '__sub-item--active')\n            .addClass('active')\n            .addClass('in-view');\n        } else {\n          $nav\n            .find('a[href=\"#' + this.id + '\"]')\n            .parents('.' + S.settings.className + '__sub-item')\n            .addClass('in-view');\n        }\n      });\n    },\n    _init_scroll_listener: function() {\n      // Set a scroll listener to update the fixed and active classes\n\n      $(window).on('scroll.scrollNav', function() {\n        S._check_pos();\n      });\n    },\n    _rm_scroll_listeners: function() {\n      $(window).off('scroll.scrollNav');\n    },\n    _init_resize_listener: function() {\n      // Set a resize listener to update position values and the fixed and active classes\n\n      $(window).on('resize.scrollNav', function() {\n        S._setup_pos();\n        S._check_pos();\n      });\n    },\n    _rm_resize_listener: function() {\n      $(window).off('resize.scrollNav');\n    },\n    _init_click_listener: function() {\n      // Scroll to section on click\n\n      $('.' + S.settings.className)\n        .find('a')\n        .on('click.scrollNav', function(e) {\n          e.preventDefault();\n\n          var value = $(this).attr('href');\n          var speed = S.settings.speed;\n          var offset = S.settings.scrollOffset;\n          var animated = S.settings.animated;\n\n          scroll_to(value, speed, offset, animated);\n        });\n    },\n    _rm_click_listener: function() {\n      $('.' + S.settings.className)\n        .find('a')\n        .off('click.scrollNav');\n    },\n    _init_keyboard_listener: function(sections) {\n      // Scroll to section on arrow key press\n\n      if (S.settings.arrowKeys) {\n        $(document).on('keydown.scrollNav', function(e) {\n          if (e.keyCode === 40 || e.keyCode === 38) {\n            var findSection = function(key) {\n              var i = 0;\n              var l = sections.length;\n\n              for (i; i < l; i++) {\n                if (sections[i].id === S.sections.active[0].id) {\n                  var array_offset = key === 40 ? i + 1 : i - 1;\n                  var id =\n                    sections[array_offset] === undefined\n                      ? undefined\n                      : sections[array_offset].id;\n\n                  return id;\n                }\n              }\n            };\n\n            var target_section = findSection(e.keyCode);\n\n            if (target_section !== undefined) {\n              e.preventDefault();\n\n              var value = '#' + target_section;\n              var speed = S.settings.speed;\n              var offset = S.settings.scrollOffset;\n              var animated = S.settings.animated;\n\n              scroll_to(value, speed, offset, animated);\n            }\n          }\n        });\n      }\n    },\n    _rm_keyboard_listener: function() {\n      $(document).off('keydown.scrollNav');\n    },\n    init: function(options) {\n      return this.each(function() {\n        var $el = $(this);\n\n        // Merge default settings with user defined options\n        S.settings = $.extend({}, S.defaults, options);\n\n        // If the insert target isn't set, use the initialized element\n        S.settings.insertTarget = S.settings.insertTarget\n          ? $(S.settings.insertTarget)\n          : $el;\n\n        if ($el.length > 0) {\n          // Initialize\n\n          // Fire custom init callback\n          if (S.settings.onInit) {\n            S.settings.onInit.call(this);\n          }\n\n          S._set_body_class('loading');\n          S._find_sections($el);\n\n          if ($el.find(S.settings.sections).length > 0) {\n            // BUILD!!!!\n\n            S._setup_sections(S.sections.raw);\n            S._setup_nav(S.sections.data);\n\n            if (S.settings.insertTarget.length > 0) {\n              //Add to page\n\n              S._insert_nav();\n              S._setup_pos();\n              S._check_pos();\n              S._init_scroll_listener();\n              S._init_resize_listener();\n              S._init_click_listener();\n              S._init_keyboard_listener(S.sections.data);\n              S._set_body_class('success');\n              if (S.settings.scrollToHash) {\n                scroll_to(get_hash());\n              }\n\n              // Fire custom render callback\n              if (S.settings.onRender) {\n                S.settings.onRender.call(this);\n              }\n            } else {\n              console.log(\n                'Build failed, scrollNav could not find \"' +\n                  S.settings.insertTarget +\n                  '\"'\n              );\n              S._set_body_class('failed');\n            }\n          } else {\n            console.log(\n              'Build failed, scrollNav could not find any \"' +\n                S.settings.sections +\n                's\" inside of \"' +\n                $el.selector +\n                '\"'\n            );\n            S._set_body_class('failed');\n          }\n        } else {\n          console.log(\n            'Build failed, scrollNav could not find \"' + $el.selector + '\"'\n          );\n          S._set_body_class('failed');\n        }\n      });\n    },\n    destroy: function() {\n      return this.each(function() {\n        // Unbind event listeners\n        S._rm_scroll_listeners();\n        S._rm_resize_listener();\n        S._rm_click_listener();\n        S._rm_keyboard_listener();\n\n        // Remove any of the loading hooks\n        $('body').removeClass('sn-loading sn-active sn-failed');\n\n        // Remove the nav from the dom\n        $('.' + S.settings.className).remove();\n\n        // Teardown sections\n        S._tear_down_sections(S.sections.data);\n\n        // Fire custom destroy callback\n        if (S.settings.onDestroy) {\n          S.settings.onDestroy.call(this);\n        }\n\n        // Remove the saved settings\n        S.settings = [];\n        S.sections = undefined;\n      });\n    },\n    resetPos: function() {\n      S._setup_pos();\n      S._check_pos();\n\n      // Fire custom reset position callback\n      if (S.settings.onResetPos) {\n        S.settings.onResetPos.call(this);\n      }\n    }\n  };\n\n  $.fn.scrollNav = function() {\n    var options;\n    var method = arguments[0];\n\n    if (S[method]) {\n      // Method exists, so use it\n\n      method = S[method];\n      options = Array.prototype.slice.call(arguments, 1);\n    } else if (typeof method === 'object' || !method) {\n      // No method passed, default to init\n\n      method = S.init;\n      options = arguments;\n    } else {\n      // Method doesn't exist\n\n      $.error('Method ' + method + ' does not exist in the scrollNav plugin');\n      return this;\n    }\n\n    return method.apply(this, options);\n  };\n})(jQuery);\n"
  },
  {
    "path": "src/scrollTo.js",
    "content": "import calculateScrollDuration from './util/calculateScrollDuration';\nimport { easing } from './util/easing';\n\n/* istanbul ignore next */\nexport default function scrollTo(targetPosition, easingStyle) {\n  return new Promise((resolve, reject) => {\n    if (typeof targetPosition !== 'number') {\n      return reject(new Error('First argument must be a number'));\n    }\n\n    easingStyle = easingStyle || 'linear';\n    if (typeof easingStyle !== 'string') {\n      return reject(new Error('Second argument must be a string'));\n    }\n\n    const startingPosition = window.pageYOffset;\n    const distance = targetPosition - startingPosition;\n    const duration = calculateScrollDuration(distance);\n    const framerate = 50;\n    const increment = 1000 / framerate;\n    let ellapsedTime = 0;\n    let easedTime;\n    let next;\n\n    function animateScroll() {\n      ellapsedTime += increment;\n      easedTime = easing[easingStyle](ellapsedTime / duration);\n      next = easedTime * distance + startingPosition;\n      window.scroll(0, next);\n\n      if (ellapsedTime < duration) {\n        setTimeout(animateScroll, increment);\n      } else {\n        resolve(window.pageYOffset);\n      }\n    }\n\n    animateScroll();\n  });\n}\n"
  },
  {
    "path": "src/scrollnav.js",
    "content": "/*\n * scrollnav\n * http://scrollnav.com\n *\n * Copyright (c) 2013-2018 James Wilson\n * Licensed under the MIT license.\n */\n\nimport extend from './util/extend';\nimport populateSectionData from './util/populateSectionData';\nimport createNav from './util/createNav';\nimport insertNav from './util/insertNav';\nimport updatePositionData from './util/updatePositionData';\nimport insertVisualDebugger from './util/insertVisualDebugger';\nimport setupClickHandlers from './setupClickHandlers';\nimport setupScrollHandler from './setupScrollHandler';\nimport setupResizeHandler from './setupResizeHandler';\nimport teardownClickHandlers from './teardownClickHandlers';\nimport teardownScrollHandler from './teardownScrollHandler';\nimport teardownResizeHandler from './teardownResizeHandler';\n\nlet clickHandler;\nlet scrollHandler;\nlet resizeHandler;\n\nfunction isElement(element) {\n  return element instanceof Element;\n}\n\nfunction init(elem, options) {\n  const defaults = {\n    sections: 'h2',\n    insertTarget: elem,\n    insertLocation: 'before',\n    easingStyle: 'easeOutQuad',\n    updateHistory: true\n  };\n  this.settings = extend(defaults, options);\n  const locationOptions = ['append', 'prepend', 'after', 'before'];\n\n  if (!isElement(elem)) {\n    if (this.settings.debug) {\n      // eslint-disable-next-line no-console\n      console.error(`\n        scrollnav build failed, content argument \"${elem}\" is not an HTML Element\n      `);\n    }\n    return;\n  }\n\n  if (this.settings.insertTarget && !isElement(this.settings.insertTarget)) {\n    if (this.settings.debug) {\n      // eslint-disable-next-line no-console\n      console.error(`\n        scrollnav build failed, options.insertTarget \"${elem}\" is not an HTML Element\n      `);\n    }\n    return;\n  }\n\n  if (!locationOptions.includes(this.settings.insertLocation)) {\n    if (this.settings.debug) {\n      // eslint-disable-next-line no-console\n      console.error(`\n        scrollnav build failed, options.insertLocation \"${\n          this.settings.insertLocation\n        }\" is not a valid option\n      `);\n    }\n    return;\n  }\n\n  const sectionsDom = elem.querySelectorAll(this.settings.sections);\n\n  if (!sectionsDom.length) {\n    if (this.settings.debug) {\n      // eslint-disable-next-line no-console\n      console.error(`\n        scrollnav build failed, could not find any \"${this.settings.sections}\"\n        elements inside of \"${elem}\"\n      `);\n    }\n    return;\n  }\n\n  this.data = populateSectionData(sectionsDom, this.settings);\n  this.nav = createNav(this.data);\n\n  insertNav(this);\n  clickHandler = setupClickHandlers(this);\n  scrollHandler = setupScrollHandler(this);\n  resizeHandler = setupResizeHandler(this);\n\n  if (this.settings.debug) insertVisualDebugger();\n  if (this.settings.onInit) return this.settings.onInit();\n}\n\nfunction destroy(options) {\n  this.settings = extend(this.settings, options);\n\n  teardownClickHandlers(this.nav, clickHandler);\n  teardownScrollHandler(scrollHandler);\n  teardownResizeHandler(resizeHandler);\n  this.nav.remove();\n\n  if (this.settings.onDestroy) return this.settings.onDestroy();\n}\n\nfunction updatePositions(options) {\n  this.settings = extend(this.settings, options);\n  this.data = updatePositionData(this.data);\n\n  if (this.settings.onUpdatePositions) return this.settings.onUpdatePositions();\n}\n\nconst scrollnav = {\n  init: init,\n  destroy: destroy,\n  updatePositions: updatePositions\n};\n\nexport default scrollnav;\n"
  },
  {
    "path": "src/setupClickHandlers.js",
    "content": "import getTargetYPosition from './util/getTargetYPosition';\nimport scrollTo from './scrollTo';\n\nexport default function setupClickHandlers(scrollnav) {\n  const settings = scrollnav.settings;\n  function clickHandler(event) {\n    event.preventDefault();\n\n    const activeArea = window.innerHeight * 0.39;\n    const targetYPosition = getTargetYPosition(event.target, scrollnav.data);\n    const scrollYTarget = targetYPosition - activeArea;\n\n    /* istanbul ignore next */\n    return scrollTo(scrollYTarget, settings.easingStyle).then(() => {\n      if (settings.updateHistory) {\n        history.replaceState({}, '', event.target.getAttribute('href'));\n      }\n\n      if (settings.onScroll) {\n        settings.onScroll();\n      }\n    });\n  }\n\n  const links = scrollnav.nav.querySelectorAll('a');\n  links.forEach(link => {\n    link.addEventListener('click', clickHandler);\n  });\n\n  return clickHandler;\n}\n"
  },
  {
    "path": "src/setupResizeHandler.js",
    "content": "import updatePositionData from './util/updatePositionData';\n\nexport default function setupResizeHandler(scrollnav) {\n  function resizeHandler() {\n    scrollnav.data = updatePositionData(scrollnav.data);\n  }\n\n  window.addEventListener('resize', resizeHandler);\n\n  return resizeHandler;\n}\n"
  },
  {
    "path": "src/setupScrollHandler.js",
    "content": "import getActiveSection from './util/getActiveSection';\nimport updateActiveNavItem from './util/updateActiveNavItem';\n\nexport default function setupScrollHandler(scrollnav) {\n  function scrollHandler() {\n    const top = window.scrollY || window.pageYOffset || document.body.scrollTop;\n    const boundryTop = top;\n    const boundryBottom = top + window.innerHeight * 0.4;\n    const activeSection = getActiveSection(\n      scrollnav.data,\n      boundryTop,\n      boundryBottom\n    );\n\n    updateActiveNavItem(activeSection, scrollnav.nav);\n\n    return activeSection;\n  }\n\n  window.addEventListener('scroll', scrollHandler);\n\n  return scrollHandler;\n}\n"
  },
  {
    "path": "src/teardownClickHandlers.js",
    "content": "export default function teardownClickHandlers(nav, clickHandler) {\n  const links = nav.querySelectorAll('a');\n  links.forEach(link => {\n    link.removeEventListener('click', clickHandler);\n  });\n}\n"
  },
  {
    "path": "src/teardownResizeHandler.js",
    "content": "export default function teardownResizelHandler(resizeHandler) {\n  window.removeEventListener('resize', resizeHandler);\n}\n"
  },
  {
    "path": "src/teardownScrollHandler.js",
    "content": "export default function teardownScrollHandler(scrollHandler) {\n  window.removeEventListener('scroll', scrollHandler);\n}\n"
  },
  {
    "path": "src/util/calculateScrollDuration.js",
    "content": "export default function calculateScrollDuration(distance) {\n  const halfDistance = Math.abs(distance / 2);\n\n  return Math.min(Math.max(halfDistance, 250), 1200);\n}\n"
  },
  {
    "path": "src/util/createList.js",
    "content": "export default function createList(data, isSubList = false) {\n  const suffix = isSubList ? '__sub-' : '__';\n  const baseClass = 'scroll-nav' + suffix;\n\n  const itemsMarkup = `\n    ${data\n      .map(\n        item =>\n          `<li class=\"${baseClass}item\" data-sn-section=\"${item.id}\">\n            <a class=\"${baseClass}link\" href=\"#${item.id}\">${item.text}</a>\n            ${\n              item.subSections && item.subSections.length\n                ? `${createList(item.subSections, true)}`\n                : ''\n            }\n          </li>`\n      )\n      .join('')}\n  `;\n\n  const list = `\n    <ol class=\"${baseClass}list\">\n      ${itemsMarkup}\n    </ol>\n  `;\n\n  return list;\n}\n"
  },
  {
    "path": "src/util/createNav.js",
    "content": "import createList from './createList';\n\nexport default function createNav(data) {\n  const nav = document.createElement('nav');\n  nav.className = 'scroll-nav';\n  nav.innerHTML = createList(data);\n\n  return nav;\n}\n"
  },
  {
    "path": "src/util/easing.js",
    "content": "/* Borrowed from https://gist.github.com/gre/1650294#gistcomment-1806616 */\n\nconst easeIn = p => t => Math.pow(t, p);\nconst easeOut = p => t => 1 - Math.abs(Math.pow(t - 1, p));\nconst easeInOut = p => t =>\n  t < 0.5 ? easeIn(p)(t * 2) / 2 : easeOut(p)(t * 2 - 1) / 2 + 0.5;\n\nconst easing = {\n  linear: easeInOut(1),\n  easeInQuad: easeIn(2),\n  easeOutQuad: easeOut(2),\n  easeInOutQuad: easeInOut(2),\n  easeInCubic: easeIn(3),\n  easeOutCubic: easeOut(3),\n  easeInOutCubic: easeInOut(3),\n  easeInQuart: easeIn(4),\n  easeOutQuart: easeOut(4),\n  easeInOutQuart: easeInOut(4),\n  easeInQuint: easeIn(5),\n  easeOutQuint: easeOut(5),\n  easeInOutQuint: easeInOut(5)\n};\n\nexport { easing };\n"
  },
  {
    "path": "src/util/extend.js",
    "content": "/* Borrowed from https://gist.github.com/cferdinandi/4f8a0e17921c5b46e6c4 */\n\nexport default function extend(defaults, options) {\n  const extended = {};\n  let prop;\n\n  for (prop in defaults) {\n    if (Object.prototype.hasOwnProperty.call(defaults, prop)) {\n      extended[prop] = defaults[prop];\n    }\n  }\n\n  for (prop in options) {\n    if (Object.prototype.hasOwnProperty.call(options, prop)) {\n      extended[prop] = options[prop];\n    }\n  }\n\n  return extended;\n}\n"
  },
  {
    "path": "src/util/getActiveSection.js",
    "content": "export default function getActiveSection(data, boundryTop, boundryBottom) {\n  let activeSection;\n\n  data.forEach(section => {\n    if (section.offsetTop > boundryBottom) {\n      if (!activeSection && section.offsetTop < boundryTop) {\n        activeSection = section;\n      }\n    } else {\n      activeSection = section;\n    }\n  });\n\n  if (activeSection && activeSection.subSections.length) {\n    let activeSubSection;\n\n    activeSubSection = getActiveSection(\n      activeSection.subSections,\n      boundryTop,\n      boundryBottom\n    );\n\n    if (activeSubSection) {\n      activeSection = activeSubSection;\n    }\n  }\n\n  return activeSection;\n}\n"
  },
  {
    "path": "src/util/getOrSetID.js",
    "content": "export default function getOrSetID(elem, setID) {\n  if (typeof elem !== 'object') {\n    return Promise.reject(new Error('First argument must be an object'));\n  }\n\n  let id = elem.id;\n\n  if (!id) {\n    if (typeof setID !== 'string') {\n      return Promise.reject(new Error('Second argument must be a string'));\n    }\n\n    id = setID;\n    elem.id = id;\n  }\n  return id;\n}\n"
  },
  {
    "path": "src/util/getTargetYPosition.js",
    "content": "export default function getTargetYPosition(target, data) {\n  let id = target.getAttribute('href');\n  if (id.charAt(0) === '#') {\n    id = id.substr(1);\n  }\n\n  const targetSection = filterData(data, id);\n\n  return targetSection.offsetTop;\n}\n\nfunction filterData(data, id) {\n  let targetSection;\n\n  data.forEach(section => {\n    if (section.id === id) {\n      targetSection = section;\n    }\n\n    if (section.subSections && targetSection === undefined) {\n      targetSection = filterData(section.subSections, id);\n    }\n  });\n\n  return targetSection;\n}\n"
  },
  {
    "path": "src/util/getYPosition.js",
    "content": "export default function getYPosition(elem, parent) {\n  if (typeof elem !== 'object') {\n    return Promise.reject(new Error('First argument must be an object'));\n  }\n\n  parent = parent || document.body;\n  if (typeof parent !== 'object') {\n    return Promise.reject(new Error('Second argument must be an object'));\n  }\n\n  const bodyRect = parent.getBoundingClientRect();\n  const elemRect = elem.getBoundingClientRect();\n\n  return elemRect.top - bodyRect.top;\n}\n"
  },
  {
    "path": "src/util/insertNav.js",
    "content": "export default function insertNav(scrollnav) {\n  const target = scrollnav.settings.insertTarget;\n  const location = scrollnav.settings.insertLocation;\n\n  if (location === 'append') {\n    target.appendChild(scrollnav.nav);\n  } else if (location === 'prepend') {\n    target.insertBefore(scrollnav.nav, target.firstChild);\n  } else if (location === 'before') {\n    target.parentNode.insertBefore(scrollnav.nav, target);\n  } else if (location === 'after') {\n    target.parentNode.insertBefore(scrollnav.nav, target.nextSibling);\n  }\n}\n"
  },
  {
    "path": "src/util/insertVisualDebugger.js",
    "content": "export default function insertVisualDebugger() {\n  const snDebugger = document.createElement('div');\n  snDebugger.className = 'snDebugger';\n\n  snDebugger.setAttribute(\n    'style',\n    `\n      position: fixed;\n      top: 40%;\n      height: 0px;\n      border-bottom:5px solid red;\n      border-top: 5px solid blue;\n      width: 100%;\n      opacity: .5;\n      pointer-events: none;\n    `\n  );\n\n  document.body.appendChild(snDebugger);\n}\n"
  },
  {
    "path": "src/util/nextUntil.js",
    "content": "/* Borrowed from https://github.com/cferdinandi/nextUntil */\n\nexport default function nextUntil(elem, selector, filter) {\n  var siblings = [];\n\n  elem = elem.nextElementSibling;\n\n  while (elem) {\n    if (elem.matches(selector)) break;\n\n    if (filter && !elem.matches(filter)) {\n      elem = elem.nextElementSibling;\n      continue;\n    }\n\n    siblings.push(elem);\n\n    elem = elem.nextElementSibling;\n  }\n\n  return siblings;\n}\n"
  },
  {
    "path": "src/util/populateSectionData.js",
    "content": "import getOrSetID from './getOrSetID';\nimport getYPosition from './getYPosition';\nimport nextUntil from './nextUntil';\n\nexport default function populateSectionData(\n  sections,\n  settings,\n  prefix = 'scroll-nav'\n) {\n  const sectionData = [];\n  prefix = prefix + '__';\n\n  sections.forEach((elem, i) => {\n    let subSectionData = [];\n    const id = getOrSetID(elem, prefix + (i + 1));\n\n    if (settings.subSections && elem.matches(settings.sections)) {\n      const subSectionDom = nextUntil(\n        elem,\n        settings.sections,\n        settings.subSections\n      );\n      subSectionData = populateSectionData(subSectionDom, settings, id);\n    }\n\n    sectionData.push({\n      id: id,\n      text: elem.innerText || elem.textContent,\n      offsetTop: getYPosition(elem),\n      subSections: subSectionData\n    });\n  });\n\n  return sectionData;\n}\n"
  },
  {
    "path": "src/util/updateActiveNavItem.js",
    "content": "export default function updateActiveNavItem(activeSection, nav) {\n  const previousActive = nav.querySelector('[data-sn-active]');\n\n  if (!activeSection) {\n    if (previousActive) {\n      previousActive.classList.remove('scroll-nav__item--active');\n      previousActive.removeAttribute('data-sn-active');\n    }\n\n    return;\n  }\n\n  const newActive = nav.querySelector(\n    '[data-sn-section=' + activeSection.id + ']'\n  );\n\n  if (newActive && newActive !== previousActive) {\n    if (previousActive) {\n      previousActive.classList.remove('scroll-nav__item--active');\n      previousActive.removeAttribute('data-sn-active');\n    }\n    newActive.classList.add('scroll-nav__item--active');\n    newActive.setAttribute('data-sn-active', true);\n  }\n}\n"
  },
  {
    "path": "src/util/updatePositionData.js",
    "content": "import getYPosition from './getYPosition';\n\nexport default function updatePositionData(data) {\n  data.forEach(section => {\n    const sectionDom = document.querySelector(`#${section.id}`);\n    section.offsetTop = getYPosition(sectionDom);\n\n    if (section.subSections.length) {\n      section.subSections = updatePositionData(section.subSections);\n    }\n  });\n\n  return data;\n}\n"
  },
  {
    "path": "test/.eslintrc.json",
    "content": "{\n  \"env\": {\n    \"jest\": true\n  }\n}\n"
  },
  {
    "path": "test/fixtures/navMarkup.js",
    "content": "const onlyH2Nav = `\n  <nav class=\"scroll-nav\" role=\"navigation\">\n    <ul class=\"scroll-nav__list\">\n      <li class=\"scroll-nav__item\" data-sn-section=\"first-heading\">\n        <a href=\"#first-heading\" class=\"scroll-nav__link\">First heading</a>\n      </li>\n      <li class=\"scroll-nav__item\" data-sn-section=\"second-heading\">\n        <a href=\"#second-heading\" class=\"scroll-nav__link\">Second heading</a>\n      </li>\n      <li class=\"scroll-nav__item\" data-sn-section=\"third-heading\">\n        <a href=\"#third-heading\" class=\"scroll-nav__link\">Third heading</a>\n      </li>\n    </ul>\n  </nav>`;\n\nconst allNav = `\n  <nav class=\"scroll-nav\" role=\"navigation\">\n    <ol class=\"scroll-nav__list\">\n      <li class=\"scroll-nav__item\" data-sn-section=\"first-heading\">\n        <a href=\"#first-heading\" class=\"scroll-nav__link\">First heading</a>\n      </li>\n      <li class=\"scroll-nav__item\" data-sn-section=\"second-heading\">\n        <a href=\"#second-heading\" class=\"scroll-nav__link\">Second heading</a>\n        <ol class=\"scroll-nav__sub-list\">\n          <li class=\"scroll-nav__sub-item\" data-sn-section=\"second-heading__1\">\n            <a class=\"scroll-nav__sub-link\" href=\"#second-heading__1\">\n              First sub-heading of the second heading\n            </a>\n          </li>\n        </ol>\n      </li>\n      <li class=\"scroll-nav__item\" data-sn-section=\"third-heading\">\n        <a href=\"#third-heading\" class=\"scroll-nav__link\">Third heading</a>\n      </li>\n    </ol>\n  </nav>`;\n\nexport { onlyH2Nav, allNav };\n"
  },
  {
    "path": "test/fixtures/noSectionsMarkup.js",
    "content": "const html = `\n  <div class=\"test-content\">\n    <p>Lorem ipsum dolor sit amet, in vis quis verear persecuti, sed ne equidem\n    singulis incorrupte. Has quem erant mentitum te, cu aeterno feugiat\n    antiopam eam. Cum ea soleat discere. Ut cum eripuit pertinax. Admodum\n    inimicus nec ex, mel quod antiopam neglegentur an, ad mutat euismod\n    qualisque nam.</p>\n\n    <p>Pri et option adversarium. Id vix euismod inermis consequat, his verear\n    veritus aliquando ad, ea recusabo ullamcorper mel. Sit ne quas aliquid\n    blandit, est ullum regione deserunt an, sea paulo lucilius ut. Et stet\n    menandri qualisque mea, quando tacimates suavitate te mei, ius offendit\n    legendos philosophia eu. Ferri dicant equidem vel cu, duo facilisis\n    definiebas appellantur ei, meis mutat invenire ut vel.</p>\n\n    <p>Sed id nulla ignota, ad has idque nusquam interesset. Sea id atomorum\n    salutandi ocurreret, vix et mucius impedit quaestio. An tritani veritus\n    oporteat quo. Id labore delicata eos, verear legendos antiopam mel te,\n    impedit corpora ea cum.</p>\n\n    <p>Mea ad insolens menandri, eam te odio aliquam, singulis qualisque vix\n    cu. Invidunt consequat ex his, appareat invidunt duo at, sed cu ipsum\n    vocent. Duo an quaerendum consequuntur, et duo error liberavisse, vix inani\n    explicari cu. Harum mandamus scribentur mea ad, pri at antiopam inimicus\n    accusamus. Pro graecis vituperatoribus in, delicata splendide ullamcorper\n    ex duo.</p>\n\n    <p>In est nonumy populo. Usu no natum percipitur reprehendunt. Vitae\n    nominavi in cum, eleifend delicatissimi no cum. Sea an omnis denique\n    gloriatur. Probo comprehensam at sit, amet ipsum repudiandae duo ei.<p>\n  </div>`;\n\nexport { html };\n"
  },
  {
    "path": "test/fixtures/pennerEasing.js",
    "content": "/* Source https://github.com/CharlotteGore/functional-easing */\n/* eslint-disable */\nMath.linear = function(t, b, c, d) {\n  return (c * t) / d + b;\n};\n\nMath.easeInQuad = function(t, b, c, d) {\n  return c * (t /= d) * t + b;\n};\n\nMath.easeOutQuad = function(t, b, c, d) {\n  return -c * (t /= d) * (t - 2) + b;\n};\n\nMath.easeInOutQuad = function(t, b, c, d) {\n  if ((t /= d / 2) < 1) return (c / 2) * t * t + b;\n  return (-c / 2) * (--t * (t - 2) - 1) + b;\n  return 0;\n};\n\nMath.easeInCubic = function(t, b, c, d) {\n  return c * (t /= d) * t * t + b;\n};\n\nMath.easeOutCubic = function(t, b, c, d) {\n  return c * ((t = t / d - 1) * t * t + 1) + b;\n};\n\nMath.easeInOutCubic = function(t, b, c, d) {\n  if ((t /= d / 2) < 1) return (c / 2) * t * t * t + b;\n  return (c / 2) * ((t -= 2) * t * t + 2) + b;\n};\n\nMath.easeInQuart = function(t, b, c, d) {\n  return c * (t /= d) * t * t * t + b;\n};\n\nMath.easeOutQuart = function(t, b, c, d) {\n  return -c * ((t = t / d - 1) * t * t * t - 1) + b;\n};\n\nMath.easeInOutQuart = function(t, b, c, d) {\n  if ((t /= d / 2) < 1) return (c / 2) * t * t * t * t + b;\n  return (-c / 2) * ((t -= 2) * t * t * t - 2) + b;\n};\n\nMath.easeInQuint = function(t, b, c, d) {\n  return c * (t /= d) * t * t * t * t + b;\n};\n\nMath.easeOutQuint = function(t, b, c, d) {\n  return c * ((t = t / d - 1) * t * t * t * t + 1) + b;\n};\n\nMath.easeInOutQuint = function(t, b, c, d) {\n  if ((t /= d / 2) < 1) return (c / 2) * t * t * t * t * t + b;\n  return (c / 2) * ((t -= 2) * t * t * t * t + 2) + b;\n};\n"
  },
  {
    "path": "test/fixtures/sectionData.js",
    "content": "const onlyH2Data = [\n  {\n    id: 'first-heading',\n    text: 'First heading',\n    offsetTop: 100,\n    subSections: []\n  },\n  {\n    id: 'second-heading',\n    text: 'Second heading',\n    offsetTop: 200,\n    subSections: []\n  },\n  {\n    id: 'third-heading',\n    text: 'Third heading',\n    offsetTop: 300,\n    subSections: []\n  }\n];\n\nconst allData = [\n  {\n    id: 'first-heading',\n    text: 'First heading',\n    offsetTop: 100,\n    subSections: []\n  },\n  {\n    id: 'second-heading',\n    text: 'Second heading',\n    offsetTop: 200,\n    subSections: [\n      {\n        id: 'second-heading__1',\n        text: 'First sub-heading of the second heading',\n        offsetTop: 225,\n        subSections: []\n      }\n    ]\n  },\n  {\n    id: 'third-heading',\n    text: 'Third heading',\n    offsetTop: 300,\n    subSections: []\n  }\n];\n\nexport { onlyH2Data, allData };\n"
  },
  {
    "path": "test/fixtures/sectionMarkup.js",
    "content": "const html = `\n  <div class=\"test-content\">\n    <p>Lorem ipsum dolor sit amet, in vis quis verear persecuti, sed ne equidem\n    singulis incorrupte. Has quem erant mentitum te, cu aeterno feugiat\n    antiopam eam. Cum ea soleat discere. Ut cum eripuit pertinax. Admodum\n    inimicus nec ex, mel quod antiopam neglegentur an, ad mutat euismod\n    qualisque nam.</p>\n\n    <h2 id=\"first-heading\">First heading</h2>\n    <p>Pri et option adversarium. Id vix euismod inermis consequat, his verear\n    veritus aliquando ad, ea recusabo ullamcorper mel. Sit ne quas aliquid\n    blandit, est ullum regione deserunt an, sea paulo lucilius ut. Et stet\n    menandri qualisque mea, quando tacimates suavitate te mei, ius offendit\n    legendos philosophia eu. Ferri dicant equidem vel cu, duo facilisis\n    definiebas appellantur ei, meis mutat invenire ut vel.</p>\n\n    <h2 id=\"second-heading\">Second heading</h2>\n    <p>Sed id nulla ignota, ad has idque nusquam interesset. Sea id atomorum\n    salutandi ocurreret, vix et mucius impedit quaestio. An tritani veritus\n    oporteat quo. Id labore delicata eos, verear legendos antiopam mel te,\n    impedit corpora ea cum.</p>\n\n    <h3>First sub-heading of the second heading</h3>\n    <p>Mea ad insolens menandri, eam te odio aliquam, singulis qualisque vix\n    cu. Invidunt consequat ex his, appareat invidunt duo at, sed cu ipsum\n    vocent. Duo an quaerendum consequuntur, et duo error liberavisse, vix inani\n    explicari cu. Harum mandamus scribentur mea ad, pri at antiopam inimicus\n    accusamus. Pro graecis vituperatoribus in, delicata splendide ullamcorper\n    ex duo.</p>\n\n    <h2 id=\"third-heading\">Third heading</h2>\n    <p>In est nonumy populo. Usu no natum percipitur reprehendunt. Vitae\n    nominavi in cum, eleifend delicatissimi no cum. Sea an omnis denique\n    gloriatur. Probo comprehensam at sit, amet ipsum repudiandae duo ei.<p>\n  </div>`;\n\nexport { html };\n"
  },
  {
    "path": "test/tests/scrollTo.test.js",
    "content": "import scrollTo from '../../src/scrollTo';\n\njest.useFakeTimers();\n\ndescribe('scrollTo', () => {\n  it('should return an error if the first param is not a number', () => {\n    return expect(scrollTo('string')).rejects.toThrow(\n      'First argument must be a number'\n    );\n  });\n\n  it('should return an error if the second param is not a string', () => {\n    return expect(scrollTo(200, 400)).rejects.toThrow(\n      'Second argument must be a string'\n    );\n  });\n\n  xit('should resolve a Promise after scrolling', async () => {\n    expect.assertions(1);\n\n    const callback = jest.fn();\n    window.pageYOffset = 2200;\n\n    await scrollTo(200, null).then(() => {\n      callback();\n    });\n\n    jest.runAllTimers();\n\n    expect(callback).toBeCalled();\n  });\n});\n"
  },
  {
    "path": "test/tests/scrollnav.test.js",
    "content": "/* eslint no-console: 0 */\nimport jestDomCustomMatchers from '@jarmee/jest-dom-custom-matchers';\nimport scrollnav from '../../src/scrollnav';\nimport { html as sectionMarkup } from '../fixtures/sectionMarkup';\nimport { html as noSectionsMarkup } from '../fixtures/noSectionsMarkup';\n\nexpect.extend(jestDomCustomMatchers);\n\ndescribe('scrollnav', () => {\n  describe('init method', () => {\n    beforeEach(() => {\n      document.body.innerHTML = sectionMarkup;\n    });\n\n    describe('element argument', () => {\n      it(`should not create a nav element if the first argument is not an\n        HTML Element`, () => {\n        const content = 'content';\n\n        scrollnav.init(content);\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it(`should not create a nav element if the first argument is not an\n        HTML Element`, () => {\n        const content = () => {\n          return;\n        };\n\n        scrollnav.init(content);\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it(`should not create a nav element if the first argument is not an\n        HTML Element`, () => {\n        const content = new Object();\n\n        scrollnav.init(content);\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it(`should log an error if the first argument is not an HTML Element and\n        the debug option is true`, () => {\n        console.error = jest.fn();\n        const content = 'content';\n\n        scrollnav.init(content, { debug: true });\n\n        expect(console.error).toHaveBeenCalled();\n      });\n\n      it(`should not log an error if the first argument is an HTML Element and\n        the debug option is true`, () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, { debug: true });\n\n        expect(console.error).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('sections option', () => {\n      it(`should not create a nav element if there are no section elements\n        within the element argument`, () => {\n        document.body.innerHTML = noSectionsMarkup;\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content);\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it(`should log an error if there are no section elements within the element\n        argument and the debug option is true`, () => {\n        console.error = jest.fn();\n        document.body.innerHTML = noSectionsMarkup;\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, { debug: true });\n\n        expect(console.error).toHaveBeenCalled();\n      });\n\n      it(`should not log an error if there are section elements within the element\n        argument and the debug option is true`, () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, { debug: true });\n\n        expect(console.error).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('insertTarget option', () => {\n      it(`should not create a nav element if the insertTarget option is not an\n        HTML Element`, () => {\n        const content = document.querySelector('.test-content');\n        const insertTarget = 'insertTarget';\n\n        scrollnav.init(content, { insertTarget: insertTarget });\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it(`should not create a nav element if the insertTarget option is not an\n        HTML Element`, () => {\n        const content = document.querySelector('.test-content');\n        const insertTarget = () => {\n          return;\n        };\n\n        scrollnav.init(content, { insertTarget: insertTarget });\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it(`should not create a nav element if the insertTarget option is not an\n        HTML Element`, () => {\n        const content = document.querySelector('.test-content');\n        const insertTarget = new Object();\n\n        scrollnav.init(content, { insertTarget: insertTarget });\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it(`should log an error if the insertTarget option is not an HTML Element\n        and the debug option is true`, () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n        const insertTarget = 'insertTarget';\n\n        scrollnav.init(content, {\n          debug: true,\n          insertTarget: insertTarget\n        });\n\n        expect(console.error).toHaveBeenCalled();\n      });\n\n      it(`should not log an error if the insertTarget option is an HTML Element\n        and the debug option is true`, () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, {\n          debug: true,\n          insertTarget: content\n        });\n\n        expect(console.error).not.toHaveBeenCalled();\n      });\n    });\n\n    describe('insertLocation option', () => {\n      it(`should not create a nav element if the insertLocation option is\n        invalid`, () => {\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, { insertLocation: 'appendTo' });\n\n        const nav = document.querySelector('nav');\n\n        expect(nav).toBe(null);\n      });\n\n      it('should log an error if the insertLocation option is invalid', () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, {\n          debug: true,\n          insertLocation: 'appendTo'\n        });\n\n        expect(console.error).toHaveBeenCalled();\n      });\n\n      it('should not log an error if the insertLocation option is valid', () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, {\n          debug: true,\n          insertLocation: 'after'\n        });\n\n        expect(console.error).not.toHaveBeenCalled();\n      });\n\n      it('should not log an error if the insertLocation option is valid', () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, {\n          debug: true,\n          insertLocation: 'before'\n        });\n\n        expect(console.error).not.toHaveBeenCalled();\n      });\n\n      it('should not log an error if the insertLocation option is valid', () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, {\n          debug: true,\n          insertLocation: 'append'\n        });\n\n        expect(console.error).not.toHaveBeenCalled();\n      });\n\n      it('should not log an error if the insertLocation option is valid', () => {\n        console.error = jest.fn();\n        const content = document.querySelector('.test-content');\n\n        scrollnav.init(content, {\n          debug: true,\n          insertLocation: 'prepend'\n        });\n\n        expect(console.error).not.toHaveBeenCalled();\n      });\n    });\n\n    it('should create a nav element with the correct class name', () => {\n      const content = document.querySelector('.test-content');\n\n      scrollnav.init(content);\n\n      const nav = document.querySelector('nav');\n\n      expect(nav).toHaveClass('scroll-nav');\n    });\n\n    it('should inject the nav element before the content container', () => {\n      const content = document.querySelector('.test-content');\n\n      scrollnav.init(content);\n\n      const nav = document.querySelector('nav');\n\n      expect(nav.nextElementSibling).toBeHTMLElement('div');\n      expect(nav.nextElementSibling).toHaveClass('test-content');\n    });\n\n    it('should contain the list of links', () => {\n      const content = document.querySelector('.test-content');\n\n      scrollnav.init(content);\n\n      const nav = document.querySelector('nav');\n      const list = nav.children;\n\n      expect(list.length).toBe(1);\n      expect(list[0]).toBeHTMLElement('ol');\n      expect(list[0].children.length).toBe(3);\n    });\n\n    it('should return a callback after the nav is added', () => {\n      const callback = jest.fn();\n      const content = document.querySelector('.test-content');\n      scrollnav.init(content, { onInit: callback });\n\n      expect(callback).toBeCalled();\n    });\n  });\n\n  describe('destroy method', () => {\n    beforeEach(() => {\n      document.body.innerHTML = sectionMarkup;\n      const content = document.querySelector('.test-content');\n\n      scrollnav.init(content);\n    });\n\n    it('should remove the nav from the document', () => {\n      scrollnav.destroy();\n\n      const nav = document.querySelector('nav');\n\n      expect(nav).toBe(null);\n    });\n\n    it('should return a callback after the the nav is removed', () => {\n      const callback = jest.fn();\n\n      scrollnav.destroy({ onDestroy: callback });\n\n      expect(callback).toBeCalled();\n    });\n  });\n\n  describe('updatePositions method', () => {\n    let sections;\n    beforeEach(() => {\n      document.body.innerHTML = sectionMarkup;\n      const content = document.querySelector('.test-content');\n      sections = document.querySelectorAll('h2');\n\n      sections.forEach((elem, i) => {\n        elem.getBoundingClientRect = () => {\n          return {\n            bottom: 800,\n            height: 100,\n            left: 0,\n            right: 800,\n            top: 100 * (i + 1),\n            width: 800\n          };\n        };\n      });\n\n      scrollnav.init(content);\n    });\n\n    it('should update the position data', () => {\n      sections.forEach((elem, i) => {\n        elem.getBoundingClientRect = () => {\n          return {\n            bottom: 800,\n            height: 100,\n            left: 0,\n            right: 800,\n            top: 200 * (i + 1),\n            width: 800\n          };\n        };\n      });\n\n      scrollnav.updatePositions();\n\n      expect(scrollnav.data[0].offsetTop).toEqual(200);\n    });\n\n    it('should return a callback after the positions are updated', () => {\n      const callback = jest.fn();\n\n      sections.forEach((elem, i) => {\n        elem.getBoundingClientRect = () => {\n          return {\n            bottom: 800,\n            height: 100,\n            left: 0,\n            right: 800,\n            top: 200 * (i + 1),\n            width: 800\n          };\n        };\n      });\n\n      scrollnav.updatePositions({ onUpdatePositions: callback });\n\n      expect(callback).toBeCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "test/tests/setupClickHandlers.test.js",
    "content": "import setupClickHandlers from '../../src/setupClickHandlers';\nimport { onlyH2Data, allData } from '../fixtures/sectionData';\nimport { onlyH2Nav, allNav } from '../fixtures/navMarkup';\nimport simulateEvent from '../util/simulateEvent';\n\ndescribe('setupClickHandlers', () => {\n  it('should not trigger a callback after scrolling to a section', async () => {\n    expect.assertions(1);\n\n    const callback = jest.fn();\n    document.body.innerHTML = onlyH2Nav;\n    const nav = document.querySelector('nav');\n    const links = nav.querySelectorAll('a');\n    const scrollnav = {\n      data: onlyH2Data,\n      nav: document.querySelector('nav'),\n      settings: {}\n    };\n\n    setupClickHandlers(scrollnav);\n    await simulateEvent('click', links[0]);\n\n    expect(callback).not.toBeCalled();\n  });\n\n  xit('should trigger a callback after scrolling to a section', async () => {\n    expect.assertions(1);\n\n    const callback = jest.fn();\n    document.body.innerHTML = onlyH2Nav;\n    const nav = document.querySelector('nav');\n    const links = nav.querySelectorAll('a');\n    const scrollnav = {\n      data: onlyH2Data,\n      nav: document.querySelector('nav'),\n      settings: {\n        onScroll: callback\n      }\n    };\n\n    setupClickHandlers(scrollnav);\n    await simulateEvent('click', links[0]);\n\n    expect(callback).toBeCalled();\n  });\n\n  xit('should trigger a callback after scrolling to a sub-section', async () => {\n    expect.assertions(1);\n\n    const callback = jest.fn();\n    document.body.innerHTML = allNav;\n    const nav = document.querySelector('nav');\n    const links = nav.querySelectorAll('a');\n    const scrollnav = {\n      data: allData,\n      nav: nav,\n      settings: {\n        onScroll: callback\n      }\n    };\n\n    setupClickHandlers(scrollnav);\n    await simulateEvent('click', links[2]);\n\n    expect(callback).toBeCalled();\n  });\n});\n"
  },
  {
    "path": "test/tests/setupResizeHandler.test.js",
    "content": "import setupResizeHandler from '../../src/setupResizeHandler';\nimport { html } from '../fixtures/sectionMarkup';\nimport { onlyH2Data } from '../fixtures/sectionData';\nimport simulateEvent from '../util/simulateEvent';\n\ndescribe('setupResizeHandler', () => {\n  let sections;\n  let scrollnav;\n\n  beforeAll(() => {\n    document.body.innerHTML = html;\n    sections = document.querySelectorAll('h2');\n    scrollnav = {\n      data: onlyH2Data\n    };\n\n    setupResizeHandler(scrollnav);\n\n    sections.forEach((elem, i) => {\n      elem.getBoundingClientRect = () => {\n        return {\n          bottom: 800,\n          height: 100,\n          left: 0,\n          right: 800,\n          top: 200 * (i + 1),\n          width: 800\n        };\n      };\n    });\n  });\n\n  it('should update the data when the window is resized', () => {\n    simulateEvent('resize', window);\n\n    const data = scrollnav.data;\n\n    expect(data[0].offsetTop).toEqual(200);\n    expect(data[1].offsetTop).toEqual(400);\n    expect(data[2].offsetTop).toEqual(600);\n  });\n});\n"
  },
  {
    "path": "test/tests/setupScrollHander.test.js",
    "content": "import jestDomCustomMatchers from '@jarmee/jest-dom-custom-matchers';\nimport setupScrollHandler from '../../src/setupScrollHandler';\nimport { onlyH2Data } from '../fixtures/sectionData';\nimport { html as sectionMarkup } from '../fixtures/sectionMarkup';\nimport { onlyH2Nav } from '../fixtures/navMarkup';\nimport simulateEvent from '../util/simulateEvent';\n\nexpect.extend(jestDomCustomMatchers);\n\ndescribe(setupScrollHandler, function() {\n  let nav;\n  let items;\n  let scrollnav;\n\n  beforeEach(() => {\n    document.body.innerHTML = onlyH2Nav + sectionMarkup;\n    nav = document.querySelector('nav');\n    items = nav.querySelectorAll('li');\n    scrollnav = {\n      data: onlyH2Data,\n      nav: nav\n    };\n  });\n\n  it('should not activate any items on load', () => {\n    window.innerHeight = 28;\n\n    setupScrollHandler(scrollnav);\n\n    expect(items[0]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[0]).not.toHaveAttribute('data-sn-active');\n  });\n\n  it('should activate the first item if it is within the boundry', () => {\n    window.innerHeight = 340;\n\n    setupScrollHandler(scrollnav);\n    simulateEvent('scroll', window);\n\n    expect(items[0]).toHaveClass('scroll-nav__item--active');\n    expect(items[0]).toHaveAttribute('data-sn-active');\n  });\n\n  it('should activate the second item and not the first if they are both within the boundry', () => {\n    window.innerHeight = 625;\n\n    setupScrollHandler(scrollnav);\n    simulateEvent('scroll', window);\n\n    expect(items[0]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[0]).not.toHaveAttribute('data-sn-active');\n    expect(items[1]).toHaveClass('scroll-nav__item--active');\n    expect(items[1]).toHaveAttribute('data-sn-active');\n  });\n});\n"
  },
  {
    "path": "test/tests/teardownClickHandlers.test.js",
    "content": "import setupClickHandlers from '../../src/setupClickHandlers';\nimport teardownClickHandlers from '../../src/teardownClickHandlers';\nimport { onlyH2Data } from '../fixtures/sectionData';\nimport { onlyH2Nav } from '../fixtures/navMarkup';\nimport simulateEvent from '../util/simulateEvent';\n\ndescribe('teardownClickHandlers', () => {\n  let nav;\n  let links;\n\n  beforeAll(() => {\n    document.body.innerHTML = onlyH2Nav;\n    nav = document.querySelector('nav');\n    links = nav.querySelectorAll('a');\n  });\n\n  it('should not trigger a callback after click', () => {\n    expect.assertions(1);\n\n    const callback = jest.fn();\n    const scrollnav = {\n      data: onlyH2Data,\n      nav: nav,\n      settings: {\n        onScroll: callback\n      }\n    };\n    const clickHandler = setupClickHandlers(scrollnav);\n\n    teardownClickHandlers(nav, clickHandler);\n    simulateEvent('click', links[0]);\n\n    expect(callback).not.toBeCalled();\n  });\n});\n"
  },
  {
    "path": "test/tests/teardownResizeHandler.test.js",
    "content": "import setupResizeHandler from '../../src/setupResizeHandler';\nimport teardownResizeHandler from '../../src/teardownResizeHandler';\nimport { html } from '../fixtures/sectionMarkup';\nimport { onlyH2Data } from '../fixtures/sectionData';\nimport simulateEvent from '../util/simulateEvent';\n\ndescribe('setupResizeHandler', () => {\n  let sections;\n  let scrollnav;\n  let resizeHandler;\n\n  beforeAll(() => {\n    document.body.innerHTML = html;\n    sections = document.querySelectorAll('h2');\n    scrollnav = {\n      data: onlyH2Data\n    };\n    resizeHandler = setupResizeHandler(scrollnav);\n\n    sections.forEach((elem, i) => {\n      elem.getBoundingClientRect = () => {\n        return {\n          bottom: 800,\n          height: 100,\n          left: 0,\n          right: 800,\n          top: 200 * (i + 1),\n          width: 800\n        };\n      };\n    });\n  });\n\n  it('should not update the data when the window is resized', () => {\n    teardownResizeHandler(resizeHandler);\n\n    simulateEvent('resize', window);\n    const data = scrollnav.data;\n\n    expect(data[0].offsetTop).toEqual(100);\n    expect(data[1].offsetTop).toEqual(200);\n    expect(data[2].offsetTop).toEqual(300);\n  });\n});\n"
  },
  {
    "path": "test/tests/teardownScrollHander.test.js",
    "content": "import jestDomCustomMatchers from '@jarmee/jest-dom-custom-matchers';\nimport setupScrollHandler from '../../src/setupScrollHandler';\nimport teardownScrollHandler from '../../src/teardownScrollHandler';\nimport { onlyH2Data } from '../fixtures/sectionData';\nimport { html as sectionMarkup } from '../fixtures/sectionMarkup';\nimport { onlyH2Nav } from '../fixtures/navMarkup';\nimport simulateEvent from '../util/simulateEvent';\n\nexpect.extend(jestDomCustomMatchers);\n\ndescribe(setupScrollHandler, function() {\n  let nav;\n  let items;\n  let scrollnav;\n\n  beforeEach(() => {\n    document.body.innerHTML = onlyH2Nav + sectionMarkup;\n    nav = document.querySelector('nav');\n    items = nav.querySelectorAll('li');\n    scrollnav = {\n      data: onlyH2Data,\n      nav: nav\n    };\n  });\n\n  it('should not activate the first item if it is within the boundry', () => {\n    window.innerHeight = 340;\n    const scrollHandler = setupScrollHandler(scrollnav);\n\n    teardownScrollHandler(scrollHandler);\n    simulateEvent('scroll', window);\n\n    expect(items[0]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[0]).not.toHaveAttribute('data-sn-active');\n  });\n\n  it('should not activate the second item and or the first if they are both within the boundry', () => {\n    window.innerHeight = 625;\n    const scrollHandler = setupScrollHandler(scrollnav);\n\n    teardownScrollHandler(scrollHandler);\n    simulateEvent('scroll', window);\n\n    expect(items[0]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[0]).not.toHaveAttribute('data-sn-active');\n    expect(items[1]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[1]).not.toHaveAttribute('data-sn-active');\n  });\n});\n"
  },
  {
    "path": "test/tests/util/calculateScrollDuration.test.js",
    "content": "import calculateScrollDuration from '../../../src/util/calculateScrollDuration.js';\n\ndescribe('calculateScrollDuration', () => {\n  const MIN_DURATION = 250;\n  const MAX_DURATION = 1200;\n\n  it('should return the minimum duration', () => {\n    const duration = calculateScrollDuration(200);\n\n    expect(duration).toBe(MIN_DURATION);\n  });\n\n  it('should return the maximum duration', () => {\n    const duration = calculateScrollDuration(3000);\n\n    expect(duration).toBe(MAX_DURATION);\n  });\n\n  it('should return a duration that is half the distance', () => {\n    const duration = calculateScrollDuration(1000);\n\n    expect(duration).toBe(500);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/createList.test.js",
    "content": "import jestDomCustomMatchers from '@jarmee/jest-dom-custom-matchers';\nimport createList from '../../../src/util/createList';\nimport { onlyH2Data, allData } from '../../fixtures/sectionData';\n\nexpect.extend(jestDomCustomMatchers);\n\ndescribe('createList', () => {\n  let testContainer;\n\n  beforeAll(() => {\n    testContainer = document.createElement('div');\n  });\n\n  it('should create an ol element with the correct class name', () => {\n    testContainer.innerHTML = createList(onlyH2Data);\n    const list = testContainer.children[0];\n\n    expect(list).toBeHTMLElement('ol');\n    expect(list).toHaveClass('scroll-nav__list');\n  });\n\n  it(`should include three li elements with the correct class name\n      and data attributes`, () => {\n    testContainer.innerHTML = createList(onlyH2Data);\n    const items = testContainer.children[0].children;\n\n    expect(items.length).toBe(3);\n    expect(items[0]).toBeHTMLElement('li');\n    expect(items[0]).toHaveClass('scroll-nav__item');\n    expect(items[0]).toHaveAttribute('data-sn-section', 'first-heading');\n  });\n\n  it(`should include a link with the correct class name, href,\n      and inner text`, () => {\n    testContainer.innerHTML = createList(onlyH2Data);\n    const links = testContainer.querySelectorAll('a');\n\n    expect(links.length).toBe(3);\n    expect(links[0]).toHaveClass('scroll-nav__link');\n    expect(links[0]).toContainText('First heading');\n    expect(links[0]).toHaveAttribute('href', '#first-heading');\n  });\n\n  it('should not include child ol element if the data does not exist', () => {\n    testContainer.innerHTML = createList(onlyH2Data);\n    const subSections = testContainer.querySelectorAll('ol ol');\n\n    expect(subSections.length).toBe(0);\n  });\n\n  it('should include child ol element with the correct class name', () => {\n    testContainer.innerHTML = createList(allData);\n    const subSections = testContainer.querySelectorAll('ol ol');\n\n    expect(subSections.length).toBe(1);\n    expect(subSections[0]).toBeHTMLElement('ol');\n    expect(subSections[0]).toHaveClass('scroll-nav__sub-list');\n  });\n\n  it(`should include an item in the child ol element with the correct class\n      name`, () => {\n    testContainer.innerHTML = createList(allData);\n    const subItems = testContainer.querySelectorAll('ol ol li');\n\n    expect(subItems.length).toBe(1);\n    expect(subItems[0]).toBeHTMLElement('li');\n    expect(subItems[0]).toHaveClass('scroll-nav__sub-item');\n    expect(subItems[0]).toHaveAttribute('data-sn-section', 'second-heading__1');\n  });\n\n  it(`should include a link with the correct class name, href,\n      and inner text`, () => {\n    testContainer.innerHTML = createList(allData);\n    const subLinks = testContainer.querySelectorAll('ol ol a');\n\n    expect(subLinks.length).toBe(1);\n    expect(subLinks[0]).toBeHTMLElement('a');\n    expect(subLinks[0]).toHaveClass('scroll-nav__sub-link');\n    expect(subLinks[0]).toContainText(\n      'First sub-heading of the second heading'\n    );\n    expect(subLinks[0]).toHaveAttribute('href', '#second-heading__1');\n  });\n});\n"
  },
  {
    "path": "test/tests/util/createNav.test.js",
    "content": "import jestDomCustomMatchers from '@jarmee/jest-dom-custom-matchers';\nimport createNav from '../../../src/util/createNav';\nimport { onlyH2Data } from '../../fixtures/sectionData';\n\nexpect.extend(jestDomCustomMatchers);\n\ndescribe('createNav', () => {\n  it('should create a nav element with the correct class name', () => {\n    const nav = createNav(onlyH2Data);\n\n    expect(nav).toBeHTMLElement('nav');\n    expect(nav).toHaveClass('scroll-nav');\n  });\n\n  it('should contain the list of links', () => {\n    const nav = createNav(onlyH2Data);\n    const list = nav.children;\n\n    expect(list.length).toBe(1);\n    expect(list[0]).toBeHTMLElement('ol');\n    expect(list[0].children.length).toBe(3);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/easing.test.js",
    "content": "require('../../fixtures/pennerEasing');\n\nimport { easing } from '../../../src/util/easing';\n\ndescribe('easing', () => {\n  const VAL_1 = 1;\n  const VAL_2 = 0.5;\n  const VAL_3 = 0.25;\n  const B = 0;\n  const C = 1;\n  const D = 1;\n\n  it('should equal Penner linear', () => {\n    expect(easing.linear(VAL_1)).toBe(Math.linear(VAL_1, B, C, D));\n    expect(easing.linear(VAL_2)).toBe(Math.linear(VAL_2, B, C, D));\n    expect(easing.linear(VAL_3)).toBe(Math.linear(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeInQuad', () => {\n    expect(easing.easeInQuad(VAL_1)).toBe(Math.easeInQuad(VAL_1, B, C, D));\n    expect(easing.easeInQuad(VAL_2)).toBe(Math.easeInQuad(VAL_2, B, C, D));\n    expect(easing.easeInQuad(VAL_3)).toBe(Math.easeInQuad(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeOutQuad', () => {\n    expect(easing.easeOutQuad(VAL_1)).toBe(Math.easeOutQuad(VAL_1, B, C, D));\n    expect(easing.easeOutQuad(VAL_2)).toBe(Math.easeOutQuad(VAL_2, B, C, D));\n    expect(easing.easeOutQuad(VAL_3)).toBe(Math.easeOutQuad(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeInOutQuad', () => {\n    expect(easing.easeInOutQuad(VAL_1)).toBe(\n      Math.easeInOutQuad(VAL_1, B, C, D)\n    );\n    expect(easing.easeInOutQuad(VAL_2)).toBe(\n      Math.easeInOutQuad(VAL_2, B, C, D)\n    );\n    expect(easing.easeInOutQuad(VAL_3)).toBe(\n      Math.easeInOutQuad(VAL_3, B, C, D)\n    );\n  });\n\n  it('should equal Penner easeInCubic', () => {\n    expect(easing.easeInCubic(VAL_1)).toBe(Math.easeInCubic(VAL_1, B, C, D));\n    expect(easing.easeInCubic(VAL_2)).toBe(Math.easeInCubic(VAL_2, B, C, D));\n    expect(easing.easeInCubic(VAL_3)).toBe(Math.easeInCubic(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeOutCubic', () => {\n    expect(easing.easeOutCubic(VAL_1)).toBe(Math.easeOutCubic(VAL_1, B, C, D));\n    expect(easing.easeOutCubic(VAL_2)).toBe(Math.easeOutCubic(VAL_2, B, C, D));\n    expect(easing.easeOutCubic(VAL_3)).toBe(Math.easeOutCubic(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeInOutCubic', () => {\n    expect(easing.easeInOutCubic(VAL_1)).toBe(\n      Math.easeInOutCubic(VAL_1, B, C, D)\n    );\n    expect(easing.easeInOutCubic(VAL_2)).toBe(\n      Math.easeInOutCubic(VAL_2, B, C, D)\n    );\n    expect(easing.easeInOutCubic(VAL_3)).toBe(\n      Math.easeInOutCubic(VAL_3, B, C, D)\n    );\n  });\n\n  it('should equal Penner easeInQuart', () => {\n    expect(easing.easeInQuart(VAL_1)).toBe(Math.easeInQuart(VAL_1, B, C, D));\n    expect(easing.easeInQuart(VAL_2)).toBe(Math.easeInQuart(VAL_2, B, C, D));\n    expect(easing.easeInQuart(VAL_3)).toBe(Math.easeInQuart(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeOutQuart', () => {\n    expect(easing.easeOutQuart(VAL_1)).toBe(Math.easeOutQuart(VAL_1, B, C, D));\n    expect(easing.easeOutQuart(VAL_2)).toBe(Math.easeOutQuart(VAL_2, B, C, D));\n    expect(easing.easeOutQuart(VAL_3)).toBe(Math.easeOutQuart(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeInOutQuart', () => {\n    expect(easing.easeInOutQuart(VAL_1)).toBe(\n      Math.easeInOutQuart(VAL_1, B, C, D)\n    );\n    expect(easing.easeInOutQuart(VAL_2)).toBe(\n      Math.easeInOutQuart(VAL_2, B, C, D)\n    );\n    expect(easing.easeInOutQuart(VAL_3)).toBe(\n      Math.easeInOutQuart(VAL_3, B, C, D)\n    );\n  });\n\n  it('should equal Penner easeInQuint', () => {\n    expect(easing.easeInQuint(VAL_1)).toBe(Math.easeInQuint(VAL_1, B, C, D));\n    expect(easing.easeInQuint(VAL_2)).toBe(Math.easeInQuint(VAL_2, B, C, D));\n    expect(easing.easeInQuint(VAL_3)).toBe(Math.easeInQuint(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeOutQuint', () => {\n    expect(easing.easeOutQuint(VAL_1)).toBe(Math.easeOutQuint(VAL_1, B, C, D));\n    expect(easing.easeOutQuint(VAL_2)).toBe(Math.easeOutQuint(VAL_2, B, C, D));\n    expect(easing.easeOutQuint(VAL_3)).toBe(Math.easeOutQuint(VAL_3, B, C, D));\n  });\n\n  it('should equal Penner easeInOutQuint', () => {\n    expect(easing.easeInOutQuint(VAL_1)).toBe(\n      Math.easeInOutQuint(VAL_1, B, C, D)\n    );\n    expect(easing.easeInOutQuint(VAL_2)).toBe(\n      Math.easeInOutQuint(VAL_2, B, C, D)\n    );\n    expect(easing.easeInOutQuint(VAL_3)).toBe(\n      Math.easeInOutQuint(VAL_3, B, C, D)\n    );\n  });\n});\n"
  },
  {
    "path": "test/tests/util/extend.test.js",
    "content": "import extend from '../../../src/util/extend';\n\ndescribe('extend', () => {\n  const defaults = {\n    number: 1,\n    bool: true,\n    magic: 'real',\n    animal: 'whale',\n    croutons: 'delicious'\n  };\n\n  const options = {\n    number: 2,\n    magic: 'real',\n    animal: 'porpoise',\n    bool: false,\n    random: 42\n  };\n\n  it('should merge two objects', () => {\n    const expected = {\n      animal: 'porpoise',\n      bool: false,\n      croutons: 'delicious',\n      magic: 'real',\n      number: 2,\n      random: 42\n    };\n\n    expect(extend(defaults, options)).toEqual(expected);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/getActiveSection.test.js",
    "content": "import getActiveSection from '../../../src/util/getActiveSection';\nimport { onlyH2Data, allData } from '../../fixtures/sectionData';\n\ndescribe('getActiveSection', () => {\n  it('should return the first section when it falls within the range', () => {\n    const activeSection = getActiveSection(onlyH2Data, 50, 180);\n\n    expect(activeSection).toEqual(onlyH2Data[0]);\n  });\n\n  it('should return the second section when it falls within the range', () => {\n    const activeSection = getActiveSection(onlyH2Data, 150, 280);\n\n    expect(activeSection).not.toEqual(onlyH2Data[0]);\n    expect(activeSection).toEqual(onlyH2Data[1]);\n  });\n\n  it('should return undefined when no section falls within the range', () => {\n    const activeSection = getActiveSection(onlyH2Data, 0, 80);\n\n    expect(activeSection).toEqual(undefined);\n  });\n\n  it(`should return the sub-section and not it's parent when it falls within the\n    range`, () => {\n    const activeSection = getActiveSection(allData, 150, 280);\n\n    expect(activeSection).not.toEqual(allData[1]);\n    expect(activeSection).toEqual(allData[1].subSections[0]);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/getOrSetID.test.js",
    "content": "import getOrSetID from '../../../src/util/getOrSetID';\n\ndescribe('getOrSetID', () => {\n  let elem;\n\n  beforeEach(() => {\n    elem = {};\n  });\n\n  it('should return an error if first param is not an object', () => {\n    return expect(getOrSetID('elem')).rejects.toThrow(\n      'First argument must be an object'\n    );\n  });\n\n  it('should return an error if second param is not a string', () => {\n    return expect(getOrSetID(elem, elem)).rejects.toThrow(\n      'Second argument must be a string'\n    );\n  });\n\n  it('should return the id property of an element that has an id', () => {\n    elem.id = 'test-1-id';\n\n    expect(getOrSetID(elem)).toEqual('test-1-id');\n  });\n\n  it('should not change the id property of an element that has an id', () => {\n    elem.id = 'test-1-id';\n\n    getOrSetID(elem, 'test-1-fail');\n    expect(elem.id).toEqual('test-1-id');\n  });\n\n  it(`should return the passed id argument of an element\n      that does not have a defined id proptery`, () => {\n    elem.id;\n\n    expect(elem.id).toEqual(undefined);\n    expect(getOrSetID(elem, 'test-2-id')).toEqual('test-2-id');\n  });\n\n  it(`should set the id property of an element\n      that does not have a defined id property`, () => {\n    elem.id;\n\n    expect(elem.id).toEqual(undefined);\n    getOrSetID(elem, 'test-2-id');\n    expect(elem.id).toEqual('test-2-id');\n  });\n});\n"
  },
  {
    "path": "test/tests/util/getTargetYPosition.test.js",
    "content": "import getTargetYPosition from '../../../src/util/getTargetYPosition';\nimport { onlyH2Data, allData } from '../../fixtures/sectionData';\nimport { onlyH2Nav, allNav } from '../../fixtures/navMarkup';\n\ndescribe('getTargetYPosition', () => {\n  let nav;\n  let links;\n\n  beforeAll(() => {\n    window.innerHeight = 1000;\n    document.body.innerHTML = onlyH2Nav;\n    nav = document.querySelector('nav');\n    links = nav.querySelectorAll('a');\n  });\n\n  it('should return the Y position of the section element', () => {\n    document.body.innerHTML = onlyH2Nav;\n    nav = document.querySelector('nav');\n    links = nav.querySelectorAll('a');\n\n    const targetYPosition = getTargetYPosition(links[0], onlyH2Data);\n\n    expect(targetYPosition).toEqual(onlyH2Data[0].offsetTop);\n  });\n\n  it('should return the Y position of the sub-section element', () => {\n    document.body.innerHTML = allNav;\n    nav = document.querySelector('nav');\n    links = nav.querySelectorAll('ol ol a');\n\n    const targetYPosition = getTargetYPosition(links[0], allData);\n\n    expect(targetYPosition).toEqual(allData[1].subSections[0].offsetTop);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/getYPosition.test.js",
    "content": "import getYPosition from '../../../src/util/getYPosition';\n\ndescribe('getYPosition', () => {\n  let elem;\n  const WINDOW_TOP = 0;\n  const PARENT_TOP = 140;\n  const ELEM_TOP = 260;\n\n  beforeAll(() => {\n    document.body.getBoundingClientRect = () => {\n      return {\n        bottom: 1000,\n        height: 1000,\n        left: 0,\n        right: 800,\n        top: WINDOW_TOP,\n        width: 800\n      };\n    };\n  });\n\n  beforeEach(() => {\n    elem = {};\n    elem.getBoundingClientRect = () => {\n      return {\n        bottom: 700,\n        height: 40,\n        left: 0,\n        right: 800,\n        top: ELEM_TOP,\n        width: 800\n      };\n    };\n  });\n\n  it('should return an error if first param is not an object', () => {\n    return expect(getYPosition('elem')).rejects.toThrow(\n      'First argument must be an object'\n    );\n  });\n\n  it('should return an error if second param is not an object', () => {\n    return expect(getYPosition(elem, 'parent')).rejects.toThrow(\n      'Second argument must be an object'\n    );\n  });\n\n  it(`should return the Y position of the element relative to the window\n      body if no parent is passed`, () => {\n    expect(getYPosition(elem)).toEqual(ELEM_TOP - WINDOW_TOP);\n  });\n\n  it(`should return the Y position of the element relative to the window body\n      if the window body is passed`, () => {\n    expect(getYPosition(elem, window.body)).toEqual(ELEM_TOP - WINDOW_TOP);\n  });\n\n  it(`should return the Y position of the element relative to a parent elem\n      if the parent elem is passed`, () => {\n    const parentElem = {\n      getBoundingClientRect: () => {\n        return {\n          bottom: 640,\n          height: 500,\n          left: 0,\n          right: 800,\n          top: PARENT_TOP,\n          width: 800\n        };\n      }\n    };\n\n    expect(getYPosition(elem, parentElem)).toEqual(ELEM_TOP - PARENT_TOP);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/insertNav.test.js",
    "content": "import insertNav from '../../../src/util/insertNav';\nimport { html as content } from '../../fixtures/sectionMarkup';\n\ndescribe('insertNav', () => {\n  let testContent;\n  let newDiv;\n  let scrollnav;\n\n  beforeEach(() => {\n    document.body.innerHTML = content;\n    testContent = document.querySelector('.test-content');\n    newDiv = document.createElement('div');\n    newDiv.className = 'new-div';\n    scrollnav = {\n      settings: { insertTarget: testContent },\n      nav: newDiv\n    };\n  });\n\n  it('should append the new element as the last child of the existing element', () => {\n    const lastChild = testContent.lastChild;\n\n    scrollnav.settings.insertLocation = 'append';\n    insertNav(scrollnav);\n\n    const foundDiv = document.querySelector('.new-div');\n\n    expect(foundDiv.parentNode).toEqual(testContent);\n    expect(foundDiv.nextSibling).toEqual(null);\n    expect(foundDiv.previousSibling).toEqual(lastChild);\n  });\n\n  it('should prepend the new element as the first child of the existing element', () => {\n    const firstChild = testContent.firstChild;\n\n    scrollnav.settings.insertLocation = 'prepend';\n    insertNav(scrollnav);\n\n    const foundDiv = document.querySelector('.new-div');\n\n    expect(foundDiv.parentNode).toEqual(testContent);\n    expect(foundDiv.nextSibling).toEqual(firstChild);\n    expect(foundDiv.previousSibling).toEqual(null);\n  });\n\n  it('should insert the new element as the previous sibling of the existing element', () => {\n    scrollnav.settings.insertLocation = 'before';\n    insertNav(scrollnav);\n\n    const foundDiv = document.querySelector('.new-div');\n\n    expect(foundDiv.parentNode).toEqual(document.body);\n    expect(foundDiv.nextSibling).toEqual(testContent);\n  });\n\n  it('should insert the new element as the next sibling of the existing element', () => {\n    scrollnav.settings.insertLocation = 'after';\n    insertNav(scrollnav);\n\n    const foundDiv = document.querySelector('.new-div');\n\n    expect(foundDiv.parentNode).toEqual(document.body);\n    expect(foundDiv.previousSibling).toEqual(testContent);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/insertVisualDebugger.test.js",
    "content": "import jestDomCustomMatchers from '@jarmee/jest-dom-custom-matchers';\nimport insertVisualDebugger from '../../../src/util/insertVisualDebugger';\n\nexpect.extend(jestDomCustomMatchers);\n\ndescribe('insertVisualDebugger', () => {\n  it('should insert the debugger with the correct dimensions', () => {\n    insertVisualDebugger();\n\n    const debuggerDom = document.querySelector('.snDebugger');\n\n    expect(debuggerDom).toBeHTMLElement('div');\n    expect(debuggerDom).toHaveClass('snDebugger');\n  });\n});\n"
  },
  {
    "path": "test/tests/util/nextUntil.test.js",
    "content": "import nextUntil from '../../../src/util/nextUntil';\nimport { html } from '../../fixtures/sectionMarkup.js';\n\ndescribe('nextUntil', () => {\n  let sections;\n  let subSections;\n\n  beforeAll(() => {\n    document.body.innerHTML = html;\n    sections = document.querySelectorAll('h2');\n    subSections = document.querySelectorAll('h3');\n  });\n\n  it('should return the subsections that belong to a section', () => {\n    const data = nextUntil(sections[1], 'h2', 'h3');\n\n    expect(data.length).toBe(1);\n    expect(data).toEqual([subSections[0]]);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/populateSectionData.test.js",
    "content": "import populateSectionData from '../../../src/util/populateSectionData';\nimport { html } from '../../fixtures/sectionMarkup';\nimport { onlyH2Data, allData } from '../../fixtures/sectionData';\n\ndescribe('populateSectionData', () => {\n  let sections;\n  let subSections;\n  const settings = {};\n\n  beforeAll(() => {\n    document.body.innerHTML = html;\n    sections = document.querySelectorAll('h2');\n    subSections = document.querySelectorAll('h3');\n    settings.sections = 'h2';\n    sections.forEach((elem, i) => {\n      elem.getBoundingClientRect = () => {\n        return {\n          bottom: 800,\n          height: 100,\n          left: 0,\n          right: 800,\n          top: 100 * (i + 1),\n          width: 800\n        };\n      };\n    });\n    subSections.forEach(elem => {\n      elem.getBoundingClientRect = () => {\n        return {\n          bottom: 800,\n          height: 25,\n          left: 0,\n          right: 800,\n          top: 225,\n          width: 800\n        };\n      };\n    });\n  });\n\n  it('should find and add all h2 elements to the array', () => {\n    const data = populateSectionData(sections, settings);\n\n    expect(data.length).toBe(3);\n    expect(data).toEqual(onlyH2Data);\n  });\n\n  it('should find and add all h3 elements when showSubItems is enabled', () => {\n    settings.subSections = 'h3';\n    const data = populateSectionData(sections, settings);\n\n    expect(data[1].subSections.length).toBe(1);\n    expect(data[1].subSections).toEqual(allData[1].subSections);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/updateActiveNavItem.test.js",
    "content": "import jestDomCustomMatchers from '@jarmee/jest-dom-custom-matchers';\nimport updateActiveNavItem from '../../../src/util/updateActiveNavItem';\nimport { onlyH2Data } from '../../fixtures/sectionData';\nimport { onlyH2Nav } from '../../fixtures/navMarkup';\n\nexpect.extend(jestDomCustomMatchers);\n\ndescribe('updateActiveNavItem', () => {\n  let nav;\n  let items;\n\n  beforeAll(() => {\n    document.body.innerHTML = onlyH2Nav;\n    nav = document.querySelector('nav');\n    items = nav.querySelectorAll('li');\n  });\n\n  it('should add the active class and data attribute', () => {\n    updateActiveNavItem(onlyH2Data[0], nav);\n\n    expect(items[0]).toHaveClass('scroll-nav__item--active');\n    expect(items[0]).toHaveAttribute('data-sn-active', true);\n  });\n\n  it('should remove the active class and data attribute', () => {\n    items[0].classList.add('scroll-nav__item--active');\n    items[0].setAttribute('data-sn-active', true);\n\n    updateActiveNavItem(undefined, nav);\n\n    expect(items[0]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[0]).not.toHaveAttribute('data-sn-active', true);\n  });\n\n  it('should switch the active class and data attributes', () => {\n    items[0].classList.add('scroll-nav__item--active');\n    items[0].setAttribute('data-sn-active', true);\n\n    updateActiveNavItem(onlyH2Data[2], nav);\n\n    expect(items[0]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[0]).not.toHaveAttribute('data-sn-active', true);\n    expect(items[2]).toHaveClass('scroll-nav__item--active');\n    expect(items[2]).toHaveAttribute('data-sn-active', true);\n  });\n\n  it('should do nothing if there is no previous active item', () => {\n    updateActiveNavItem(undefined, nav);\n\n    expect(items[0]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[0]).not.toHaveAttribute('data-sn-active', true);\n    expect(items[2]).not.toHaveClass('scroll-nav__item--active');\n    expect(items[2]).not.toHaveAttribute('data-sn-active', true);\n  });\n\n  it(`should do nothing if the previous active item is the same as the current\n    active item`, () => {\n    items[0].classList.add('scroll-nav__item--active');\n    items[0].setAttribute('data-sn-active', true);\n\n    updateActiveNavItem(onlyH2Data[0], nav);\n\n    expect(items[0]).toHaveClass('scroll-nav__item--active');\n    expect(items[0]).toHaveAttribute('data-sn-active', true);\n  });\n});\n"
  },
  {
    "path": "test/tests/util/updatePositionData.test.js",
    "content": "import updatePositionData from '../../../src/util/updatePositionData';\nimport { html } from '../../fixtures/sectionMarkup';\nimport { onlyH2Data, allData } from '../../fixtures/sectionData';\n\ndescribe('updatePositionData', () => {\n  let sections;\n  let subSections;\n\n  beforeAll(() => {\n    document.body.innerHTML = html;\n    sections = document.querySelectorAll('h2');\n    subSections = document.querySelectorAll('h3');\n    subSections[0].id = 'second-heading__1';\n\n    sections.forEach((elem, i) => {\n      elem.getBoundingClientRect = () => {\n        return {\n          bottom: 800,\n          height: 100,\n          left: 0,\n          right: 800,\n          top: 200 * (i + 1),\n          width: 800\n        };\n      };\n    });\n    subSections.forEach(elem => {\n      elem.getBoundingClientRect = () => {\n        return {\n          bottom: 800,\n          height: 25,\n          left: 0,\n          right: 800,\n          top: 450,\n          width: 800\n        };\n      };\n    });\n  });\n\n  it('should get the correct positions of each section', () => {\n    const data = updatePositionData(onlyH2Data);\n\n    expect(data[0].offsetTop).toEqual(200);\n    expect(data[1].offsetTop).toEqual(400);\n    expect(data[2].offsetTop).toEqual(600);\n  });\n\n  it('should get the correct positions of each sub-section', () => {\n    const data = updatePositionData(allData);\n\n    expect(data[0].offsetTop).toEqual(200);\n    expect(data[1].offsetTop).toEqual(400);\n    expect(data[1].subSections[0].offsetTop).toEqual(450);\n    expect(data[2].offsetTop).toEqual(600);\n  });\n});\n"
  },
  {
    "path": "test/util/simulateEvent.js",
    "content": "/**\n * @param {string} eventType - The type of event.\n * @param {HTMLNode} target - Target of the event.\n * @param {Object} eventOption - Options to add to the event.\n * @returns {HTMLNode} The target of the event.\n */\nexport default function simulateEvent(eventType, target, eventOption) {\n  const event = document.createEvent('Event');\n  if (eventOption && eventOption.keyCode) {\n    event.keyCode = eventOption.keyCode;\n  }\n  event.initEvent(eventType, true, true);\n  return target.dispatchEvent(event);\n}\n"
  }
]