├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── workflows │ ├── bandit.yml │ ├── docker-image.yml │ ├── pep8.yml │ ├── python-publish.yml │ ├── static.yml │ └── unittest.yml ├── LICENSE ├── README.md ├── TODO.md ├── docker ├── Dockerfile ├── README.md ├── remove.sh └── start.sh ├── docs ├── github │ └── graphical_resources │ │ ├── IPRoyal-Logo_Transparent_500x500.png │ │ ├── Logo-Without_background-MetaDetective.png │ │ ├── Logo-Without_background-Without_text-MetaDetective.png │ │ ├── MetaDetective_promotional_trailer.mp4 │ │ ├── Screenshot-MetaDetective_Demo.png │ │ ├── Screenshot-MetaDetective_HTML_Export_Demo.png │ │ └── Screenshot-MetaDetective_Scraping_Demo.png └── website │ ├── css │ ├── base.css │ ├── fonts.css │ ├── ionicons │ │ ├── css │ │ │ ├── ionicons.css │ │ │ └── ionicons.min.css │ │ └── fonts │ │ │ ├── ionicons.eot │ │ │ ├── ionicons.svg │ │ │ ├── ionicons.ttf │ │ │ └── ionicons.woff │ ├── main.css │ ├── micons │ │ ├── fonts │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.svg │ │ │ ├── icomoon.ttf │ │ │ └── icomoon.woff │ │ └── micons.css │ └── vendor.css │ ├── favicon.png │ ├── fonts │ ├── merriweather │ │ ├── merriweather-black-webfont.eot │ │ ├── merriweather-black-webfont.svg │ │ ├── merriweather-black-webfont.ttf │ │ ├── merriweather-black-webfont.woff │ │ ├── merriweather-bold-webfont.eot │ │ ├── merriweather-bold-webfont.svg │ │ ├── merriweather-bold-webfont.ttf │ │ ├── merriweather-bold-webfont.woff │ │ ├── merriweather-bolditalic-webfont.eot │ │ ├── merriweather-bolditalic-webfont.svg │ │ ├── merriweather-bolditalic-webfont.ttf │ │ ├── merriweather-bolditalic-webfont.woff │ │ ├── merriweather-heavyitalic-webfont.eot │ │ ├── merriweather-heavyitalic-webfont.svg │ │ ├── merriweather-heavyitalic-webfont.ttf │ │ ├── merriweather-heavyitalic-webfont.woff │ │ ├── merriweather-italic-webfont.eot │ │ ├── merriweather-italic-webfont.svg │ │ ├── merriweather-italic-webfont.ttf │ │ ├── merriweather-italic-webfont.woff │ │ ├── merriweather-light-webfont.eot │ │ ├── merriweather-light-webfont.svg │ │ ├── merriweather-light-webfont.ttf │ │ ├── merriweather-light-webfont.woff │ │ ├── merriweather-lightitalic-webfont.eot │ │ ├── merriweather-lightitalic-webfont.svg │ │ ├── merriweather-lightitalic-webfont.ttf │ │ ├── merriweather-lightitalic-webfont.woff │ │ ├── merriweather-regular-webfont.eot │ │ ├── merriweather-regular-webfont.svg │ │ ├── merriweather-regular-webfont.ttf │ │ └── merriweather-regular-webfont.woff │ ├── montserrat │ │ ├── montserrat-bold-webfont.eot │ │ ├── montserrat-bold-webfont.svg │ │ ├── montserrat-bold-webfont.ttf │ │ ├── montserrat-bold-webfont.woff │ │ ├── montserrat-regular-webfont.eot │ │ ├── montserrat-regular-webfont.svg │ │ ├── montserrat-regular-webfont.ttf │ │ └── montserrat-regular-webfont.woff │ └── raleway │ │ ├── raleway-bold-webfont.eot │ │ ├── raleway-bold-webfont.svg │ │ ├── raleway-bold-webfont.ttf │ │ ├── raleway-bold-webfont.woff │ │ ├── raleway-extrabold-webfont.eot │ │ ├── raleway-extrabold-webfont.svg │ │ ├── raleway-extrabold-webfont.ttf │ │ ├── raleway-extrabold-webfont.woff │ │ ├── raleway-extralight-webfont.eot │ │ ├── raleway-extralight-webfont.svg │ │ ├── raleway-extralight-webfont.ttf │ │ ├── raleway-extralight-webfont.woff │ │ ├── raleway-heavy-webfont.eot │ │ ├── raleway-heavy-webfont.svg │ │ ├── raleway-heavy-webfont.ttf │ │ ├── raleway-heavy-webfont.woff │ │ ├── raleway-light-webfont.eot │ │ ├── raleway-light-webfont.svg │ │ ├── raleway-light-webfont.ttf │ │ ├── raleway-light-webfont.woff │ │ ├── raleway-medium-webfont.eot │ │ ├── raleway-medium-webfont.svg │ │ ├── raleway-medium-webfont.ttf │ │ ├── raleway-medium-webfont.woff │ │ ├── raleway-regular-webfont.eot │ │ ├── raleway-regular-webfont.svg │ │ ├── raleway-regular-webfont.ttf │ │ ├── raleway-regular-webfont.woff │ │ ├── raleway-semibold-webfont.eot │ │ ├── raleway-semibold-webfont.svg │ │ ├── raleway-semibold-webfont.ttf │ │ ├── raleway-semibold-webfont.woff │ │ ├── raleway-thin-webfont.eot │ │ ├── raleway-thin-webfont.svg │ │ ├── raleway-thin-webfont.ttf │ │ └── raleway-thin-webfont.woff │ ├── images │ ├── footer-logo.png │ ├── intro-bg.jpg │ ├── logo.png │ └── play-button.png │ ├── index.html │ └── js │ ├── jquery-1.11.3.min.js │ ├── jquery-migrate-1.2.1.min.js │ ├── main.js │ ├── modernizr.js │ └── plugins.js ├── examples ├── Black_Organization.png ├── MetaDetective-APTX_4869_report.pdf ├── MetaDetective-Kogoro_s_Choice.pdf ├── MetaDetective-Scuba_Diving_Murder_Case.pdf ├── MetaDetective.docx └── MetaDetective.png ├── pyproject.toml ├── src └── MetaDetective │ └── MetaDetective.py └── tests └── test_MetaDetective.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | # patreon: # Replace with a single Patreon username 5 | # open_collective: # Replace with a single Open Collective username 6 | # ko_fi: # Replace with a single Ko-fi username 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | # polar: # Replace with a single Polar username 13 | # buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | # thanks_dev: # Replace with a single thanks.dev username 15 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | 17 | custom: ['https://iproyal.com/?r=848799'] 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Your detailed bug reports are pivotal in elevating this project's quality. 4 | Your expertise in identifying issues is deeply valued and appreciated. 5 | title: "[ISSUE] Problem Encountered" 6 | labels: bug 7 | assignees: franckferman 8 | 9 | --- 10 | 11 | ## Problem Summary 12 | _Provide a clear and concise summary of the encountered issue._ 13 | 14 | ## Steps to Reproduce 15 | Provide a step-by-step description on how to reproduce the anomaly: 16 | 17 | 1. Command initiated: `...` 18 | 2. During the process: `....` 19 | 3. Observed issue: `....` 20 | 4. Output/response anomaly: `...` 21 | 22 | ## Expected Outcome 23 | _Describe the anticipated result after executing the provided steps._ 24 | 25 | ## Visual Evidence 26 | If possible, attach screenshots to support your description. 27 | 28 | ## Technical Details 29 | 30 | ### Operating System 31 | 32 | - **Linux:** [e.g. Linux root 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20) x86_64 GNU/Linux] 33 | *Retrieval:* `uname -a` 34 | 35 | - **Windows:** [e.g. Microsoft Windows 10 Pro DevBox 10.0.15063 Multiprocessor Free 64-bit] 36 | *Retrieval:* 37 | ```powershell 38 | $Properties = 'Caption', 'CSName', 'Version', 'BuildType', 'OSArchitecture' 39 | Get-CimInstance Win32_OperatingSystem | Select-Object $Properties | Format-Table -AutoSize 40 | ``` 41 | 42 | ### Software Versions 43 | 44 | - **Python**: [e.g. Python 3.11.4] 45 | *Retrieval:* `python3 -V` 46 | 47 | - **Exiftool**: [e.g. 12.56] 48 | *Retrieval:* `exiftool -ver` 49 | 50 | - **MetaDetective**: [e.g. 1.0.8] 51 | 52 | ### Docker (if used) 53 | 54 | - **Image Version**: [e.g. "1.0.1"] 55 | 56 | ## Additional Information 57 | 58 | Provide any other pertinent details or context regarding the issue. 59 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Clearly outline the primary goal of this feature request. 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: franckferman 7 | 8 | --- 9 | 10 | **Problem Nexus:** 11 | _Can you link this feature request to an existing problem or limitation?_ 12 | Provide a precise and thorough depiction of the issue. For instance, "The current workflow requires manual data entry, leading to inefficiencies and potential for error." 13 | 14 | **Envisioned Solution:** 15 | _Detail the desired feature or enhancement._ 16 | Expound upon how you imagine this feature will function, how it will address the problem, and the advantages it will introduce. 17 | 18 | **Alternative Strategies Assessed:** 19 | _Have you thought of other potential solutions?_ 20 | List and briefly explain other solutions or functionalities you've considered. Highlight why they might be less optimal than your proposed solution. 21 | 22 | **Implications and Integration:** 23 | _How will this feature fit into the current system?_ 24 | Discuss any potential interactions with existing features, how it aligns with the project's goals, or any other relevant integrations. 25 | 26 | **Visual Demonstrations (if applicable):** 27 | _Do you have visual aids or mock-ups?_ 28 | Embed any diagrams, wireframes, or screenshots that can provide clarity on the desired feature's function and integration. 29 | -------------------------------------------------------------------------------- /.github/workflows/bandit.yml: -------------------------------------------------------------------------------- 1 | name: bandit 2 | 3 | on: 4 | push: 5 | branches: [ "stable" ] 6 | paths: 7 | - 'src/MetaDetective/MetaDetective.py' 8 | pull_request: 9 | branches: [ "stable" ] 10 | paths: 11 | - 'src/MetaDetective/MetaDetective.py' 12 | 13 | jobs: 14 | security-check: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout Repository 19 | uses: actions/checkout@v2 20 | 21 | - name: Set up Python 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: 3.11 25 | 26 | - name: Install Bandit 27 | run: pip install bandit 28 | 29 | - name: Run Bandit 30 | run: bandit ./src/MetaDetective/MetaDetective.py -s B404,B607,B603,B310 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: docker-image 2 | 3 | on: 4 | push: 5 | branches: [ "stable" ] 6 | paths: 7 | - 'docker/Dockerfile' 8 | pull_request: 9 | branches: [ "stable" ] 10 | paths: 11 | - 'docker/Dockerfile' 12 | 13 | jobs: 14 | 15 | build_and_push: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Build the Docker image 23 | run: docker build . --file docker/Dockerfile --tag franckferman/metadetective:1.0.9-df.2 24 | 25 | - name: Login to Docker Hub 26 | run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} 27 | - name: Push Docker Image 28 | run: docker push franckferman/metadetective:1.0.9-df.2 29 | -------------------------------------------------------------------------------- /.github/workflows/pep8.yml: -------------------------------------------------------------------------------- 1 | name: pep8 2 | 3 | on: 4 | push: 5 | branches: [ "stable" ] 6 | paths: 7 | - 'tests/test_MetaDetective.py' 8 | - 'src/MetaDetective/MetaDetective.py' 9 | pull_request: 10 | branches: [ "stable" ] 11 | paths: 12 | - 'tests/test_MetaDetective.py' 13 | - 'src/MetaDetective/MetaDetective.py' 14 | 15 | jobs: 16 | flake8: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout Repository 21 | uses: actions/checkout@v2 22 | 23 | - name: Set up Python 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: 3.11 27 | 28 | - name: Install flake8 29 | run: pip install flake8 30 | 31 | - name: Check PEP8 compliance 32 | run: flake8 . --count --ignore=E501 33 | 34 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | name: python-publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up Python 18 | uses: actions/setup-python@v3 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install build 25 | - name: Build package 26 | run: python -m build 27 | - name: Publish package 28 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 29 | with: 30 | user: __token__ 31 | password: ${{ secrets.PYPI_API_TOKEN }} 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: static 2 | 3 | on: 4 | push: 5 | branches: [ "stable" ] 6 | paths: 7 | - 'docs/website/**' 8 | pull_request: 9 | branches: [ "stable" ] 10 | paths: 11 | - 'docs/website/**' 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 17 | permissions: 18 | contents: read 19 | pages: write 20 | id-token: write 21 | 22 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 23 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 24 | concurrency: 25 | group: "pages" 26 | cancel-in-progress: false 27 | 28 | jobs: 29 | # Single deploy job since we're just deploying 30 | deploy: 31 | environment: 32 | name: github-pages 33 | url: ${{ steps.deployment.outputs.page_url }} 34 | runs-on: ubuntu-latest 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v3 38 | - name: Setup Pages 39 | uses: actions/configure-pages@v3 40 | - name: Upload artifact 41 | uses: actions/upload-pages-artifact@v2 42 | with: 43 | # Upload entire repository 44 | path: './docs/website' 45 | - name: Deploy to GitHub Pages 46 | id: deployment 47 | uses: actions/deploy-pages@v2 48 | -------------------------------------------------------------------------------- /.github/workflows/unittest.yml: -------------------------------------------------------------------------------- 1 | name: unittest 2 | 3 | on: 4 | push: 5 | branches: [ "stable" ] 6 | paths: 7 | - 'tests/test_MetaDetective.py' 8 | - 'src/MetaDetective/MetaDetective.py' 9 | pull_request: 10 | branches: [ "stable" ] 11 | paths: 12 | - 'tests/test_MetaDetective.py' 13 | - 'src/MetaDetective/MetaDetective.py' 14 | 15 | jobs: 16 | unittest: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout Repository 21 | uses: actions/checkout@v2 22 | 23 | - name: Set up Python 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: 3.11 27 | 28 | - name: Run Unit Tests 29 | run: python -m unittest discover tests -p '*test*.py' 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
5 |
6 |
7 |
8 |
This project is supported by IPRoyal
10 | Trusted proxy services for scraping, OSINT and cybersecurity research.
29 | Unleash Metadata Intelligence with MetaDetective.
30 |
31 | Bridging the chasm in metadata extraction and analysis.
32 |
36 | 📘 Explore the full documentation 37 | · 38 | 🎥 View Demo 39 | · 40 | 🐞 Report Bug 41 | · 42 | 🛠️ Request Feature 43 |
44 | 45 | https://github.com/franckferman/MetaDetective/assets/73023545/7b245f87-37e2-40b7-8b3c-88aefecb4e13 46 | 47 |
83 |
84 |
91 |
92 |
318 |
319 |
MetaDetective is an advanced tool built for seamless metadata extraction and analysis, offering a suite of features tailored for cybersecurity experts.
112 | 113 |Designed with efficiency and precision in mind, MetaDetective ensures robust metadata extraction. Its unique approach bridges existing gaps in metadata analysis, offering insightful perspectives crucial for OSINT and pentesting. 129 |
130 | 131 |Beyond mere extraction, MetaDetective showcases metadata in a meticulously categorized manner. It provides a comprehensive view, ensuring users grasp the complete spectrum of data for an array of files. 143 |
144 | 145 | 146 |Unlike tools relying solely on search engines, MetaDetective directly targets websites for data scraping. This method offers a richer and more precise dataset, spotlighting potential data leaks without the need for complex proxy workarounds. 158 |
159 | 160 | 161 |Export your analysis findings with ease. MetaDetective offers enhanced data presentation in HTML format for a comprehensive view, while also providing a TXT format for those who need it. 173 |
174 | 175 | 176 |MetaDetective is designed for synergy with other tools, making it a quintessential addition for pentesters and OSINT researchers. Enhance your data acquisition and broaden your analysis horizons with MetaDetective. 188 |
189 | 190 | 191 |MetaDetective is always evolving. Stay tuned for more advanced features and functionalities that are currently under development. We're committed to providing top-notch capabilities to our user community. 203 |
204 | 205 |Dive into our FAQ section to learn more about MetaDetective, its inception, and its significance in the world of OSINT and pentesting.
227 | 228 |Metadata is hidden information within files that can describe or point to other data. This can include the origin of the file, the creator, creation date, and much more. They are often embedded within various file types, including documents and images.
240 | 241 |MetaDetective was born out of the desire to offer in-depth metadata analysis with an intuitive visualization. With key tools shifting away from pure metadata analysis, the need for a robust alternative arose. MetaDetective fills this void by offering a powerful solution for metadata extraction and analysis.
248 | 249 |Metadata can reveal a wealth of information about a potential target. This might include details about the operating system, software used, locations, and even user habits. For OSINT investigators and pentesters, such information can provide potential entry points, expose vulnerabilities, or give insights into an organization's internal operations.
256 | 257 |Searching for information solely through search engines like Google presents limitations, including IP restrictions and the need for proxy use. By opting for direct web scraping, MetaDetective bypasses these challenges, delivering richer and more accurate data while shedding light on potential data leaks.
264 | 265 |Once data is collected and analyzed by MetaDetective, you have the capability to export your findings. The primary export format is HTML, providing a structured and aesthetic presentation of metadata. However, for those preferring a rawer approach, a TXT format export is also available.
272 | 273 |We are continually working on enhancing MetaDetective by adding new features and optimizing the existing ones. Keep an eye on our GitHub page for updates, and feel free to contribute or share your feedback.
280 | 281 |296 | If you're looking to sponsor and gain visibility with a community of cybersecurity enthusiasts and professionals, this is the perfect spot for you! By becoming a sponsor, you have the opportunity to showcase your brand or product to a dedicated and tech-savvy audience. If you're interested in this unique sponsorship opportunity, please get in touch to discuss potential collaboration. 297 |
298 | 299 |Download MetaDetective Now. Available on:
323 | 324 |x
",d.appendChild(f.childNodes[1])}return b&&a.extend(c,b),this.each(function(){var b=['iframe[src*="player.vimeo.com"]','iframe[src*="youtube.com"]','iframe[src*="youtube-nocookie.com"]','iframe[src*="kickstarter.com"][src*="video.html"]',"object","embed"];c.customSelector&&b.push(c.customSelector);var d=".fitvidsignore";c.ignore&&(d=d+", "+c.ignore);var e=a(this).find(b.join(","));e=e.not("object object"),e=e.not(d),e.each(function(b){var c=a(this);if(!(c.parents(d).length>0||"embed"===this.tagName.toLowerCase()&&c.parent("object").length||c.parent(".fluid-width-video-wrapper").length)){c.css("height")||c.css("width")||!isNaN(c.attr("height"))&&!isNaN(c.attr("width"))||(c.attr("height",9),c.attr("width",16));var e="object"===this.tagName.toLowerCase()||c.attr("height")&&!isNaN(parseInt(c.attr("height"),10))?parseInt(c.attr("height"),10):c.height(),f=isNaN(parseInt(c.attr("width"),10))?c.width():parseInt(c.attr("width"),10),g=e/f;if(!c.attr("id")){var h="fitvid"+b;c.attr("id",h)}c.wrap('').parent(".fluid-width-video-wrapper").css("padding-top",100*g+"%"),c.removeAttr("height").removeAttr("width")}})})}}(window.jQuery||window.Zepto); 26 | 27 | 28 | /*! 29 | Waypoints - 4.0.0 30 | Copyright © 2011-2015 Caleb Troughton 31 | Licensed under the MIT license. 32 | https://github.com/imakewebthings/waypoints/blog/master/licenses.txt 33 | */ 34 | !function(){"use strict";function t(o){if(!o)throw new Error("No options passed to Waypoint constructor");if(!o.element)throw new Error("No element option passed to Waypoint constructor");if(!o.handler)throw new Error("No handler option passed to Waypoint constructor");this.key="waypoint-"+e,this.options=t.Adapter.extend({},t.defaults,o),this.element=this.options.element,this.adapter=new t.Adapter(this.element),this.callback=o.handler,this.axis=this.options.horizontal?"horizontal":"vertical",this.enabled=this.options.enabled,this.triggerPoint=null,this.group=t.Group.findOrCreate({name:this.options.group,axis:this.axis}),this.context=t.Context.findOrCreateByElement(this.options.context),t.offsetAliases[this.options.offset]&&(this.options.offset=t.offsetAliases[this.options.offset]),this.group.add(this),this.context.add(this),i[this.key]=this,e+=1}var e=0,i={};t.prototype.queueTrigger=function(t){this.group.queueTrigger(this,t)},t.prototype.trigger=function(t){this.enabled&&this.callback&&this.callback.apply(this,t)},t.prototype.destroy=function(){this.context.remove(this),this.group.remove(this),delete i[this.key]},t.prototype.disable=function(){return this.enabled=!1,this},t.prototype.enable=function(){return this.context.refresh(),this.enabled=!0,this},t.prototype.next=function(){return this.group.next(this)},t.prototype.previous=function(){return this.group.previous(this)},t.invokeAll=function(t){var e=[];for(var o in i)e.push(i[o]);for(var n=0,r=e.length;r>n;n++)e[n][t]()},t.destroyAll=function(){t.invokeAll("destroy")},t.disableAll=function(){t.invokeAll("disable")},t.enableAll=function(){t.invokeAll("enable")},t.refreshAll=function(){t.Context.refreshAll()},t.viewportHeight=function(){return window.innerHeight||document.documentElement.clientHeight},t.viewportWidth=function(){return document.documentElement.clientWidth},t.adapters=[],t.defaults={context:window,continuous:!0,enabled:!0,group:"default",horizontal:!1,offset:0},t.offsetAliases={"bottom-in-view":function(){return this.context.innerHeight()-this.adapter.outerHeight()},"right-in-view":function(){return this.context.innerWidth()-this.adapter.outerWidth()}},window.Waypoint=t}(),function(){"use strict";function t(t){window.setTimeout(t,1e3/60)}function e(t){this.element=t,this.Adapter=n.Adapter,this.adapter=new this.Adapter(t),this.key="waypoint-context-"+i,this.didScroll=!1,this.didResize=!1,this.oldScroll={x:this.adapter.scrollLeft(),y:this.adapter.scrollTop()},this.waypoints={vertical:{},horizontal:{}},t.waypointContextKey=this.key,o[t.waypointContextKey]=this,i+=1,this.createThrottledScrollHandler(),this.createThrottledResizeHandler()}var i=0,o={},n=window.Waypoint,r=window.onload;e.prototype.add=function(t){var e=t.options.horizontal?"horizontal":"vertical";this.waypoints[e][t.key]=t,this.refresh()},e.prototype.checkEmpty=function(){var t=this.Adapter.isEmptyObject(this.waypoints.horizontal),e=this.Adapter.isEmptyObject(this.waypoints.vertical);t&&e&&(this.adapter.off(".waypoints"),delete o[this.key])},e.prototype.createThrottledResizeHandler=function(){function t(){e.handleResize(),e.didResize=!1}var e=this;this.adapter.on("resize.waypoints",function(){e.didResize||(e.didResize=!0,n.requestAnimationFrame(t))})},e.prototype.createThrottledScrollHandler=function(){function t(){e.handleScroll(),e.didScroll=!1}var e=this;this.adapter.on("scroll.waypoints",function(){(!e.didScroll||n.isTouch)&&(e.didScroll=!0,n.requestAnimationFrame(t))})},e.prototype.handleResize=function(){n.Context.refreshAll()},e.prototype.handleScroll=function(){var t={},e={horizontal:{newScroll:this.adapter.scrollLeft(),oldScroll:this.oldScroll.x,forward:"right",backward:"left"},vertical:{newScroll:this.adapter.scrollTop(),oldScroll:this.oldScroll.y,forward:"down",backward:"up"}};for(var i in e){var o=e[i],n=o.newScroll>o.oldScroll,r=n?o.forward:o.backward;for(var s in this.waypoints[i]){var a=this.waypoints[i][s],l=o.oldScroll