aboutsummaryrefslogtreecommitdiff
path: root/assets/js/plugins
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@gmail.com>2019-10-27 09:57:37 -0400
committerAndrew Lee <alee14498@gmail.com>2019-10-27 09:57:37 -0400
commite62e7bb6b14555c9bbe5d40d217103984f4f80e6 (patch)
tree21887d847dccacb47644eb6f74ce31326172303c /assets/js/plugins
parent716ea6ed2b64c921a799d872a07bfbd53b2a3e58 (diff)
downloadpokeworld-website-e62e7bb6b14555c9bbe5d40d217103984f4f80e6.tar.gz
pokeworld-website-e62e7bb6b14555c9bbe5d40d217103984f4f80e6.tar.bz2
pokeworld-website-e62e7bb6b14555c9bbe5d40d217103984f4f80e6.zip
Rewrite progress
Diffstat (limited to 'assets/js/plugins')
-rw-r--r--assets/js/plugins/gumshoe.js484
-rw-r--r--assets/js/plugins/jquery.ba-throttle-debounce.js252
-rw-r--r--assets/js/plugins/jquery.fitvids.js82
-rw-r--r--assets/js/plugins/jquery.greedy-navigation.js83
-rw-r--r--assets/js/plugins/jquery.magnific-popup.js1860
-rw-r--r--assets/js/plugins/smooth-scroll.js632
6 files changed, 3393 insertions, 0 deletions
diff --git a/assets/js/plugins/gumshoe.js b/assets/js/plugins/gumshoe.js
new file mode 100644
index 0000000..713b6eb
--- /dev/null
+++ b/assets/js/plugins/gumshoe.js
@@ -0,0 +1,484 @@
+/*!
+ * gumshoejs v5.1.1
+ * A simple, framework-agnostic scrollspy script.
+ * (c) 2019 Chris Ferdinandi
+ * MIT License
+ * http://github.com/cferdinandi/gumshoe
+ */
+
+(function (root, factory) {
+ if ( typeof define === 'function' && define.amd ) {
+ define([], (function () {
+ return factory(root);
+ }));
+ } else if ( typeof exports === 'object' ) {
+ module.exports = factory(root);
+ } else {
+ root.Gumshoe = factory(root);
+ }
+})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this, (function (window) {
+
+ 'use strict';
+
+ //
+ // Defaults
+ //
+
+ var defaults = {
+
+ // Active classes
+ navClass: 'active',
+ contentClass: 'active',
+
+ // Nested navigation
+ nested: false,
+ nestedClass: 'active',
+
+ // Offset & reflow
+ offset: 0,
+ reflow: false,
+
+ // Event support
+ events: true
+
+ };
+
+
+ //
+ // Methods
+ //
+
+ /**
+ * Merge two or more objects together.
+ * @param {Object} objects The objects to merge together
+ * @returns {Object} Merged values of defaults and options
+ */
+ var extend = function () {
+ var merged = {};
+ Array.prototype.forEach.call(arguments, (function (obj) {
+ for (var key in obj) {
+ if (!obj.hasOwnProperty(key)) return;
+ merged[key] = obj[key];
+ }
+ }));
+ return merged;
+ };
+
+ /**
+ * Emit a custom event
+ * @param {String} type The event type
+ * @param {Node} elem The element to attach the event to
+ * @param {Object} detail Any details to pass along with the event
+ */
+ var emitEvent = function (type, elem, detail) {
+
+ // Make sure events are enabled
+ if (!detail.settings.events) return;
+
+ // Create a new event
+ var event = new CustomEvent(type, {
+ bubbles: true,
+ cancelable: true,
+ detail: detail
+ });
+
+ // Dispatch the event
+ elem.dispatchEvent(event);
+
+ };
+
+ /**
+ * Get an element's distance from the top of the Document.
+ * @param {Node} elem The element
+ * @return {Number} Distance from the top in pixels
+ */
+ var getOffsetTop = function (elem) {
+ var location = 0;
+ if (elem.offsetParent) {
+ while (elem) {
+ location += elem.offsetTop;
+ elem = elem.offsetParent;
+ }
+ }
+ return location >= 0 ? location : 0;
+ };
+
+ /**
+ * Sort content from first to last in the DOM
+ * @param {Array} contents The content areas
+ */
+ var sortContents = function (contents) {
+ if(contents) {
+ contents.sort((function (item1, item2) {
+ var offset1 = getOffsetTop(item1.content);
+ var offset2 = getOffsetTop(item2.content);
+ if (offset1 < offset2) return -1;
+ return 1;
+ }));
+ }
+ };
+
+ /**
+ * Get the offset to use for calculating position
+ * @param {Object} settings The settings for this instantiation
+ * @return {Float} The number of pixels to offset the calculations
+ */
+ var getOffset = function (settings) {
+
+ // if the offset is a function run it
+ if (typeof settings.offset === 'function') {
+ return parseFloat(settings.offset());
+ }
+
+ // Otherwise, return it as-is
+ return parseFloat(settings.offset);
+
+ };
+
+ /**
+ * Get the document element's height
+ * @private
+ * @returns {Number}
+ */
+ var getDocumentHeight = function () {
+ return Math.max(
+ document.body.scrollHeight, document.documentElement.scrollHeight,
+ document.body.offsetHeight, document.documentElement.offsetHeight,
+ document.body.clientHeight, document.documentElement.clientHeight
+ );
+ };
+
+ /**
+ * Determine if an element is in view
+ * @param {Node} elem The element
+ * @param {Object} settings The settings for this instantiation
+ * @param {Boolean} bottom If true, check if element is above bottom of viewport instead
+ * @return {Boolean} Returns true if element is in the viewport
+ */
+ var isInView = function (elem, settings, bottom) {
+ var bounds = elem.getBoundingClientRect();
+ var offset = getOffset(settings);
+ if (bottom) {
+ return parseInt(bounds.bottom, 10) < (window.innerHeight || document.documentElement.clientHeight);
+ }
+ return parseInt(bounds.top, 10) <= offset;
+ };
+
+ /**
+ * Check if at the bottom of the viewport
+ * @return {Boolean} If true, page is at the bottom of the viewport
+ */
+ var isAtBottom = function () {
+ if (window.innerHeight + window.pageYOffset >= getDocumentHeight()) return true;
+ return false;
+ };
+
+ /**
+ * Check if the last item should be used (even if not at the top of the page)
+ * @param {Object} item The last item
+ * @param {Object} settings The settings for this instantiation
+ * @return {Boolean} If true, use the last item
+ */
+ var useLastItem = function (item, settings) {
+ if (isAtBottom() && isInView(item.content, settings, true)) return true;
+ return false;
+ };
+
+ /**
+ * Get the active content
+ * @param {Array} contents The content areas
+ * @param {Object} settings The settings for this instantiation
+ * @return {Object} The content area and matching navigation link
+ */
+ var getActive = function (contents, settings) {
+ var last = contents[contents.length-1];
+ if (useLastItem(last, settings)) return last;
+ for (var i = contents.length - 1; i >= 0; i--) {
+ if (isInView(contents[i].content, settings)) return contents[i];
+ }
+ };
+
+ /**
+ * Deactivate parent navs in a nested navigation
+ * @param {Node} nav The starting navigation element
+ * @param {Object} settings The settings for this instantiation
+ */
+ var deactivateNested = function (nav, settings) {
+
+ // If nesting isn't activated, bail
+ if (!settings.nested) return;
+
+ // Get the parent navigation
+ var li = nav.parentNode.closest('li');
+ if (!li) return;
+
+ // Remove the active class
+ li.classList.remove(settings.nestedClass);
+
+ // Apply recursively to any parent navigation elements
+ deactivateNested(li, settings);
+
+ };
+
+ /**
+ * Deactivate a nav and content area
+ * @param {Object} items The nav item and content to deactivate
+ * @param {Object} settings The settings for this instantiation
+ */
+ var deactivate = function (items, settings) {
+
+ // Make sure their are items to deactivate
+ if (!items) return;
+
+ // Get the parent list item
+ var li = items.nav.closest('li');
+ if (!li) return;
+
+ // Remove the active class from the nav and content
+ li.classList.remove(settings.navClass);
+ items.content.classList.remove(settings.contentClass);
+
+ // Deactivate any parent navs in a nested navigation
+ deactivateNested(li, settings);
+
+ // Emit a custom event
+ emitEvent('gumshoeDeactivate', li, {
+ link: items.nav,
+ content: items.content,
+ settings: settings
+ });
+
+ };
+
+
+ /**
+ * Activate parent navs in a nested navigation
+ * @param {Node} nav The starting navigation element
+ * @param {Object} settings The settings for this instantiation
+ */
+ var activateNested = function (nav, settings) {
+
+ // If nesting isn't activated, bail
+ if (!settings.nested) return;
+
+ // Get the parent navigation
+ var li = nav.parentNode.closest('li');
+ if (!li) return;
+
+ // Add the active class
+ li.classList.add(settings.nestedClass);
+
+ // Apply recursively to any parent navigation elements
+ activateNested(li, settings);
+
+ };
+
+ /**
+ * Activate a nav and content area
+ * @param {Object} items The nav item and content to activate
+ * @param {Object} settings The settings for this instantiation
+ */
+ var activate = function (items, settings) {
+
+ // Make sure their are items to activate
+ if (!items) return;
+
+ // Get the parent list item
+ var li = items.nav.closest('li');
+ if (!li) return;
+
+ // Add the active class to the nav and content
+ li.classList.add(settings.navClass);
+ items.content.classList.add(settings.contentClass);
+
+ // Activate any parent navs in a nested navigation
+ activateNested(li, settings);
+
+ // Emit a custom event
+ emitEvent('gumshoeActivate', li, {
+ link: items.nav,
+ content: items.content,
+ settings: settings
+ });
+
+ };
+
+ /**
+ * Create the Constructor object
+ * @param {String} selector The selector to use for navigation items
+ * @param {Object} options User options and settings
+ */
+ var Constructor = function (selector, options) {
+
+ //
+ // Variables
+ //
+
+ var publicAPIs = {};
+ var navItems, contents, current, timeout, settings;
+
+
+ //
+ // Methods
+ //
+
+ /**
+ * Set variables from DOM elements
+ */
+ publicAPIs.setup = function () {
+
+ // Get all nav items
+ navItems = document.querySelectorAll(selector);
+
+ // Create contents array
+ contents = [];
+
+ // Loop through each item, get it's matching content, and push to the array
+ Array.prototype.forEach.call(navItems, (function (item) {
+
+ // Get the content for the nav item
+ var content = document.getElementById(decodeURIComponent(item.hash.substr(1)));
+ if (!content) return;
+
+ // Push to the contents array
+ contents.push({
+ nav: item,
+ content: content
+ });
+
+ }));
+
+ // Sort contents by the order they appear in the DOM
+ sortContents(contents);
+
+ };
+
+ /**
+ * Detect which content is currently active
+ */
+ publicAPIs.detect = function () {
+
+ // Get the active content
+ var active = getActive(contents, settings);
+
+ // if there's no active content, deactivate and bail
+ if (!active) {
+ if (current) {
+ deactivate(current, settings);
+ current = null;
+ }
+ return;
+ }
+
+ // If the active content is the one currently active, do nothing
+ if (current && active.content === current.content) return;
+
+ // Deactivate the current content and activate the new content
+ deactivate(current, settings);
+ activate(active, settings);
+
+ // Update the currently active content
+ current = active;
+
+ };
+
+ /**
+ * Detect the active content on scroll
+ * Debounced for performance
+ */
+ var scrollHandler = function (event) {
+
+ // If there's a timer, cancel it
+ if (timeout) {
+ window.cancelAnimationFrame(timeout);
+ }
+
+ // Setup debounce callback
+ timeout = window.requestAnimationFrame(publicAPIs.detect);
+
+ };
+
+ /**
+ * Update content sorting on resize
+ * Debounced for performance
+ */
+ var resizeHandler = function (event) {
+
+ // If there's a timer, cancel it
+ if (timeout) {
+ window.cancelAnimationFrame(timeout);
+ }
+
+ // Setup debounce callback
+ timeout = window.requestAnimationFrame((function () {
+ sortContents(contents);
+ publicAPIs.detect();
+ }));
+
+ };
+
+ /**
+ * Destroy the current instantiation
+ */
+ publicAPIs.destroy = function () {
+
+ // Undo DOM changes
+ if (current) {
+ deactivate(current, settings);
+ }
+
+ // Remove event listeners
+ window.removeEventListener('scroll', scrollHandler, false);
+ if (settings.reflow) {
+ window.removeEventListener('resize', resizeHandler, false);
+ }
+
+ // Reset variables
+ contents = null;
+ navItems = null;
+ current = null;
+ timeout = null;
+ settings = null;
+
+ };
+
+ /**
+ * Initialize the current instantiation
+ */
+ var init = function () {
+
+ // Merge user options into defaults
+ settings = extend(defaults, options || {});
+
+ // Setup variables based on the current DOM
+ publicAPIs.setup();
+
+ // Find the currently active content
+ publicAPIs.detect();
+
+ // Setup event listeners
+ window.addEventListener('scroll', scrollHandler, false);
+ if (settings.reflow) {
+ window.addEventListener('resize', resizeHandler, false);
+ }
+
+ };
+
+
+ //
+ // Initialize and return the public APIs
+ //
+
+ init();
+ return publicAPIs;
+
+ };
+
+
+ //
+ // Return the Constructor
+ //
+
+ return Constructor;
+
+})); \ No newline at end of file
diff --git a/assets/js/plugins/jquery.ba-throttle-debounce.js b/assets/js/plugins/jquery.ba-throttle-debounce.js
new file mode 100644
index 0000000..fa30bdf
--- /dev/null
+++ b/assets/js/plugins/jquery.ba-throttle-debounce.js
@@ -0,0 +1,252 @@
+/*!
+ * jQuery throttle / debounce - v1.1 - 3/7/2010
+ * http://benalman.com/projects/jquery-throttle-debounce-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+
+// Script: jQuery throttle / debounce: Sometimes, less is more!
+//
+// *Version: 1.1, Last updated: 3/7/2010*
+//
+// Project Home - http://benalman.com/projects/jquery-throttle-debounce-plugin/
+// GitHub - http://github.com/cowboy/jquery-throttle-debounce/
+// Source - http://github.com/cowboy/jquery-throttle-debounce/raw/master/jquery.ba-throttle-debounce.js
+// (Minified) - http://github.com/cowboy/jquery-throttle-debounce/raw/master/jquery.ba-throttle-debounce.min.js (0.7kb)
+//
+// About: License
+//
+// Copyright (c) 2010 "Cowboy" Ben Alman,
+// Dual licensed under the MIT and GPL licenses.
+// http://benalman.com/about/license/
+//
+// About: Examples
+//
+// These working examples, complete with fully commented code, illustrate a few
+// ways in which this plugin can be used.
+//
+// Throttle - http://benalman.com/code/projects/jquery-throttle-debounce/examples/throttle/
+// Debounce - http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/
+//
+// About: Support and Testing
+//
+// Information about what version or versions of jQuery this plugin has been
+// tested with, what browsers it has been tested in, and where the unit tests
+// reside (so you can test it yourself).
+//
+// jQuery Versions - none, 1.3.2, 1.4.2
+// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome 4-5, Opera 9.6-10.1.
+// Unit Tests - http://benalman.com/code/projects/jquery-throttle-debounce/unit/
+//
+// About: Release History
+//
+// 1.1 - (3/7/2010) Fixed a bug in <jQuery.throttle> where trailing callbacks
+// executed later than they should. Reworked a fair amount of internal
+// logic as well.
+// 1.0 - (3/6/2010) Initial release as a stand-alone project. Migrated over
+// from jquery-misc repo v0.4 to jquery-throttle repo v1.0, added the
+// no_trailing throttle parameter and debounce functionality.
+//
+// Topic: Note for non-jQuery users
+//
+// jQuery isn't actually required for this plugin, because nothing internal
+// uses any jQuery methods or properties. jQuery is just used as a namespace
+// under which these methods can exist.
+//
+// Since jQuery isn't actually required for this plugin, if jQuery doesn't exist
+// when this plugin is loaded, the method described below will be created in
+// the `Cowboy` namespace. Usage will be exactly the same, but instead of
+// $.method() or jQuery.method(), you'll need to use Cowboy.method().
+
+(function(window,undefined){
+ '$:nomunge'; // Used by YUI compressor.
+
+ // Since jQuery really isn't required for this plugin, use `jQuery` as the
+ // namespace only if it already exists, otherwise use the `Cowboy` namespace,
+ // creating it if necessary.
+ var $ = window.jQuery || window.Cowboy || ( window.Cowboy = {} ),
+
+ // Internal method reference.
+ jq_throttle;
+
+ // Method: jQuery.throttle
+ //
+ // Throttle execution of a function. Especially useful for rate limiting
+ // execution of handlers on events like resize and scroll. If you want to
+ // rate-limit execution of a function to a single time, see the
+ // <jQuery.debounce> method.
+ //
+ // In this visualization, | is a throttled-function call and X is the actual
+ // callback execution:
+ //
+ // > Throttled with `no_trailing` specified as false or unspecified:
+ // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
+ // > X X X X X X X X X X X X
+ // >
+ // > Throttled with `no_trailing` specified as true:
+ // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
+ // > X X X X X X X X X X
+ //
+ // Usage:
+ //
+ // > var throttled = jQuery.throttle( delay, [ no_trailing, ] callback );
+ // >
+ // > jQuery('selector').bind( 'someevent', throttled );
+ // > jQuery('selector').unbind( 'someevent', throttled );
+ //
+ // This also works in jQuery 1.4+:
+ //
+ // > jQuery('selector').bind( 'someevent', jQuery.throttle( delay, [ no_trailing, ] callback ) );
+ // > jQuery('selector').unbind( 'someevent', callback );
+ //
+ // Arguments:
+ //
+ // delay - (Number) A zero-or-greater delay in milliseconds. For event
+ // callbacks, values around 100 or 250 (or even higher) are most useful.
+ // no_trailing - (Boolean) Optional, defaults to false. If no_trailing is
+ // true, callback will only execute every `delay` milliseconds while the
+ // throttled-function is being called. If no_trailing is false or
+ // unspecified, callback will be executed one final time after the last
+ // throttled-function call. (After the throttled-function has not been
+ // called for `delay` milliseconds, the internal counter is reset)
+ // callback - (Function) A function to be executed after delay milliseconds.
+ // The `this` context and all arguments are passed through, as-is, to
+ // `callback` when the throttled-function is executed.
+ //
+ // Returns:
+ //
+ // (Function) A new, throttled, function.
+
+ $.throttle = jq_throttle = function( delay, no_trailing, callback, debounce_mode ) {
+ // After wrapper has stopped being called, this timeout ensures that
+ // `callback` is executed at the proper times in `throttle` and `end`
+ // debounce modes.
+ var timeout_id,
+
+ // Keep track of the last time `callback` was executed.
+ last_exec = 0;
+
+ // `no_trailing` defaults to falsy.
+ if ( typeof no_trailing !== 'boolean' ) {
+ debounce_mode = callback;
+ callback = no_trailing;
+ no_trailing = undefined;
+ }
+
+ // The `wrapper` function encapsulates all of the throttling / debouncing
+ // functionality and when executed will limit the rate at which `callback`
+ // is executed.
+ function wrapper() {
+ var that = this,
+ elapsed = +new Date() - last_exec,
+ args = arguments;
+
+ // Execute `callback` and update the `last_exec` timestamp.
+ function exec() {
+ last_exec = +new Date();
+ callback.apply( that, args );
+ };
+
+ // If `debounce_mode` is true (at_begin) this is used to clear the flag
+ // to allow future `callback` executions.
+ function clear() {
+ timeout_id = undefined;
+ };
+
+ if ( debounce_mode && !timeout_id ) {
+ // Since `wrapper` is being called for the first time and
+ // `debounce_mode` is true (at_begin), execute `callback`.
+ exec();
+ }
+
+ // Clear any existing timeout.
+ timeout_id && clearTimeout( timeout_id );
+
+ if ( debounce_mode === undefined && elapsed > delay ) {
+ // In throttle mode, if `delay` time has been exceeded, execute
+ // `callback`.
+ exec();
+
+ } else if ( no_trailing !== true ) {
+ // In trailing throttle mode, since `delay` time has not been
+ // exceeded, schedule `callback` to execute `delay` ms after most
+ // recent execution.
+ //
+ // If `debounce_mode` is true (at_begin), schedule `clear` to execute
+ // after `delay` ms.
+ //
+ // If `debounce_mode` is false (at end), schedule `callback` to
+ // execute after `delay` ms.
+ timeout_id = setTimeout( debounce_mode ? clear : exec, debounce_mode === undefined ? delay - elapsed : delay );
+ }
+ };
+
+ // Set the guid of `wrapper` function to the same of original callback, so
+ // it can be removed in jQuery 1.4+ .unbind or .die by using the original
+ // callback as a reference.
+ if ( $.guid ) {
+ wrapper.guid = callback.guid = callback.guid || $.guid++;
+ }
+
+ // Return the wrapper function.
+ return wrapper;
+ };
+
+ // Method: jQuery.debounce
+ //
+ // Debounce execution of a function. Debouncing, unlike throttling,
+ // guarantees that a function is only executed a single time, either at the
+ // very beginning of a series of calls, or at the very end. If you want to
+ // simply rate-limit execution of a function, see the <jQuery.throttle>
+ // method.
+ //
+ // In this visualization, | is a debounced-function call and X is the actual
+ // callback execution:
+ //
+ // > Debounced with `at_begin` specified as false or unspecified:
+ // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
+ // > X X
+ // >
+ // > Debounced with `at_begin` specified as true:
+ // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
+ // > X X
+ //
+ // Usage:
+ //
+ // > var debounced = jQuery.debounce( delay, [ at_begin, ] callback );
+ // >
+ // > jQuery('selector').bind( 'someevent', debounced );
+ // > jQuery('selector').unbind( 'someevent', debounced );
+ //
+ // This also works in jQuery 1.4+:
+ //
+ // > jQuery('selector').bind( 'someevent', jQuery.debounce( delay, [ at_begin, ] callback ) );
+ // > jQuery('selector').unbind( 'someevent', callback );
+ //
+ // Arguments:
+ //
+ // delay - (Number) A zero-or-greater delay in milliseconds. For event
+ // callbacks, values around 100 or 250 (or even higher) are most useful.
+ // at_begin - (Boolean) Optional, defaults to false. If at_begin is false or
+ // unspecified, callback will only be executed `delay` milliseconds after
+ // the last debounced-function call. If at_begin is true, callback will be
+ // executed only at the first debounced-function call. (After the
+ // throttled-function has not been called for `delay` milliseconds, the
+ // internal counter is reset)
+ // callback - (Function) A function to be executed after delay milliseconds.
+ // The `this` context and all arguments are passed through, as-is, to
+ // `callback` when the debounced-function is executed.
+ //
+ // Returns:
+ //
+ // (Function) A new, debounced, function.
+
+ $.debounce = function( delay, at_begin, callback ) {
+ return callback === undefined
+ ? jq_throttle( delay, at_begin, false )
+ : jq_throttle( delay, callback, at_begin !== false );
+ };
+
+})(this);
diff --git a/assets/js/plugins/jquery.fitvids.js b/assets/js/plugins/jquery.fitvids.js
new file mode 100644
index 0000000..5c2f85c
--- /dev/null
+++ b/assets/js/plugins/jquery.fitvids.js
@@ -0,0 +1,82 @@
+/*jshint browser:true */
+/*!
+* FitVids 1.1
+*
+* Copyright 2013, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com
+* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/
+* Released under the WTFPL license - http://sam.zoy.org/wtfpl/
+*
+*/
+
+;(function( $ ){
+
+ 'use strict';
+
+ $.fn.fitVids = function( options ) {
+ var settings = {
+ customSelector: null,
+ ignore: null
+ };
+
+ if(!document.getElementById('fit-vids-style')) {
+ // appendStyles: https://github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.js
+ var head = document.head || document.getElementsByTagName('head')[0];
+ var css = '.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}';
+ var div = document.createElement("div");
+ div.innerHTML = '<p>x</p><style id="fit-vids-style">' + css + '</style>';
+ head.appendChild(div.childNodes[1]);
+ }
+
+ if ( options ) {
+ $.extend( settings, options );
+ }
+
+ return this.each(function(){
+ var selectors = [
+ 'iframe[src*="player.vimeo.com"]',
+ 'iframe[src*="youtube.com"]',
+ 'iframe[src*="youtube-nocookie.com"]',
+ 'iframe[src*="kickstarter.com"][src*="video.html"]',
+ 'object',
+ 'embed'
+ ];
+
+ if (settings.customSelector) {
+ selectors.push(settings.customSelector);
+ }
+
+ var ignoreList = '.fitvidsignore';
+
+ if(settings.ignore) {
+ ignoreList = ignoreList + ', ' + settings.ignore;
+ }
+
+ var $allVideos = $(this).find(selectors.join(','));
+ $allVideos = $allVideos.not('object object'); // SwfObj conflict patch
+ $allVideos = $allVideos.not(ignoreList); // Disable FitVids on this video.
+
+ $allVideos.each(function(count){
+ var $this = $(this);
+ if($this.parents(ignoreList).length > 0) {
+ return; // Disable FitVids on this video.
+ }
+ if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; }
+ if ((!$this.css('height') && !$this.css('width')) && (isNaN($this.attr('height')) || isNaN($this.attr('width'))))
+ {
+ $this.attr('height', 9);
+ $this.attr('width', 16);
+ }
+ var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(),
+ width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(),
+ aspectRatio = height / width;
+ if(!$this.attr('id')){
+ var videoID = 'fitvid' + count;
+ $this.attr('id', videoID);
+ }
+ $this.wrap('<div class="fluid-width-video-wrapper"></div>').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+'%');
+ $this.removeAttr('height').removeAttr('width');
+ });
+ });
+ };
+// Works with either jQuery or Zepto
+})( window.jQuery || window.Zepto ); \ No newline at end of file
diff --git a/assets/js/plugins/jquery.greedy-navigation.js b/assets/js/plugins/jquery.greedy-navigation.js
new file mode 100644
index 0000000..3eabccd
--- /dev/null
+++ b/assets/js/plugins/jquery.greedy-navigation.js
@@ -0,0 +1,83 @@
+/*
+GreedyNav.js - https://github.com/lukejacksonn/GreedyNav
+Licensed under the MIT license - http://opensource.org/licenses/MIT
+Copyright (c) 2015 Luke Jackson
+*/
+
+$(document).ready(function() {
+ var $btn = $("nav.greedy-nav .greedy-nav__toggle");
+ var $vlinks = $("nav.greedy-nav .visible-links");
+ var $hlinks = $("nav.greedy-nav .hidden-links");
+
+ var numOfItems = 0;
+ var totalSpace = 0;
+ var closingTime = 1000;
+ var breakWidths = [];
+
+ // Get initial state
+ $vlinks.children().outerWidth(function(i, w) {
+ totalSpace += w;
+ numOfItems += 1;
+ breakWidths.push(totalSpace);
+ });
+
+ var availableSpace, numOfVisibleItems, requiredSpace, timer;
+
+ function check() {
+ // Get instant state
+ availableSpace = $vlinks.width() - $btn.width();
+ numOfVisibleItems = $vlinks.children().length;
+ requiredSpace = breakWidths[numOfVisibleItems - 1];
+
+ // There is not enough space
+ if (requiredSpace > availableSpace) {
+ $vlinks
+ .children()
+ .last()
+ .prependTo($hlinks);
+ numOfVisibleItems -= 1;
+ check();
+ // There is more than enough space
+ } else if (availableSpace > breakWidths[numOfVisibleItems]) {
+ $hlinks
+ .children()
+ .first()
+ .appendTo($vlinks);
+ numOfVisibleItems += 1;
+ check();
+ }
+ // Update the button accordingly
+ $btn.attr("count", numOfItems - numOfVisibleItems);
+ if (numOfVisibleItems === numOfItems) {
+ $btn.addClass("hidden");
+ } else {
+ $btn.removeClass("hidden");
+ }
+ }
+
+ // Window listeners
+ $(window).resize(function() {
+ check();
+ });
+
+ $btn.on("click", function() {
+ $hlinks.toggleClass("hidden");
+ $(this).toggleClass("close");
+ clearTimeout(timer);
+ });
+
+ $hlinks
+ .on("mouseleave", function() {
+ // Mouse has left, start the timer
+ timer = setTimeout(function() {
+ $hlinks.addClass("hidden");
+ $btn.toggleClass("close");
+ }, closingTime);
+ })
+ .on("mouseenter", function() {
+ // Mouse is back, cancel the timer
+ clearTimeout(timer);
+ });
+
+ check();
+});
diff --git a/assets/js/plugins/jquery.magnific-popup.js b/assets/js/plugins/jquery.magnific-popup.js
new file mode 100644
index 0000000..7d1d197
--- /dev/null
+++ b/assets/js/plugins/jquery.magnific-popup.js
@@ -0,0 +1,1860 @@
+/*! Magnific Popup - v1.1.0 - 2016-02-20
+* http://dimsemenov.com/plugins/magnific-popup/
+* Copyright (c) 2016 Dmitry Semenov; */
+;(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof exports === 'object') {
+ // Node/CommonJS
+ factory(require('jquery'));
+ } else {
+ // Browser globals
+ factory(window.jQuery || window.Zepto);
+ }
+ }(function($) {
+
+ /*>>core*/
+ /**
+ *
+ * Magnific Popup Core JS file
+ *
+ */
+
+
+ /**
+ * Private static constants
+ */
+ var CLOSE_EVENT = 'Close',
+ BEFORE_CLOSE_EVENT = 'BeforeClose',
+ AFTER_CLOSE_EVENT = 'AfterClose',
+ BEFORE_APPEND_EVENT = 'BeforeAppend',
+ MARKUP_PARSE_EVENT = 'MarkupParse',
+ OPEN_EVENT = 'Open',
+ CHANGE_EVENT = 'Change',
+ NS = 'mfp',
+ EVENT_NS = '.' + NS,
+ READY_CLASS = 'mfp-ready',
+ REMOVING_CLASS = 'mfp-removing',
+ PREVENT_CLOSE_CLASS = 'mfp-prevent-close';
+
+
+ /**
+ * Private vars
+ */
+ /*jshint -W079 */
+ var mfp, // As we have only one instance of MagnificPopup object, we define it locally to not to use 'this'
+ MagnificPopup = function(){},
+ _isJQ = !!(window.jQuery),
+ _prevStatus,
+ _window = $(window),
+ _document,
+ _prevContentType,
+ _wrapClasses,
+ _currPopupType;
+
+
+ /**
+ * Private functions
+ */
+ var _mfpOn = function(name, f) {
+ mfp.ev.on(NS + name + EVENT_NS, f);
+ },
+ _getEl = function(className, appendTo, html, raw) {
+ var el = document.createElement('div');
+ el.className = 'mfp-'+className;
+ if(html) {
+ el.innerHTML = html;
+ }
+ if(!raw) {
+ el = $(el);
+ if(appendTo) {
+ el.appendTo(appendTo);
+ }
+ } else if(appendTo) {
+ appendTo.appendChild(el);
+ }
+ return el;
+ },
+ _mfpTrigger = function(e, data) {
+ mfp.ev.triggerHandler(NS + e, data);
+
+ if(mfp.st.callbacks) {
+ // converts "mfpEventName" to "eventName" callback and triggers it if it's present
+ e = e.charAt(0).toLowerCase() + e.slice(1);
+ if(mfp.st.callbacks[e]) {
+ mfp.st.callbacks[e].apply(mfp, $.isArray(data) ? data : [data]);
+ }
+ }
+ },
+ _getCloseBtn = function(type) {
+ if(type !== _currPopupType || !mfp.currTemplate.closeBtn) {
+ mfp.currTemplate.closeBtn = $( mfp.st.closeMarkup.replace('%title%', mfp.st.tClose ) );
+ _currPopupType = type;
+ }
+ return mfp.currTemplate.closeBtn;
+ },
+ // Initialize Magnific Popup only when called at least once
+ _checkInstance = function() {
+ if(!$.magnificPopup.instance) {
+ /*jshint -W020 */
+ mfp = new MagnificPopup();
+ mfp.init();
+ $.magnificPopup.instance = mfp;
+ }
+ },
+ // CSS transition detection, http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
+ supportsTransitions = function() {
+ var s = document.createElement('p').style, // 's' for style. better to create an element if body yet to exist
+ v = ['ms','O','Moz','Webkit']; // 'v' for vendor
+
+ if( s['transition'] !== undefined ) {
+ return true;
+ }
+
+ while( v.length ) {
+ if( v.pop() + 'Transition' in s ) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+
+
+ /**
+ * Public functions
+ */
+ MagnificPopup.prototype = {
+
+ constructor: MagnificPopup,
+
+ /**
+ * Initializes Magnific Popup plugin.
+ * This function is triggered only once when $.fn.magnificPopup or $.magnificPopup is executed
+ */
+ init: function() {
+ var appVersion = navigator.appVersion;
+ mfp.isLowIE = mfp.isIE8 = document.all && !document.addEventListener;
+ mfp.isAndroid = (/android/gi).test(appVersion);
+ mfp.isIOS = (/iphone|ipad|ipod/gi).test(appVersion);
+ mfp.supportsTransition = supportsTransitions();
+
+ // We disable fixed positioned lightbox on devices that don't handle it nicely.
+ // If you know a better way of detecting this - let me know.
+ mfp.probablyMobile = (mfp.isAndroid || mfp.isIOS || /(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent) );
+ _document = $(document);
+
+ mfp.popupsCache = {};
+ },
+
+ /**
+ * Opens popup
+ * @param data [description]
+ */
+ open: function(data) {
+
+ var i;
+
+ if(data.isObj === false) {
+ // convert jQuery collection to array to avoid conflicts later
+ mfp.items = data.items.toArray();
+
+ mfp.index = 0;
+ var items = data.items,
+ item;
+ for(i = 0; i < items.length; i++) {
+ item = items[i];
+ if(item.parsed) {
+ item = item.el[0];
+ }
+ if(item === data.el[0]) {
+ mfp.index = i;
+ break;
+ }
+ }
+ } else {
+ mfp.items = $.isArray(data.items) ? data.items : [data.items];
+ mfp.index = data.index || 0;
+ }
+
+ // if popup is already opened - we just update the content
+ if(mfp.isOpen) {
+ mfp.updateItemHTML();
+ return;
+ }
+
+ mfp.types = [];
+ _wrapClasses = '';
+ if(data.mainEl && data.mainEl.length) {
+ mfp.ev = data.mainEl.eq(0);
+ } else {
+ mfp.ev = _document;
+ }
+
+ if(data.key) {
+ if(!mfp.popupsCache[data.key]) {
+ mfp.popupsCache[data.key] = {};
+ }
+ mfp.currTemplate = mfp.popupsCache[data.key];
+ } else {
+ mfp.currTemplate = {};
+ }
+
+
+
+ mfp.st = $.extend(true, {}, $.magnificPopup.defaults, data );
+ mfp.fixedContentPos = mfp.st.fixedContentPos === 'auto' ? !mfp.probablyMobile : mfp.st.fixedContentPos;
+
+ if(mfp.st.modal) {
+ mfp.st.closeOnContentClick = false;
+ mfp.st.closeOnBgClick = false;
+ mfp.st.showCloseBtn = false;
+ mfp.st.enableEscapeKey = false;
+ }
+
+
+ // Building markup
+ // main containers are created only once
+ if(!mfp.bgOverlay) {
+
+ // Dark overlay
+ mfp.bgOverlay = _getEl('bg').on('click'+EVENT_NS, function() {
+ mfp.close();
+ });
+
+ mfp.wrap = _getEl('wrap').attr('tabindex', -1).on('click'+EVENT_NS, function(e) {
+ if(mfp._checkIfClose(e.target)) {
+ mfp.close();
+ }
+ });
+
+ mfp.container = _getEl('container', mfp.wrap);
+ }
+
+ mfp.contentContainer = _getEl('content');
+ if(mfp.st.preloader) {
+ mfp.preloader = _getEl('preloader', mfp.container, mfp.st.tLoading);
+ }
+
+
+ // Initializing modules
+ var modules = $.magnificPopup.modules;
+ for(i = 0; i < modules.length; i++) {
+ var n = modules[i];
+ n = n.charAt(0).toUpperCase() + n.slice(1);
+ mfp['init'+n].call(mfp);
+ }
+ _mfpTrigger('BeforeOpen');
+
+
+ if(mfp.st.showCloseBtn) {
+ // Close button
+ if(!mfp.st.closeBtnInside) {
+ mfp.wrap.append( _getCloseBtn() );
+ } else {
+ _mfpOn(MARKUP_PARSE_EVENT, function(e, template, values, item) {
+ values.close_replaceWith = _getCloseBtn(item.type);
+ });
+ _wrapClasses += ' mfp-close-btn-in';
+ }
+ }
+
+ if(mfp.st.alignTop) {
+ _wrapClasses += ' mfp-align-top';
+ }
+
+
+
+ if(mfp.fixedContentPos) {
+ mfp.wrap.css({
+ overflow: mfp.st.overflowY,
+ overflowX: 'hidden',
+ overflowY: mfp.st.overflowY
+ });
+ } else {
+ mfp.wrap.css({
+ top: _window.scrollTop(),
+ position: 'absolute'
+ });
+ }
+ if( mfp.st.fixedBgPos === false || (mfp.st.fixedBgPos === 'auto' && !mfp.fixedContentPos) ) {
+ mfp.bgOverlay.css({
+ height: _document.height(),
+ position: 'absolute'
+ });
+ }
+
+
+
+ if(mfp.st.enableEscapeKey) {
+ // Close on ESC key
+ _document.on('keyup' + EVENT_NS, function(e) {
+ if(e.keyCode === 27) {
+ mfp.close();
+ }
+ });
+ }
+
+ _window.on('resize' + EVENT_NS, function() {
+ mfp.updateSize();
+ });
+
+
+ if(!mfp.st.closeOnContentClick) {
+ _wrapClasses += ' mfp-auto-cursor';
+ }
+
+ if(_wrapClasses)
+ mfp.wrap.addClass(_wrapClasses);
+
+
+ // this triggers recalculation of layout, so we get it once to not to trigger twice
+ var windowHeight = mfp.wH = _window.height();
+
+
+ var windowStyles = {};
+
+ if( mfp.fixedContentPos ) {
+ if(mfp._hasScrollBar(windowHeight)){
+ var s = mfp._getScrollbarSize();
+ if(s) {
+ windowStyles.marginRight = s;
+ }
+ }
+ }
+
+ if(mfp.fixedContentPos) {
+ if(!mfp.isIE7) {
+ windowStyles.overflow = 'hidden';
+ } else {
+ // ie7 double-scroll bug
+ $('body, html').css('overflow', 'hidden');
+ }
+ }
+
+
+
+ var classesToadd = mfp.st.mainClass;
+ if(mfp.isIE7) {
+ classesToadd += ' mfp-ie7';
+ }
+ if(classesToadd) {
+ mfp._addClassToMFP( classesToadd );
+ }
+
+ // add content
+ mfp.updateItemHTML();
+
+ _mfpTrigger('BuildControls');
+
+ // remove scrollbar, add margin e.t.c
+ $('html').css(windowStyles);
+
+ // add everything to DOM
+ mfp.bgOverlay.add(mfp.wrap).prependTo( mfp.st.prependTo || $(document.body) );
+
+ // Save last focused element
+ mfp._lastFocusedEl = document.activeElement;
+
+ // Wait for next cycle to allow CSS transition
+ setTimeout(function() {
+
+ if(mfp.content) {
+ mfp._addClassToMFP(READY_CLASS);
+ mfp._setFocus();
+ } else {
+ // if content is not defined (not loaded e.t.c) we add class only for BG
+ mfp.bgOverlay.addClass(READY_CLASS);
+ }
+
+ // Trap the focus in popup
+ _document.on('focusin' + EVENT_NS, mfp._onFocusIn);
+
+ }, 16);
+
+ mfp.isOpen = true;
+ mfp.updateSize(windowHeight);
+ _mfpTrigger(OPEN_EVENT);
+
+ return data;
+ },
+
+ /**
+ * Closes the popup
+ */
+ close: function() {
+ if(!mfp.isOpen) return;
+ _mfpTrigger(BEFORE_CLOSE_EVENT);
+
+ mfp.isOpen = false;
+ // for CSS3 animation
+ if(mfp.st.removalDelay && !mfp.isLowIE && mfp.supportsTransition ) {
+ mfp._addClassToMFP(REMOVING_CLASS);
+ setTimeout(function() {
+ mfp._close();
+ }, mfp.st.removalDelay);
+ } else {
+ mfp._close();
+ }
+ },
+
+ /**
+ * Helper for close() function
+ */
+ _close: function() {
+ _mfpTrigger(CLOSE_EVENT);
+
+ var classesToRemove = REMOVING_CLASS + ' ' + READY_CLASS + ' ';
+
+ mfp.bgOverlay.detach();
+ mfp.wrap.detach();
+ mfp.container.empty();
+
+ if(mfp.st.mainClass) {
+ classesToRemove += mfp.st.mainClass + ' ';
+ }
+
+ mfp._removeClassFromMFP(classesToRemove);
+
+ if(mfp.fixedContentPos) {
+ var windowStyles = {marginRight: ''};
+ if(mfp.isIE7) {
+ $('body, html').css('overflow', '');
+ } else {
+ windowStyles.overflow = '';
+ }
+ $('html').css(windowStyles);
+ }
+
+ _document.off('keyup' + EVENT_NS + ' focusin' + EVENT_NS);
+ mfp.ev.off(EVENT_NS);
+
+ // clean up DOM elements that aren't removed
+ mfp.wrap.attr('class', 'mfp-wrap').removeAttr('style');
+ mfp.bgOverlay.attr('class', 'mfp-bg');
+ mfp.container.attr('class', 'mfp-container');
+
+ // remove close button from target element
+ if(mfp.st.showCloseBtn &&
+ (!mfp.st.closeBtnInside || mfp.currTemplate[mfp.currItem.type] === true)) {
+ if(mfp.currTemplate.closeBtn)
+ mfp.currTemplate.closeBtn.detach();
+ }
+
+
+ if(mfp.st.autoFocusLast && mfp._lastFocusedEl) {
+ $(mfp._lastFocusedEl).focus(); // put tab focus back
+ }
+ mfp.currItem = null;
+ mfp.content = null;
+ mfp.currTemplate = null;
+ mfp.prevHeight = 0;
+
+ _mfpTrigger(AFTER_CLOSE_EVENT);
+ },
+
+ updateSize: function(winHeight) {
+
+ if(mfp.isIOS) {
+ // fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2
+ var zoomLevel = document.documentElement.clientWidth / window.innerWidth;
+ var height = window.innerHeight * zoomLevel;
+ mfp.wrap.css('height', height);
+ mfp.wH = height;
+ } else {
+ mfp.wH = winHeight || _window.height();
+ }
+ // Fixes #84: popup incorrectly positioned with position:relative on body
+ if(!mfp.fixedContentPos) {
+ mfp.wrap.css('height', mfp.wH);
+ }
+
+ _mfpTrigger('Resize');
+
+ },
+
+ /**
+ * Set content of popup based on current index
+ */
+ updateItemHTML: function() {
+ var item = mfp.items[mfp.index];
+
+ // Detach and perform modifications
+ mfp.contentContainer.detach();
+
+ if(mfp.content)
+ mfp.content.detach();
+
+ if(!item.parsed) {
+ item = mfp.parseEl( mfp.index );
+ }
+
+ var type = item.type;
+
+ _mfpTrigger('BeforeChange', [mfp.currItem ? mfp.currItem.type : '', type]);
+ // BeforeChange event works like so:
+ // _mfpOn('BeforeChange', function(e, prevType, newType) { });
+
+ mfp.currItem = item;
+
+ if(!mfp.currTemplate[type]) {
+ var markup = mfp.st[type] ? mfp.st[type].markup : false;
+
+ // allows to modify markup
+ _mfpTrigger('FirstMarkupParse', markup);
+
+ if(markup) {
+ mfp.currTemplate[type] = $(markup);
+ } else {
+ // if there is no markup found we just define that template is parsed
+ mfp.currTemplate[type] = true;
+ }
+ }
+
+ if(_prevContentType && _prevContentType !== item.type) {
+ mfp.container.removeClass('mfp-'+_prevContentType+'-holder');
+ }
+
+ var newContent = mfp['get' + type.charAt(0).toUpperCase() + type.slice(1)](item, mfp.currTemplate[type]);
+ mfp.appendContent(newContent, type);
+
+ item.preloaded = true;
+
+ _mfpTrigger(CHANGE_EVENT, item);
+ _prevContentType = item.type;
+
+ // Append container back after its content changed
+ mfp.container.prepend(mfp.contentContainer);
+
+ _mfpTrigger('AfterChange');
+ },
+
+
+ /**
+ * Set HTML content of popup
+ */
+ appendContent: function(newContent, type) {
+ mfp.content = newContent;
+
+ if(newContent) {
+ if(mfp.st.showCloseBtn && mfp.st.closeBtnInside &&
+ mfp.currTemplate[type] === true) {
+ // if there is no markup, we just append close button element inside
+ if(!mfp.content.find('.mfp-close').length) {
+ mfp.content.append(_getCloseBtn());
+ }
+ } else {
+ mfp.content = newContent;
+ }
+ } else {
+ mfp.content = '';
+ }
+
+ _mfpTrigger(BEFORE_APPEND_EVENT);
+ mfp.container.addClass('mfp-'+type+'-holder');
+
+ mfp.contentContainer.append(mfp.content);
+ },
+
+
+ /**
+ * Creates Magnific Popup data object based on given data
+ * @param {int} index Index of item to parse
+ */
+ parseEl: function(index) {
+ var item = mfp.items[index],
+ type;
+
+ if(item.tagName) {
+ item = { el: $(item) };
+ } else {
+ type = item.type;
+ item = { data: item, src: item.src };
+ }
+
+ if(item.el) {
+ var types = mfp.types;
+
+ // check for 'mfp-TYPE' class
+ for(var i = 0; i < types.length; i++) {
+ if( item.el.hasClass('mfp-'+types[i]) ) {
+ type = types[i];
+ break;
+ }
+ }
+
+ item.src = item.el.attr('data-mfp-src');
+ if(!item.src) {
+ item.src = item.el.attr('href');
+ }
+ }
+
+ item.type = type || mfp.st.type || 'inline';
+ item.index = index;
+ item.parsed = true;
+ mfp.items[index] = item;
+ _mfpTrigger('ElementParse', item);
+
+ return mfp.items[index];
+ },
+
+
+ /**
+ * Initializes single popup or a group of popups
+ */
+ addGroup: function(el, options) {
+ var eHandler = function(e) {
+ e.mfpEl = this;
+ mfp._openClick(e, el, options);
+ };
+
+ if(!options) {
+ options = {};
+ }
+
+ var eName = 'click.magnificPopup';
+ options.mainEl = el;
+
+ if(options.items) {
+ options.isObj = true;
+ el.off(eName).on(eName, eHandler);
+ } else {
+ options.isObj = false;
+ if(options.delegate) {
+ el.off(eName).on(eName, options.delegate , eHandler);
+ } else {
+ options.items = el;
+ el.off(eName).on(eName, eHandler);
+ }
+ }
+ },
+ _openClick: function(e, el, options) {
+ var midClick = options.midClick !== undefined ? options.midClick : $.magnificPopup.defaults.midClick;
+
+
+ if(!midClick && ( e.which === 2 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey ) ) {
+ return;
+ }
+
+ var disableOn = options.disableOn !== undefined ? options.disableOn : $.magnificPopup.defaults.disableOn;
+
+ if(disableOn) {
+ if($.isFunction(disableOn)) {
+ if( !disableOn.call(mfp) ) {
+ return true;
+ }
+ } else { // else it's number
+ if( _window.width() < disableOn ) {
+ return true;
+ }
+ }
+ }
+
+ if(e.type) {
+ e.preventDefault();
+
+ // This will prevent popup from closing if element is inside and popup is already opened
+ if(mfp.isOpen) {
+ e.stopPropagation();
+ }
+ }
+
+ options.el = $(e.mfpEl);
+ if(options.delegate) {
+ options.items = el.find(options.delegate);
+ }
+ mfp.open(options);
+ },
+
+
+ /**
+ * Updates text on preloader
+ */
+ updateStatus: function(status, text) {
+
+ if(mfp.preloader) {
+ if(_prevStatus !== status) {
+ mfp.container.removeClass('mfp-s-'+_prevStatus);
+ }
+
+ if(!text && status === 'loading') {
+ text = mfp.st.tLoading;
+ }
+
+ var data = {
+ status: status,
+ text: text
+ };
+ // allows to modify status
+ _mfpTrigger('UpdateStatus', data);
+
+ status = data.status;
+ text = data.text;
+
+ mfp.preloader.html(text);
+
+ mfp.preloader.find('a').on('click', function(e) {
+ e.stopImmediatePropagation();
+ });
+
+ mfp.container.addClass('mfp-s-'+status);
+ _prevStatus = status;
+ }
+ },
+
+
+ /*
+ "Private" helpers that aren't private at all
+ */
+ // Check to close popup or not
+ // "target" is an element that was clicked
+ _checkIfClose: function(target) {
+
+ if($(target).hasClass(PREVENT_CLOSE_CLASS)) {
+ return;
+ }
+
+ var closeOnContent = mfp.st.closeOnContentClick;
+ var closeOnBg = mfp.st.closeOnBgClick;
+
+ if(closeOnContent && closeOnBg) {
+ return true;
+ } else {
+
+ // We close the popup if click is on close button or on preloader. Or if there is no content.
+ if(!mfp.content || $(target).hasClass('mfp-close') || (mfp.preloader && target === mfp.preloader[0]) ) {
+ return true;
+ }
+
+ // if click is outside the content
+ if( (target !== mfp.content[0] && !$.contains(mfp.content[0], target)) ) {
+ if(closeOnBg) {
+ // last check, if the clicked element is in DOM, (in case it's removed onclick)
+ if( $.contains(document, target) ) {
+ return true;
+ }
+ }
+ } else if(closeOnContent) {
+ return true;
+ }
+
+ }
+ return false;
+ },
+ _addClassToMFP: function(cName) {
+ mfp.bgOverlay.addClass(cName);
+ mfp.wrap.addClass(cName);
+ },
+ _removeClassFromMFP: function(cName) {
+ this.bgOverlay.removeClass(cName);
+ mfp.wrap.removeClass(cName);
+ },
+ _hasScrollBar: function(winHeight) {
+ return ( (mfp.isIE7 ? _document.height() : document.body.scrollHeight) > (winHeight || _window.height()) );
+ },
+ _setFocus: function() {
+ (mfp.st.focus ? mfp.content.find(mfp.st.focus).eq(0) : mfp.wrap).focus();
+ },
+ _onFocusIn: function(e) {
+ if( e.target !== mfp.wrap[0] && !$.contains(mfp.wrap[0], e.target) ) {
+ mfp._setFocus();
+ return false;
+ }
+ },
+ _parseMarkup: function(template, values, item) {
+ var arr;
+ if(item.data) {
+ values = $.extend(item.data, values);
+ }
+ _mfpTrigger(MARKUP_PARSE_EVENT, [template, values, item] );
+
+ $.each(values, function(key, value) {
+ if(value === undefined || value === false) {
+ return true;
+ }
+ arr = key.split('_');
+ if(arr.length > 1) {
+ var el = template.find(EVENT_NS + '-'+arr[0]);
+
+ if(el.length > 0) {
+ var attr = arr[1];
+ if(attr === 'replaceWith') {
+ if(el[0] !== value[0]) {
+ el.replaceWith(value);
+ }
+ } else if(attr === 'img') {
+ if(el.is('img')) {
+ el.attr('src', value);
+ } else {
+ el.replaceWith( $('<img>').attr('src', value).attr('class', el.attr('class')) );
+ }
+ } else {
+ el.attr(arr[1], value);
+ }
+ }
+
+ } else {
+ template.find(EVENT_NS + '-'+key).html(value);
+ }
+ });
+ },
+
+ _getScrollbarSize: function() {
+ // thx David
+ if(mfp.scrollbarSize === undefined) {
+ var scrollDiv = document.createElement("div");
+ scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
+ document.body.appendChild(scrollDiv);
+ mfp.scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth;
+ document.body.removeChild(scrollDiv);
+ }
+ return mfp.scrollbarSize;
+ }
+
+ }; /* MagnificPopup core prototype end */
+
+
+
+
+ /**
+ * Public static functions
+ */
+ $.magnificPopup = {
+ instance: null,
+ proto: MagnificPopup.prototype,
+ modules: [],
+
+ open: function(options, index) {
+ _checkInstance();
+
+ if(!options) {
+ options = {};
+ } else {
+ options = $.extend(true, {}, options);
+ }
+
+ options.isObj = true;
+ options.index = index || 0;
+ return this.instance.open(options);
+ },
+
+ close: function() {
+ return $.magnificPopup.instance && $.magnificPopup.instance.close();
+ },
+
+ registerModule: function(name, module) {
+ if(module.options) {
+ $.magnificPopup.defaults[name] = module.options;
+ }
+ $.extend(this.proto, module.proto);
+ this.modules.push(name);
+ },
+
+ defaults: {
+
+ // Info about options is in docs:
+ // http://dimsemenov.com/plugins/magnific-popup/documentation.html#options
+
+ disableOn: 0,
+
+ key: null,
+
+ midClick: false,
+
+ mainClass: '',
+
+ preloader: true,
+
+ focus: '', // CSS selector of input to focus after popup is opened
+
+ closeOnContentClick: false,
+
+ closeOnBgClick: true,
+
+ closeBtnInside: true,
+
+ showCloseBtn: true,
+
+ enableEscapeKey: true,
+
+ modal: false,
+
+ alignTop: false,
+
+ removalDelay: 0,
+
+ prependTo: null,
+
+ fixedContentPos: 'auto',
+
+ fixedBgPos: 'auto',
+
+ overflowY: 'auto',
+
+ closeMarkup: '<button title="%title%" type="button" class="mfp-close">&#215;</button>',
+
+ tClose: 'Close (Esc)',
+
+ tLoading: 'Loading...',
+
+ autoFocusLast: true
+
+ }
+ };
+
+
+
+ $.fn.magnificPopup = function(options) {
+ _checkInstance();
+
+ var jqEl = $(this);
+
+ // We call some API method of first param is a string
+ if (typeof options === "string" ) {
+
+ if(options === 'open') {
+ var items,
+ itemOpts = _isJQ ? jqEl.data('magnificPopup') : jqEl[0].magnificPopup,
+ index = parseInt(arguments[1], 10) || 0;
+
+ if(itemOpts.items) {
+ items = itemOpts.items[index];
+ } else {
+ items = jqEl;
+ if(itemOpts.delegate) {
+ items = items.find(itemOpts.delegate);
+ }
+ items = items.eq( index );
+ }
+ mfp._openClick({mfpEl:items}, jqEl, itemOpts);
+ } else {
+ if(mfp.isOpen)
+ mfp[options].apply(mfp, Array.prototype.slice.call(arguments, 1));
+ }
+
+ } else {
+ // clone options obj
+ options = $.extend(true, {}, options);
+
+ /*
+ * As Zepto doesn't support .data() method for objects
+ * and it works only in normal browsers
+ * we assign "options" object directly to the DOM element. FTW!
+ */
+ if(_isJQ) {
+ jqEl.data('magnificPopup', options);
+ } else {
+ jqEl[0].magnificPopup = options;
+ }
+
+ mfp.addGroup(jqEl, options);
+
+ }
+ return jqEl;
+ };
+
+ /*>>core*/
+
+ /*>>inline*/
+
+ var INLINE_NS = 'inline',
+ _hiddenClass,
+ _inlinePlaceholder,
+ _lastInlineElement,
+ _putInlineElementsBack = function() {
+ if(_lastInlineElement) {
+ _inlinePlaceholder.after( _lastInlineElement.addClass(_hiddenClass) ).detach();
+ _lastInlineElement = null;
+ }
+ };
+
+ $.magnificPopup.registerModule(INLINE_NS, {
+ options: {
+ hiddenClass: 'hide', // will be appended with `mfp-` prefix
+ markup: '',
+ tNotFound: 'Content not found'
+ },
+ proto: {
+
+ initInline: function() {
+ mfp.types.push(INLINE_NS);
+
+ _mfpOn(CLOSE_EVENT+'.'+INLINE_NS, function() {
+ _putInlineElementsBack();
+ });
+ },
+
+ getInline: function(item, template) {
+
+ _putInlineElementsBack();
+
+ if(item.src) {
+ var inlineSt = mfp.st.inline,
+ el = $(item.src);
+
+ if(el.length) {
+
+ // If target element has parent - we replace it with placeholder and put it back after popup is closed
+ var parent = el[0].parentNode;
+ if(parent && parent.tagName) {
+ if(!_inlinePlaceholder) {
+ _hiddenClass = inlineSt.hiddenClass;
+ _inlinePlaceholder = _getEl(_hiddenClass);
+ _hiddenClass = 'mfp-'+_hiddenClass;
+ }
+ // replace target inline element with placeholder
+ _lastInlineElement = el.after(_inlinePlaceholder).detach().removeClass(_hiddenClass);
+ }
+
+ mfp.updateStatus('ready');
+ } else {
+ mfp.updateStatus('error', inlineSt.tNotFound);
+ el = $('<div>');
+ }
+
+ item.inlineElement = el;
+ return el;
+ }
+
+ mfp.updateStatus('ready');
+ mfp._parseMarkup(template, {}, item);
+ return template;
+ }
+ }
+ });
+
+ /*>>inline*/
+
+ /*>>ajax*/
+ var AJAX_NS = 'ajax',
+ _ajaxCur,
+ _removeAjaxCursor = function() {
+ if(_ajaxCur) {
+ $(document.body).removeClass(_ajaxCur);
+ }
+ },
+ _destroyAjaxRequest = function() {
+ _removeAjaxCursor();
+ if(mfp.req) {
+ mfp.req.abort();
+ }
+ };
+
+ $.magnificPopup.registerModule(AJAX_NS, {
+
+ options: {
+ settings: null,
+ cursor: 'mfp-ajax-cur',
+ tError: '<a href="%url%">The content</a> could not be loaded.'
+ },
+
+ proto: {
+ initAjax: function() {
+ mfp.types.push(AJAX_NS);
+ _ajaxCur = mfp.st.ajax.cursor;
+
+ _mfpOn(CLOSE_EVENT+'.'+AJAX_NS, _destroyAjaxRequest);
+ _mfpOn('BeforeChange.' + AJAX_NS, _destroyAjaxRequest);
+ },
+ getAjax: function(item) {
+
+ if(_ajaxCur) {
+ $(document.body).addClass(_ajaxCur);
+ }
+
+ mfp.updateStatus('loading');
+
+ var opts = $.extend({
+ url: item.src,
+ success: function(data, textStatus, jqXHR) {
+ var temp = {
+ data:data,
+ xhr:jqXHR
+ };
+
+ _mfpTrigger('ParseAjax', temp);
+
+ mfp.appendContent( $(temp.data), AJAX_NS );
+
+ item.finished = true;
+
+ _removeAjaxCursor();
+
+ mfp._setFocus();
+
+ setTimeout(function() {
+ mfp.wrap.addClass(READY_CLASS);
+ }, 16);
+
+ mfp.updateStatus('ready');
+
+ _mfpTrigger('AjaxContentAdded');
+ },
+ error: function() {
+ _removeAjaxCursor();
+ item.finished = item.loadError = true;
+ mfp.updateStatus('error', mfp.st.ajax.tError.replace('%url%', item.src));
+ }
+ }, mfp.st.ajax.settings);
+
+ mfp.req = $.ajax(opts);
+
+ return '';
+ }
+ }
+ });
+
+ /*>>ajax*/
+
+ /*>>image*/
+ var _imgInterval,
+ _getTitle = function(item) {
+ if(item.data && item.data.title !== undefined)
+ return item.data.title;
+
+ var src = mfp.st.image.titleSrc;
+
+ if(src) {
+ if($.isFunction(src)) {
+ return src.call(mfp, item);
+ } else if(item.el) {
+ return item.el.attr(src) || '';
+ }
+ }
+ return '';
+ };
+
+ $.magnificPopup.registerModule('image', {
+
+ options: {
+ markup: '<div class="mfp-figure">'+
+ '<div class="mfp-close"></div>'+
+ '<figure>'+
+ '<div class="mfp-img"></div>'+
+ '<figcaption>'+
+ '<div class="mfp-bottom-bar">'+
+ '<div class="mfp-title"></div>'+
+ '<div class="mfp-counter"></div>'+
+ '</div>'+
+ '</figcaption>'+
+ '</figure>'+
+ '</div>',
+ cursor: 'mfp-zoom-out-cur',
+ titleSrc: 'title',
+ verticalFit: true,
+ tError: '<a href="%url%">The image</a> could not be loaded.'
+ },
+
+ proto: {
+ initImage: function() {
+ var imgSt = mfp.st.image,
+ ns = '.image';
+
+ mfp.types.push('image');
+
+ _mfpOn(OPEN_EVENT+ns, function() {
+ if(mfp.currItem.type === 'image' && imgSt.cursor) {
+ $(document.body).addClass(imgSt.cursor);
+ }
+ });
+
+ _mfpOn(CLOSE_EVENT+ns, function() {
+ if(imgSt.cursor) {
+ $(document.body).removeClass(imgSt.cursor);
+ }
+ _window.off('resize' + EVENT_NS);
+ });
+
+ _mfpOn('Resize'+ns, mfp.resizeImage);
+ if(mfp.isLowIE) {
+ _mfpOn('AfterChange', mfp.resizeImage);
+ }
+ },
+ resizeImage: function() {
+ var item = mfp.currItem;
+ if(!item || !item.img) return;
+
+ if(mfp.st.image.verticalFit) {
+ var decr = 0;
+ // fix box-sizing in ie7/8
+ if(mfp.isLowIE) {
+ decr = parseInt(item.img.css('padding-top'), 10) + parseInt(item.img.css('padding-bottom'),10);
+ }
+ item.img.css('max-height', mfp.wH-decr);
+ }
+ },
+ _onImageHasSize: function(item) {
+ if(item.img) {
+
+ item.hasSize = true;
+
+ if(_imgInterval) {
+ clearInterval(_imgInterval);
+ }
+
+ item.isCheckingImgSize = false;
+
+ _mfpTrigger('ImageHasSize', item);
+
+ if(item.imgHidden) {
+ if(mfp.content)
+ mfp.content.removeClass('mfp-loading');
+
+ item.imgHidden = false;
+ }
+
+ }
+ },
+
+ /**
+ * Function that loops until the image has size to display elements that rely on it asap
+ */
+ findImageSize: function(item) {
+
+ var counter = 0,
+ img = item.img[0],
+ mfpSetInterval = function(delay) {
+
+ if(_imgInterval) {
+ clearInterval(_imgInterval);
+ }
+ // decelerating interval that checks for size of an image
+ _imgInterval = setInterval(function() {
+ if(img.naturalWidth > 0) {
+ mfp._onImageHasSize(item);
+ return;
+ }
+
+ if(counter > 200) {
+ clearInterval(_imgInterval);
+ }
+
+ counter++;
+ if(counter === 3) {
+ mfpSetInterval(10);
+ } else if(counter === 40) {
+ mfpSetInterval(50);
+ } else if(counter === 100) {
+ mfpSetInterval(500);
+ }
+ }, delay);
+ };
+
+ mfpSetInterval(1);
+ },
+
+ getImage: function(item, template) {
+
+ var guard = 0,
+
+ // image load complete handler
+ onLoadComplete = function() {
+ if(item) {
+ if (item.img[0].complete) {
+ item.img.off('.mfploader');
+
+ if(item === mfp.currItem){
+ mfp._onImageHasSize(item);
+
+ mfp.updateStatus('ready');
+ }
+
+ item.hasSize = true;
+ item.loaded = true;
+
+ _mfpTrigger('ImageLoadComplete');
+
+ }
+ else {
+ // if image complete check fails 200 times (20 sec), we assume that there was an error.
+ guard++;
+ if(guard < 200) {
+ setTimeout(onLoadComplete,100);
+ } else {
+ onLoadError();
+ }
+ }
+ }
+ },
+
+ // image error handler
+ onLoadError = function() {
+ if(item) {
+ item.img.off('.mfploader');
+ if(item === mfp.currItem){
+ mfp._onImageHasSize(item);
+ mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) );
+ }
+
+ item.hasSize = true;
+ item.loaded = true;
+ item.loadError = true;
+ }
+ },
+ imgSt = mfp.st.image;
+
+
+ var el = template.find('.mfp-img');
+ if(el.length) {
+ var img = document.createElement('img');
+ img.className = 'mfp-img';
+ if(item.el && item.el.find('img').length) {
+ img.alt = item.el.find('img').attr('alt');
+ }
+ item.img = $(img).on('load.mfploader', onLoadComplete).on('error.mfploader', onLoadError);
+ img.src = item.src;
+
+ // without clone() "error" event is not firing when IMG is replaced by new IMG
+ // TODO: find a way to avoid such cloning
+ if(el.is('img')) {
+ item.img = item.img.clone();
+ }
+
+ img = item.img[0];
+ if(img.naturalWidth > 0) {
+ item.hasSize = true;
+ } else if(!img.width) {
+ item.hasSize = false;
+ }
+ }
+
+ mfp._parseMarkup(template, {
+ title: _getTitle(item),
+ img_replaceWith: item.img
+ }, item);
+
+ mfp.resizeImage();
+
+ if(item.hasSize) {
+ if(_imgInterval) clearInterval(_imgInterval);
+
+ if(item.loadError) {
+ template.addClass('mfp-loading');
+ mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) );
+ } else {
+ template.removeClass('mfp-loading');
+ mfp.updateStatus('ready');
+ }
+ return template;
+ }
+
+ mfp.updateStatus('loading');
+ item.loading = true;
+
+ if(!item.hasSize) {
+ item.imgHidden = true;
+ template.addClass('mfp-loading');
+ mfp.findImageSize(item);
+ }
+
+ return template;
+ }
+ }
+ });
+
+ /*>>image*/
+
+ /*>>zoom*/
+ var hasMozTransform,
+ getHasMozTransform = function() {
+ if(hasMozTransform === undefined) {
+ hasMozTransform = document.createElement('p').style.MozTransform !== undefined;
+ }
+ return hasMozTransform;
+ };
+
+ $.magnificPopup.registerModule('zoom', {
+
+ options: {
+ enabled: false,
+ easing: 'ease-in-out',
+ duration: 300,
+ opener: function(element) {
+ return element.is('img') ? element : element.find('img');
+ }
+ },
+
+ proto: {
+
+ initZoom: function() {
+ var zoomSt = mfp.st.zoom,
+ ns = '.zoom',
+ image;
+
+ if(!zoomSt.enabled || !mfp.supportsTransition) {
+ return;
+ }
+
+ var duration = zoomSt.duration,
+ getElToAnimate = function(image) {
+ var newImg = image.clone().removeAttr('style').removeAttr('class').addClass('mfp-animated-image'),
+ transition = 'all '+(zoomSt.duration/1000)+'s ' + zoomSt.easing,
+ cssObj = {
+ position: 'fixed',
+ zIndex: 9999,
+ left: 0,
+ top: 0,
+ '-webkit-backface-visibility': 'hidden'
+ },
+ t = 'transition';
+
+ cssObj['-webkit-'+t] = cssObj['-moz-'+t] = cssObj['-o-'+t] = cssObj[t] = transition;
+
+ newImg.css(cssObj);
+ return newImg;
+ },
+ showMainContent = function() {
+ mfp.content.css('visibility', 'visible');
+ },
+ openTimeout,
+ animatedImg;
+
+ _mfpOn('BuildControls'+ns, function() {
+ if(mfp._allowZoom()) {
+
+ clearTimeout(openTimeout);
+ mfp.content.css('visibility', 'hidden');
+
+ // Basically, all code below does is clones existing image, puts in on top of the current one and animated it
+
+ image = mfp._getItemToZoom();
+
+ if(!image) {
+ showMainContent();
+ return;
+ }
+
+ animatedImg = getElToAnimate(image);
+
+ animatedImg.css( mfp._getOffset() );
+
+ mfp.wrap.append(animatedImg);
+
+ openTimeout = setTimeout(function() {
+ animatedImg.css( mfp._getOffset( true ) );
+ openTimeout = setTimeout(function() {
+
+ showMainContent();
+
+ setTimeout(function() {
+ animatedImg.remove();
+ image = animatedImg = null;
+ _mfpTrigger('ZoomAnimationEnded');
+ }, 16); // avoid blink when switching images
+
+ }, duration); // this timeout equals animation duration
+
+ }, 16); // by adding this timeout we avoid short glitch at the beginning of animation
+
+
+ // Lots of timeouts...
+ }
+ });
+ _mfpOn(BEFORE_CLOSE_EVENT+ns, function() {
+ if(mfp._allowZoom()) {
+
+ clearTimeout(openTimeout);
+
+ mfp.st.removalDelay = duration;
+
+ if(!image) {
+ image = mfp._getItemToZoom();
+ if(!image) {
+ return;
+ }
+ animatedImg = getElToAnimate(image);
+ }
+
+ animatedImg.css( mfp._getOffset(true) );
+ mfp.wrap.append(animatedImg);
+ mfp.content.css('visibility', 'hidden');
+
+ setTimeout(function() {
+ animatedImg.css( mfp._getOffset() );
+ }, 16);
+ }
+
+ });
+
+ _mfpOn(CLOSE_EVENT+ns, function() {
+ if(mfp._allowZoom()) {
+ showMainContent();
+ if(animatedImg) {
+ animatedImg.remove();
+ }
+ image = null;
+ }
+ });
+ },
+
+ _allowZoom: function() {
+ return mfp.currItem.type === 'image';
+ },
+
+ _getItemToZoom: function() {
+ if(mfp.currItem.hasSize) {
+ return mfp.currItem.img;
+ } else {
+ return false;
+ }
+ },
+
+ // Get element postion relative to viewport
+ _getOffset: function(isLarge) {
+ var el;
+ if(isLarge) {
+ el = mfp.currItem.img;
+ } else {
+ el = mfp.st.zoom.opener(mfp.currItem.el || mfp.currItem);
+ }
+
+ var offset = el.offset();
+ var paddingTop = parseInt(el.css('padding-top'),10);
+ var paddingBottom = parseInt(el.css('padding-bottom'),10);
+ offset.top -= ( $(window).scrollTop() - paddingTop );
+
+
+ /*
+
+ Animating left + top + width/height looks glitchy in Firefox, but perfect in Chrome. And vice-versa.
+
+ */
+ var obj = {
+ width: el.width(),
+ // fix Zepto height+padding issue
+ height: (_isJQ ? el.innerHeight() : el[0].offsetHeight) - paddingBottom - paddingTop
+ };
+
+ // I hate to do this, but there is no another option
+ if( getHasMozTransform() ) {
+ obj['-moz-transform'] = obj['transform'] = 'translate(' + offset.left + 'px,' + offset.top + 'px)';
+ } else {
+ obj.left = offset.left;
+ obj.top = offset.top;
+ }
+ return obj;
+ }
+
+ }
+ });
+
+
+
+ /*>>zoom*/
+
+ /*>>iframe*/
+
+ var IFRAME_NS = 'iframe',
+ _emptyPage = '//about:blank',
+
+ _fixIframeBugs = function(isShowing) {
+ if(mfp.currTemplate[IFRAME_NS]) {
+ var el = mfp.currTemplate[IFRAME_NS].find('iframe');
+ if(el.length) {
+ // reset src after the popup is closed to avoid "video keeps playing after popup is closed" bug
+ if(!isShowing) {
+ el[0].src = _emptyPage;
+ }
+
+ // IE8 black screen bug fix
+ if(mfp.isIE8) {
+ el.css('display', isShowing ? 'block' : 'none');
+ }
+ }
+ }
+ };
+
+ $.magnificPopup.registerModule(IFRAME_NS, {
+
+ options: {
+ markup: '<div class="mfp-iframe-scaler">'+
+ '<div class="mfp-close"></div>'+
+ '<iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe>'+
+ '</div>',
+
+ srcAction: 'iframe_src',
+
+ // we don't care and support only one default type of URL by default
+ patterns: {
+ youtube: {
+ index: 'youtube.com',
+ id: 'v=',
+ src: '//www.youtube.com/embed/%id%?autoplay=1'
+ },
+ vimeo: {
+ index: 'vimeo.com/',
+ id: '/',
+ src: '//player.vimeo.com/video/%id%?autoplay=1'
+ },
+ gmaps: {
+ index: '//maps.google.',
+ src: '%id%&output=embed'
+ }
+ }
+ },
+
+ proto: {
+ initIframe: function() {
+ mfp.types.push(IFRAME_NS);
+
+ _mfpOn('BeforeChange', function(e, prevType, newType) {
+ if(prevType !== newType) {
+ if(prevType === IFRAME_NS) {
+ _fixIframeBugs(); // iframe if removed
+ } else if(newType === IFRAME_NS) {
+ _fixIframeBugs(true); // iframe is showing
+ }
+ }// else {
+ // iframe source is switched, don't do anything
+ //}
+ });
+
+ _mfpOn(CLOSE_EVENT + '.' + IFRAME_NS, function() {
+ _fixIframeBugs();
+ });
+ },
+
+ getIframe: function(item, template) {
+ var embedSrc = item.src;
+ var iframeSt = mfp.st.iframe;
+
+ $.each(iframeSt.patterns, function() {
+ if(embedSrc.indexOf( this.index ) > -1) {
+ if(this.id) {
+ if(typeof this.id === 'string') {
+ embedSrc = embedSrc.substr(embedSrc.lastIndexOf(this.id)+this.id.length, embedSrc.length);
+ } else {
+ embedSrc = this.id.call( this, embedSrc );
+ }
+ }
+ embedSrc = this.src.replace('%id%', embedSrc );
+ return false; // break;
+ }
+ });
+
+ var dataObj = {};
+ if(iframeSt.srcAction) {
+ dataObj[iframeSt.srcAction] = embedSrc;
+ }
+ mfp._parseMarkup(template, dataObj, item);
+
+ mfp.updateStatus('ready');
+
+ return template;
+ }
+ }
+ });
+
+
+
+ /*>>iframe*/
+
+ /*>>gallery*/
+ /**
+ * Get looped index depending on number of slides
+ */
+ var _getLoopedId = function(index) {
+ var numSlides = mfp.items.length;
+ if(index > numSlides - 1) {
+ return index - numSlides;
+ } else if(index < 0) {
+ return numSlides + index;
+ }
+ return index;
+ },
+ _replaceCurrTotal = function(text, curr, total) {
+ return text.replace(/%curr%/gi, curr + 1).replace(/%total%/gi, total);
+ };
+
+ $.magnificPopup.registerModule('gallery', {
+
+ options: {
+ enabled: false,
+ arrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',
+ preload: [0,2],
+ navigateByImgClick: true,
+ arrows: true,
+
+ tPrev: 'Previous (Left arrow key)',
+ tNext: 'Next (Right arrow key)',
+ tCounter: '%curr% of %total%'
+ },
+
+ proto: {
+ initGallery: function() {
+
+ var gSt = mfp.st.gallery,
+ ns = '.mfp-gallery';
+
+ mfp.direction = true; // true - next, false - prev
+
+ if(!gSt || !gSt.enabled ) return false;
+
+ _wrapClasses += ' mfp-gallery';
+
+ _mfpOn(OPEN_EVENT+ns, function() {
+
+ if(gSt.navigateByImgClick) {
+ mfp.wrap.on('click'+ns, '.mfp-img', function() {
+ if(mfp.items.length > 1) {
+ mfp.next();
+ return false;
+ }
+ });
+ }
+
+ _document.on('keydown'+ns, function(e) {
+ if (e.keyCode === 37) {
+ mfp.prev();
+ } else if (e.keyCode === 39) {
+ mfp.next();
+ }
+ });
+ });
+
+ _mfpOn('UpdateStatus'+ns, function(e, data) {
+ if(data.text) {
+ data.text = _replaceCurrTotal(data.text, mfp.currItem.index, mfp.items.length);
+ }
+ });
+
+ _mfpOn(MARKUP_PARSE_EVENT+ns, function(e, element, values, item) {
+ var l = mfp.items.length;
+ values.counter = l > 1 ? _replaceCurrTotal(gSt.tCounter, item.index, l) : '';
+ });
+
+ _mfpOn('BuildControls' + ns, function() {
+ if(mfp.items.length > 1 && gSt.arrows && !mfp.arrowLeft) {
+ var markup = gSt.arrowMarkup,
+ arrowLeft = mfp.arrowLeft = $( markup.replace(/%title%/gi, gSt.tPrev).replace(/%dir%/gi, 'left') ).addClass(PREVENT_CLOSE_CLASS),
+ arrowRight = mfp.arrowRight = $( markup.replace(/%title%/gi, gSt.tNext).replace(/%dir%/gi, 'right') ).addClass(PREVENT_CLOSE_CLASS);
+
+ arrowLeft.click(function() {
+ mfp.prev();
+ });
+ arrowRight.click(function() {
+ mfp.next();
+ });
+
+ mfp.container.append(arrowLeft.add(arrowRight));
+ }
+ });
+
+ _mfpOn(CHANGE_EVENT+ns, function() {
+ if(mfp._preloadTimeout) clearTimeout(mfp._preloadTimeout);
+
+ mfp._preloadTimeout = setTimeout(function() {
+ mfp.preloadNearbyImages();
+ mfp._preloadTimeout = null;
+ }, 16);
+ });
+
+
+ _mfpOn(CLOSE_EVENT+ns, function() {
+ _document.off(ns);
+ mfp.wrap.off('click'+ns);
+ mfp.arrowRight = mfp.arrowLeft = null;
+ });
+
+ },
+ next: function() {
+ mfp.direction = true;
+ mfp.index = _getLoopedId(mfp.index + 1);
+ mfp.updateItemHTML();
+ },
+ prev: function() {
+ mfp.direction = false;
+ mfp.index = _getLoopedId(mfp.index - 1);
+ mfp.updateItemHTML();
+ },
+ goTo: function(newIndex) {
+ mfp.direction = (newIndex >= mfp.index);
+ mfp.index = newIndex;
+ mfp.updateItemHTML();
+ },
+ preloadNearbyImages: function() {
+ var p = mfp.st.gallery.preload,
+ preloadBefore = Math.min(p[0], mfp.items.length),
+ preloadAfter = Math.min(p[1], mfp.items.length),
+ i;
+
+ for(i = 1; i <= (mfp.direction ? preloadAfter : preloadBefore); i++) {
+ mfp._preloadItem(mfp.index+i);
+ }
+ for(i = 1; i <= (mfp.direction ? preloadBefore : preloadAfter); i++) {
+ mfp._preloadItem(mfp.index-i);
+ }
+ },
+ _preloadItem: function(index) {
+ index = _getLoopedId(index);
+
+ if(mfp.items[index].preloaded) {
+ return;
+ }
+
+ var item = mfp.items[index];
+ if(!item.parsed) {
+ item = mfp.parseEl( index );
+ }
+
+ _mfpTrigger('LazyLoad', item);
+
+ if(item.type === 'image') {
+ item.img = $('<img class="mfp-img" />').on('load.mfploader', function() {
+ item.hasSize = true;
+ }).on('error.mfploader', function() {
+ item.hasSize = true;
+ item.loadError = true;
+ _mfpTrigger('LazyLoadError', item);
+ }).attr('src', item.src);
+ }
+
+
+ item.preloaded = true;
+ }
+ }
+ });
+
+ /*>>gallery*/
+
+ /*>>retina*/
+
+ var RETINA_NS = 'retina';
+
+ $.magnificPopup.registerModule(RETINA_NS, {
+ options: {
+ replaceSrc: function(item) {
+ return item.src.replace(/\.\w+$/, function(m) { return '@2x' + m; });
+ },
+ ratio: 1 // Function or number. Set to 1 to disable.
+ },
+ proto: {
+ initRetina: function() {
+ if(window.devicePixelRatio > 1) {
+
+ var st = mfp.st.retina,
+ ratio = st.ratio;
+
+ ratio = !isNaN(ratio) ? ratio : ratio();
+
+ if(ratio > 1) {
+ _mfpOn('ImageHasSize' + '.' + RETINA_NS, function(e, item) {
+ item.img.css({
+ 'max-width': item.img[0].naturalWidth / ratio,
+ 'width': '100%'
+ });
+ });
+ _mfpOn('ElementParse' + '.' + RETINA_NS, function(e, item) {
+ item.src = st.replaceSrc(item, ratio);
+ });
+ }
+ }
+
+ }
+ }
+ });
+
+ /*>>retina*/
+ _checkInstance(); })); \ No newline at end of file
diff --git a/assets/js/plugins/smooth-scroll.js b/assets/js/plugins/smooth-scroll.js
new file mode 100644
index 0000000..e0cf796
--- /dev/null
+++ b/assets/js/plugins/smooth-scroll.js
@@ -0,0 +1,632 @@
+/*!
+ * smooth-scroll v15.2.1
+ * Animate scrolling to anchor links
+ * (c) 2019 Chris Ferdinandi
+ * MIT License
+ * http://github.com/cferdinandi/smooth-scroll
+ */
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define([], (function () {
+ return factory(root);
+ }));
+ } else if (typeof exports === 'object') {
+ module.exports = factory(root);
+ } else {
+ root.SmoothScroll = factory(root);
+ }
+})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this, (function (window) {
+
+ 'use strict';
+
+ //
+ // Default settings
+ //
+
+ var defaults = {
+
+ // Selectors
+ ignore: '[data-scroll-ignore]',
+ header: null,
+ topOnEmptyHash: true,
+
+ // Speed & Duration
+ speed: 500,
+ speedAsDuration: false,
+ durationMax: null,
+ durationMin: null,
+ clip: true,
+ offset: 0,
+
+ // Easing
+ easing: 'easeInOutCubic',
+ customEasing: null,
+
+ // History
+ updateURL: true,
+ popstate: true,
+
+ // Custom Events
+ emitEvents: true
+
+ };
+
+
+ //
+ // Utility Methods
+ //
+
+ /**
+ * Check if browser supports required methods
+ * @return {Boolean} Returns true if all required methods are supported
+ */
+ var supports = function () {
+ return (
+ 'querySelector' in document &&
+ 'addEventListener' in window &&
+ 'requestAnimationFrame' in window &&
+ 'closest' in window.Element.prototype
+ );
+ };
+
+ /**
+ * Merge two or more objects together.
+ * @param {Object} objects The objects to merge together
+ * @returns {Object} Merged values of defaults and options
+ */
+ var extend = function () {
+ var merged = {};
+ Array.prototype.forEach.call(arguments, (function (obj) {
+ for (var key in obj) {
+ if (!obj.hasOwnProperty(key)) return;
+ merged[key] = obj[key];
+ }
+ }));
+ return merged;
+ };
+
+ /**
+ * Check to see if user prefers reduced motion
+ * @param {Object} settings Script settings
+ */
+ var reduceMotion = function (settings) {
+ if ('matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Get the height of an element.
+ * @param {Node} elem The element to get the height of
+ * @return {Number} The element's height in pixels
+ */
+ var getHeight = function (elem) {
+ return parseInt(window.getComputedStyle(elem).height, 10);
+ };
+
+ /**
+ * Escape special characters for use with querySelector
+ * @author Mathias Bynens
+ * @link https://github.com/mathiasbynens/CSS.escape
+ * @param {String} id The anchor ID to escape
+ */
+ var escapeCharacters = function (id) {
+
+ // Remove leading hash
+ if (id.charAt(0) === '#') {
+ id = id.substr(1);
+ }
+
+ var string = String(id);
+ var length = string.length;
+ var index = -1;
+ var codeUnit;
+ var result = '';
+ var firstCodeUnit = string.charCodeAt(0);
+ while (++index < length) {
+ codeUnit = string.charCodeAt(index);
+ // Note: there’s no need to special-case astral symbols, surrogate
+ // pairs, or lone surrogates.
+
+ // If the character is NULL (U+0000), then throw an
+ // `InvalidCharacterError` exception and terminate these steps.
+ if (codeUnit === 0x0000) {
+ throw new InvalidCharacterError(
+ 'Invalid character: the input contains U+0000.'
+ );
+ }
+
+ if (
+ // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
+ // U+007F, […]
+ (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
+ // If the character is the first character and is in the range [0-9]
+ // (U+0030 to U+0039), […]
+ (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
+ // If the character is the second character and is in the range [0-9]
+ // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
+ (
+ index === 1 &&
+ codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
+ firstCodeUnit === 0x002D
+ )
+ ) {
+ // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
+ result += '\\' + codeUnit.toString(16) + ' ';
+ continue;
+ }
+
+ // If the character is not handled by one of the above rules and is
+ // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
+ // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
+ // U+005A), or [a-z] (U+0061 to U+007A), […]
+ if (
+ codeUnit >= 0x0080 ||
+ codeUnit === 0x002D ||
+ codeUnit === 0x005F ||
+ codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
+ codeUnit >= 0x0041 && codeUnit <= 0x005A ||
+ codeUnit >= 0x0061 && codeUnit <= 0x007A
+ ) {
+ // the character itself
+ result += string.charAt(index);
+ continue;
+ }
+
+ // Otherwise, the escaped character.
+ // http://dev.w3.org/csswg/cssom/#escape-a-character
+ result += '\\' + string.charAt(index);
+
+ }
+
+ // Return sanitized hash
+ return '#' + result;
+
+ };
+
+ /**
+ * Calculate the easing pattern
+ * @link https://gist.github.com/gre/1650294
+ * @param {String} type Easing pattern
+ * @param {Number} time Time animation should take to complete
+ * @returns {Number}
+ */
+ var easingPattern = function (settings, time) {
+ var pattern;
+
+ // Default Easing Patterns
+ if (settings.easing === 'easeInQuad') pattern = time * time; // accelerating from zero velocity
+ if (settings.easing === 'easeOutQuad') pattern = time * (2 - time); // decelerating to zero velocity
+ if (settings.easing === 'easeInOutQuad') pattern = time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
+ if (settings.easing === 'easeInCubic') pattern = time * time * time; // accelerating from zero velocity
+ if (settings.easing === 'easeOutCubic') pattern = (--time) * time * time + 1; // decelerating to zero velocity
+ if (settings.easing === 'easeInOutCubic') pattern = time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
+ if (settings.easing === 'easeInQuart') pattern = time * time * time * time; // accelerating from zero velocity
+ if (settings.easing === 'easeOutQuart') pattern = 1 - (--time) * time * time * time; // decelerating to zero velocity
+ if (settings.easing === 'easeInOutQuart') pattern = time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration
+ if (settings.easing === 'easeInQuint') pattern = time * time * time * time * time; // accelerating from zero velocity
+ if (settings.easing === 'easeOutQuint') pattern = 1 + (--time) * time * time * time * time; // decelerating to zero velocity
+ if (settings.easing === 'easeInOutQuint') pattern = time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration
+
+ // Custom Easing Patterns
+ if (!!settings.customEasing) pattern = settings.customEasing(time);
+
+ return pattern || time; // no easing, no acceleration
+ };
+
+ /**
+ * Determine the document's height
+ * @returns {Number}
+ */
+ var getDocumentHeight = function () {
+ return Math.max(
+ document.body.scrollHeight, document.documentElement.scrollHeight,
+ document.body.offsetHeight, document.documentElement.offsetHeight,
+ document.body.clientHeight, document.documentElement.clientHeight
+ );
+ };
+
+ /**
+ * Calculate how far to scroll
+ * Clip support added by robjtede - https://github.com/cferdinandi/smooth-scroll/issues/405
+ * @param {Element} anchor The anchor element to scroll to
+ * @param {Number} headerHeight Height of a fixed header, if any
+ * @param {Number} offset Number of pixels by which to offset scroll
+ * @param {Boolean} clip If true, adjust scroll distance to prevent abrupt stops near the bottom of the page
+ * @returns {Number}
+ */
+ var getEndLocation = function (anchor, headerHeight, offset, clip) {
+ var location = 0;
+ if (anchor.offsetParent) {
+ do {
+ location += anchor.offsetTop;
+ anchor = anchor.offsetParent;
+ } while (anchor);
+ }
+ location = Math.max(location - headerHeight - offset, 0);
+ if (clip) {
+ location = Math.min(location, getDocumentHeight() - window.innerHeight);
+ }
+ return location;
+ };
+
+ /**
+ * Get the height of the fixed header
+ * @param {Node} header The header
+ * @return {Number} The height of the header
+ */
+ var getHeaderHeight = function (header) {
+ return !header ? 0 : (getHeight(header) + header.offsetTop);
+ };
+
+ /**
+ * Calculate the speed to use for the animation
+ * @param {Number} distance The distance to travel
+ * @param {Object} settings The plugin settings
+ * @return {Number} How fast to animate
+ */
+ var getSpeed = function (distance, settings) {
+ var speed = settings.speedAsDuration ? settings.speed : Math.abs(distance / 1000 * settings.speed);
+ if (settings.durationMax && speed > settings.durationMax) return settings.durationMax;
+ if (settings.durationMin && speed < settings.durationMin) return settings.durationMin;
+ return parseInt(speed, 10);
+ };
+
+ var setHistory = function (options) {
+
+ // Make sure this should run
+ if (!history.replaceState || !options.updateURL || history.state) return;
+
+ // Get the hash to use
+ var hash = window.location.hash;
+ hash = hash ? hash : '';
+
+ // Set a default history
+ history.replaceState(
+ {
+ smoothScroll: JSON.stringify(options),
+ anchor: hash ? hash : window.pageYOffset
+ },
+ document.title,
+ hash ? hash : window.location.href
+ );
+
+ };
+
+ /**
+ * Update the URL
+ * @param {Node} anchor The anchor that was scrolled to
+ * @param {Boolean} isNum If true, anchor is a number
+ * @param {Object} options Settings for Smooth Scroll
+ */
+ var updateURL = function (anchor, isNum, options) {
+
+ // Bail if the anchor is a number
+ if (isNum) return;
+
+ // Verify that pushState is supported and the updateURL option is enabled
+ if (!history.pushState || !options.updateURL) return;
+
+ // Update URL
+ history.pushState(
+ {
+ smoothScroll: JSON.stringify(options),
+ anchor: anchor.id
+ },
+ document.title,
+ anchor === document.documentElement ? '#top' : '#' + anchor.id
+ );
+
+ };
+
+ /**
+ * Bring the anchored element into focus
+ * @param {Node} anchor The anchor element
+ * @param {Number} endLocation The end location to scroll to
+ * @param {Boolean} isNum If true, scroll is to a position rather than an element
+ */
+ var adjustFocus = function (anchor, endLocation, isNum) {
+
+ // Is scrolling to top of page, blur
+ if (anchor === 0) {
+ document.body.focus();
+ }
+
+ // Don't run if scrolling to a number on the page
+ if (isNum) return;
+
+ // Otherwise, bring anchor element into focus
+ anchor.focus();
+ if (document.activeElement !== anchor) {
+ anchor.setAttribute('tabindex', '-1');
+ anchor.focus();
+ anchor.style.outline = 'none';
+ }
+ window.scrollTo(0 , endLocation);
+
+ };
+
+ /**
+ * Emit a custom event
+ * @param {String} type The event type
+ * @param {Object} options The settings object
+ * @param {Node} anchor The anchor element
+ * @param {Node} toggle The toggle element
+ */
+ var emitEvent = function (type, options, anchor, toggle) {
+ if (!options.emitEvents || typeof window.CustomEvent !== 'function') return;
+ var event = new CustomEvent(type, {
+ bubbles: true,
+ detail: {
+ anchor: anchor,
+ toggle: toggle
+ }
+ });
+ document.dispatchEvent(event);
+ };
+
+
+ //
+ // SmoothScroll Constructor
+ //
+
+ var SmoothScroll = function (selector, options) {
+
+ //
+ // Variables
+ //
+
+ var smoothScroll = {}; // Object for public APIs
+ var settings, anchor, toggle, fixedHeader, eventTimeout, animationInterval;
+
+
+ //
+ // Methods
+ //
+
+ /**
+ * Cancel a scroll-in-progress
+ */
+ smoothScroll.cancelScroll = function (noEvent) {
+ cancelAnimationFrame(animationInterval);
+ animationInterval = null;
+ if (noEvent) return;
+ emitEvent('scrollCancel', settings);
+ };
+
+ /**
+ * Start/stop the scrolling animation
+ * @param {Node|Number} anchor The element or position to scroll to
+ * @param {Element} toggle The element that toggled the scroll event
+ * @param {Object} options
+ */
+ smoothScroll.animateScroll = function (anchor, toggle, options) {
+
+ // Cancel any in progress scrolls
+ smoothScroll.cancelScroll();
+
+ // Local settings
+ var _settings = extend(settings || defaults, options || {}); // Merge user options with defaults
+
+ // Selectors and variables
+ var isNum = Object.prototype.toString.call(anchor) === '[object Number]' ? true : false;
+ var anchorElem = isNum || !anchor.tagName ? null : anchor;
+ if (!isNum && !anchorElem) return;
+ var startLocation = window.pageYOffset; // Current location on the page
+ if (_settings.header && !fixedHeader) {
+ // Get the fixed header if not already set
+ fixedHeader = document.querySelector(_settings.header);
+ }
+ var headerHeight = getHeaderHeight(fixedHeader);
+ var endLocation = isNum ? anchor : getEndLocation(anchorElem, headerHeight, parseInt((typeof _settings.offset === 'function' ? _settings.offset(anchor, toggle) : _settings.offset), 10), _settings.clip); // Location to scroll to
+ var distance = endLocation - startLocation; // distance to travel
+ var documentHeight = getDocumentHeight();
+ var timeLapsed = 0;
+ var speed = getSpeed(distance, _settings);
+ var start, percentage, position;
+
+ /**
+ * Stop the scroll animation when it reaches its target (or the bottom/top of page)
+ * @param {Number} position Current position on the page
+ * @param {Number} endLocation Scroll to location
+ * @param {Number} animationInterval How much to scroll on this loop
+ */
+ var stopAnimateScroll = function (position, endLocation) {
+
+ // Get the current location
+ var currentLocation = window.pageYOffset;
+
+ // Check if the end location has been reached yet (or we've hit the end of the document)
+ if (position == endLocation || currentLocation == endLocation || ((startLocation < endLocation && window.innerHeight + currentLocation) >= documentHeight)) {
+
+ // Clear the animation timer
+ smoothScroll.cancelScroll(true);
+
+ // Bring the anchored element into focus
+ adjustFocus(anchor, endLocation, isNum);
+
+ // Emit a custom event
+ emitEvent('scrollStop', _settings, anchor, toggle);
+
+ // Reset start
+ start = null;
+ animationInterval = null;
+
+ return true;
+
+ }
+ };
+
+ /**
+ * Loop scrolling animation
+ */
+ var loopAnimateScroll = function (timestamp) {
+ if (!start) { start = timestamp; }
+ timeLapsed += timestamp - start;
+ percentage = speed === 0 ? 0 : (timeLapsed / speed);
+ percentage = (percentage > 1) ? 1 : percentage;
+ position = startLocation + (distance * easingPattern(_settings, percentage));
+ window.scrollTo(0, Math.floor(position));
+ if (!stopAnimateScroll(position, endLocation)) {
+ animationInterval = window.requestAnimationFrame(loopAnimateScroll);
+ start = timestamp;
+ }
+ };
+
+ /**
+ * Reset position to fix weird iOS bug
+ * @link https://github.com/cferdinandi/smooth-scroll/issues/45
+ */
+ if (window.pageYOffset === 0) {
+ window.scrollTo(0, 0);
+ }
+
+ // Update the URL
+ updateURL(anchor, isNum, _settings);
+
+ // Emit a custom event
+ emitEvent('scrollStart', _settings, anchor, toggle);
+
+ // Start scrolling animation
+ smoothScroll.cancelScroll(true);
+ window.requestAnimationFrame(loopAnimateScroll);
+
+ };
+
+ /**
+ * If smooth scroll element clicked, animate scroll
+ */
+ var clickHandler = function (event) {
+
+ // Don't run if the user prefers reduced motion
+ if (reduceMotion(settings)) return;
+
+ // Don't run if right-click or command/control + click
+ if (event.button !== 0 || event.metaKey || event.ctrlKey) return;
+
+ // Check if event.target has closest() method
+ // By @totegi - https://github.com/cferdinandi/smooth-scroll/pull/401/
+ if(!('closest' in event.target))return;
+
+ // Check if a smooth scroll link was clicked
+ toggle = event.target.closest(selector);
+ if (!toggle || toggle.tagName.toLowerCase() !== 'a' || event.target.closest(settings.ignore)) return;
+
+ // Only run if link is an anchor and points to the current page
+ if (toggle.hostname !== window.location.hostname || toggle.pathname !== window.location.pathname || !/#/.test(toggle.href)) return;
+
+ // Get an escaped version of the hash
+ var hash = escapeCharacters(toggle.hash);
+
+ // Get the anchored element
+ var anchor = settings.topOnEmptyHash && hash === '#' ? document.documentElement : document.querySelector(hash);
+ anchor = !anchor && hash === '#top' ? document.documentElement : anchor;
+
+ // If anchored element exists, scroll to it
+ if (!anchor) return;
+ event.preventDefault();
+ setHistory(settings);
+ smoothScroll.animateScroll(anchor, toggle);
+
+ };
+
+ /**
+ * Animate scroll on popstate events
+ */
+ var popstateHandler = function (event) {
+
+ // Stop if history.state doesn't exist (ex. if clicking on a broken anchor link).
+ // fixes `Cannot read property 'smoothScroll' of null` error getting thrown.
+ if (history.state === null) return;
+
+ // Only run if state is a popstate record for this instantiation
+ if (!history.state.smoothScroll || history.state.smoothScroll !== JSON.stringify(settings)) return;
+
+ // Only run if state includes an anchor
+
+ // if (!history.state.anchor && history.state.anchor !== 0) return;
+
+ // Get the anchor
+ var anchor = history.state.anchor;
+ if (typeof anchor === 'string' && anchor) {
+ anchor = document.querySelector(escapeCharacters(history.state.anchor));
+ if (!anchor) return;
+ }
+
+ // Animate scroll to anchor link
+ smoothScroll.animateScroll(anchor, null, {updateURL: false});
+
+ };
+
+ /**
+ * Destroy the current initialization.
+ */
+ smoothScroll.destroy = function () {
+
+ // If plugin isn't already initialized, stop
+ if (!settings) return;
+
+ // Remove event listeners
+ document.removeEventListener('click', clickHandler, false);
+ window.removeEventListener('popstate', popstateHandler, false);
+
+ // Cancel any scrolls-in-progress
+ smoothScroll.cancelScroll();
+
+ // Reset variables
+ settings = null;
+ anchor = null;
+ toggle = null;
+ fixedHeader = null;
+ eventTimeout = null;
+ animationInterval = null;
+
+ };
+
+ /**
+ * Initialize Smooth Scroll
+ * @param {Object} options User settings
+ */
+ smoothScroll.init = function (options) {
+
+ // feature test
+ if (!supports()) throw 'Smooth Scroll: This browser does not support the required JavaScript methods and browser APIs.';
+
+ // Destroy any existing initializations
+ smoothScroll.destroy();
+
+ // Selectors and variables
+ settings = extend(defaults, options || {}); // Merge user options with defaults
+ fixedHeader = settings.header ? document.querySelector(settings.header) : null; // Get the fixed header
+
+ // When a toggle is clicked, run the click handler
+ document.addEventListener('click', clickHandler, false);
+
+ // If updateURL and popState are enabled, listen for pop events
+ if (settings.updateURL && settings.popstate) {
+ window.addEventListener('popstate', popstateHandler, false);
+ }
+
+ };
+
+
+ //
+ // Initialize plugin
+ //
+
+ smoothScroll.init(options);
+
+
+ //
+ // Public APIs
+ //
+
+ return smoothScroll;
+
+ };
+
+ return SmoothScroll;
+
+}));