You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmonero/html/js/webflow.js

3711 lines
119 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*!
* ----------------------------------------------------------------------
* Webflow: Front-end site library
* @license MIT
* Other scripts may access this api using an async handler:
* var Webflow = Webflow || [];
* Webflow.push(readyFunction);
* ----------------------------------------------------------------------
*/
var Webflow = { w: Webflow };
Webflow.init = function() {
'use strict';
var $ = window.$;
var api = {};
var modules = {};
var primary = [];
var secondary = this.w || [];
var $win = $(window);
var _ = api._ = underscore();
var domready = false;
var tram = window.tram;
var Modernizr = window.Modernizr;
var noop = function() {};
tram.config.hideBackface = false;
tram.config.keepInherited = true;
/**
* Webflow.define() - Define a webflow.js module
* @param {string} name
* @param {function} factory
*/
api.define = function(name, factory) {
var module = modules[name] = factory($, _);
if (!module) return;
// If running in Webflow app, subscribe to design/preview events
if (api.env()) {
$.isFunction(module.design) && window.addEventListener('__wf_design', module.design);
$.isFunction(module.preview) && window.addEventListener('__wf_preview', module.preview);
}
// Subscribe to module front-end events
$.isFunction(module.destroy) && $win.on('__wf_destroy', module.destroy);
// Look for a ready method on module
if (module.ready && $.isFunction(module.ready)) {
// If domready has already happened, call ready method
if (domready) module.ready();
// Otherwise push ready method into primary queue
else primary.push(module.ready);
}
};
/**
* Webflow.require() - Load a Webflow.js module
* @param {string} name
* @return {object}
*/
api.require = function(name) {
return modules[name];
};
/**
* Webflow.push() - Add a ready handler into secondary queue
* @param {function} ready Callback to invoke on domready
*/
api.push = function(ready) {
// If domready has already happened, invoke handler
if (domready) {
$.isFunction(ready) && ready();
return;
}
// Otherwise push into secondary queue
secondary.push(ready);
};
/**
* Webflow.env() - Get the state of the Webflow app
* @param {string} mode [optional]
* @return {boolean}
*/
api.env = function(mode) {
var designFlag = window.__wf_design;
var inApp = typeof designFlag != 'undefined';
if (!mode) return inApp;
if (mode == 'design') return inApp && designFlag;
if (mode == 'preview') return inApp && !designFlag;
if (mode == 'slug') return inApp && window.__wf_slug;
if (mode == 'editor') return window.WebflowEditor;
};
// Feature detects + browser sniffs ಠ_ಠ
var userAgent = navigator.userAgent.toLowerCase();
var appVersion = navigator.appVersion.toLowerCase();
api.env.touch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch;
var chrome = api.env.chrome = /chrome/.test(userAgent) && /Google/.test(navigator.vendor) && parseInt(appVersion.match(/chrome\/(\d+)\./)[1], 10);
var ios = api.env.ios = Modernizr && Modernizr.ios;
api.env.safari = /safari/.test(userAgent) && !chrome && !ios;
/**
* Webflow.resize, Webflow.scroll - throttled event proxies
*/
var resizeEvents = 'resize.webflow orientationchange.webflow load.webflow';
var scrollEvents = 'scroll.webflow ' + resizeEvents;
api.resize = eventProxy($win, resizeEvents);
api.scroll = eventProxy($win, scrollEvents);
api.redraw = eventProxy();
// Create a proxy instance for throttled events
function eventProxy(target, types) {
// Set up throttled method (using custom frame-based _.throttle)
var handlers = [];
var proxy = {};
proxy.up = _.throttle(function(evt) {
_.each(handlers, function(h) { h(evt); });
});
// Bind events to target
if (target && types) target.on(types, proxy.up);
/**
* Add an event handler
* @param {function} handler
*/
proxy.on = function(handler) {
if (typeof handler != 'function') return;
if (_.contains(handlers, handler)) return;
handlers.push(handler);
};
/**
* Remove an event handler
* @param {function} handler
*/
proxy.off = function(handler) {
handlers = _.filter(handlers, function(h) {
return h !== handler;
});
};
return proxy;
}
// Provide optional IX events to components
api.ixEvents = function() {
var ix = api.require('ix');
return (ix && ix.events) || {
reset: noop,
intro: noop,
outro: noop
};
};
// Webflow.location() - Wrap window.location in api
api.location = function(url) {
window.location = url;
};
// Webflow.app - Designer-specific methods
api.app = api.env() ? {} : null;
if (api.app) {
// Trigger redraw for specific elements
var Event = window.Event;
var redraw = new Event('__wf_redraw');
api.app.redrawElement = function(i, el) { el.dispatchEvent(redraw); };
// Webflow.location - Re-route location change to trigger an event
api.location = function(url) {
window.dispatchEvent(new CustomEvent('__wf_location', { detail: url }));
};
}
// Webflow.ready() - Call primary and secondary handlers
api.ready = function() {
domready = true;
$.each(primary.concat(secondary), function(index, value) {
$.isFunction(value) && value();
});
// Trigger resize
api.resize.up();
};
/**
* Webflow.load() - Add a window load handler that will run even if load event has already happened
* @param {function} handler
*/
var deferLoad;
api.load = function(handler) {
deferLoad.then(handler);
};
function bindLoad() {
// Reject any previous deferred (to support destroy)
if (deferLoad) {
deferLoad.reject();
$win.off('load', deferLoad.resolve);
}
// Create deferred and bind window load event
deferLoad = new $.Deferred();
$win.on('load', deferLoad.resolve);
}
// Webflow.destroy() - Trigger a cleanup event for all modules
api.destroy = function() {
$win.triggerHandler('__wf_destroy');
// If load event has not yet fired, replace the deferred
if (deferLoad.state() == 'pending') bindLoad();
};
// Listen for domready
$(api.ready);
// Listen for window.onload and resolve deferred
bindLoad();
/*!
* Webflow._ (aka) Underscore.js 1.6.0 (custom build)
* _.each
* _.map
* _.find
* _.filter
* _.any
* _.contains
* _.delay
* _.defer
* _.throttle (webflow)
* _.debounce
* _.keys
* _.has
* _.now
*
* http://underscorejs.org
* (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Underscore may be freely distributed under the MIT license.
*/
function underscore() {
var _ = {};
// Current version.
_.VERSION = '1.6.0-Webflow';
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
/* jshint shadow:true */
if (obj == null) return obj;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
return obj;
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var result;
any(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
each(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) results.push(value);
});
return results;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
each(obj, function(value, index, list) {
if (result || (result = predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
return value === target;
});
};
// Function (ahem) Functions
// --------------------
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered once every
// browser animation frame - using tram's requestAnimationFrame polyfill.
_.throttle = function(func) {
var wait, args, context;
return function() {
if (wait) return;
wait = true;
args = arguments;
context = this;
tram.frame(function() {
wait = false;
func.apply(context, args);
});
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
var last = _.now() - timestamp;
if (last < wait) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
if (!timeout) {
timeout = setTimeout(later, wait);
}
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = function(obj) {
if (!_.isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Utility Functions
// -----------------
// A (possibly faster) way to get the current timestamp as an integer.
_.now = Date.now || function() { return new Date().getTime(); };
// Export underscore
return _;
}
// Export api
Webflow = api;
};
/*!
* ----------------------------------------------------------------------
* Webflow: 3rd party plugins
*/
/* jshint ignore:start */
/*!
* tram.js v0.8.1-global
* Cross-browser CSS3 transitions in JavaScript
* https://github.com/bkwld/tram
* MIT License
*/
window.tram=function(a){function b(a,b){var c=new L.Bare;return c.init(a,b)}function c(a){return a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()})}function d(a){var b=parseInt(a.slice(1),16),c=b>>16&255,d=b>>8&255,e=255&b;return[c,d,e]}function e(a,b,c){return"#"+(1<<24|a<<16|b<<8|c).toString(16).slice(1)}function f(){}function g(a,b){_("Type warning: Expected: ["+a+"] Got: ["+typeof b+"] "+b)}function h(a,b,c){_("Units do not match ["+a+"]: "+b+", "+c)}function i(a,b,c){if(void 0!==b&&(c=b),void 0===a)return c;var d=c;return Z.test(a)||!$.test(a)?d=parseInt(a,10):$.test(a)&&(d=1e3*parseFloat(a)),0>d&&(d=0),d===d?d:c}function j(a){for(var b=-1,c=a?a.length:0,d=[];++b<c;){var e=a[b];e&&d.push(e)}return d}var k=function(a,b,c){function d(a){return"object"==typeof a}function e(a){return"function"==typeof a}function f(){}function g(h,i){function j(){var a=new k;return e(a.init)&&a.init.apply(a,arguments),a}function k(){}i===c&&(i=h,h=Object),j.Bare=k;var l,m=f[a]=h[a],n=k[a]=j[a]=new f;return n.constructor=j,j.mixin=function(b){return k[a]=j[a]=g(j,b)[a],j},j.open=function(a){if(l={},e(a)?l=a.call(j,n,m,j,h):d(a)&&(l=a),d(l))for(var c in l)b.call(l,c)&&(n[c]=l[c]);return e(n.init)||(n.init=h),j},j.open(i)}return g}("prototype",{}.hasOwnProperty),l={ease:["ease",function(a,b,c,d){var e=(a/=d)*a,f=e*a;return b+c*(-2.75*f*e+11*e*e+-15.5*f+8*e+.25*a)}],"ease-in":["ease-in",function(a,b,c,d){var e=(a/=d)*a,f=e*a;return b+c*(-1*f*e+3*e*e+-3*f+2*e)}],"ease-out":["ease-out",function(a,b,c,d){var e=(a/=d)*a,f=e*a;return b+c*(.3*f*e+-1.6*e*e+2.2*f+-1.8*e+1.9*a)}],"ease-in-out":["ease-in-out",function(a,b,c,d){var e=(a/=d)*a,f=e*a;return b+c*(2*f*e+-5*e*e+2*f+2*e)}],linear:["linear",function(a,b,c,d){return c*a/d+b}],"ease-in-quad":["cubic-bezier(0.550, 0.085, 0.680, 0.530)",function(a,b,c,d){return c*(a/=d)*a+b}],"ease-out-quad":["cubic-bezier(0.250, 0.460, 0.450, 0.940)",function(a,b,c,d){return-c*(a/=d)*(a-2)+b}],"ease-in-out-quad":["cubic-bezier(0.455, 0.030, 0.515, 0.955)",function(a,b,c,d){return(a/=d/2)<1?c/2*a*a+b:-c/2*(--a*(a-2)-1)+b}],"ease-in-cubic":["cubic-bezier(0.550, 0.055, 0.675, 0.190)",function(a,b,c,d){return c*(a/=d)*a*a+b}],"ease-out-cubic":["cubic-bezier(0.215, 0.610, 0.355, 1)",function(a,b,c,d){return c*((a=a/d-1)*a*a+1)+b}],"ease-in-out-cubic":["cubic-bezier(0.645, 0.045, 0.355, 1)",function(a,b,c,d){return(a/=d/2)<1?c/2*a*a*a+b:c/2*((a-=2)*a*a+2)+b}],"ease-in-quart":["cubic-bezier(0.895, 0.030, 0.685, 0.220)",function(a,b,c,d){return c*(a/=d)*a*a*a+b}],"ease-out-quart":["cubic-bezier(0.165, 0.840, 0.440, 1)",function(a,b,c,d){return-c*((a=a/d-1)*a*a*a-1)+b}],"ease-in-out-quart":["cubic-bezier(0.770, 0, 0.175, 1)",function(a,b,c,d){return(a/=d/2)<1?c/2*a*a*a*a+b:-c/2*((a-=2)*a*a*a-2)+b}],"ease-in-quint":["cubic-bezier(0.755, 0.050, 0.855, 0.060)",function(a,b,c,d){return c*(a/=d)*a*a*a*a+b}],"ease-out-quint":["cubic-bezier(0.230, 1, 0.320, 1)",function(a,b,c,d){return c*((a=a/d-1)*a*a*a*a+1)+b}],"ease-in-out-quint":["cubic-bezier(0.860, 0, 0.070, 1)",function(a,b,c,d){return(a/=d/2)<1?c/2*a*a*a*a*a+b:c/2*((a-=2)*a*a*a*a+2)+b}],"ease-in-sine":["cubic-bezier(0.470, 0, 0.745, 0.715)",function(a,b,c,d){return-c*Math.cos(a/d*(Math.PI/2))+c+b}],"ease-out-sine":["cubic-bezier(0.390, 0.575, 0.565, 1)",function(a,b,c,d){return c*Math.sin(a/d*(Math.PI/2))+b}],"ease-in-out-sine":["cubic-bezier(0.445, 0.050, 0.550, 0.950)",function(a,b,c,d){return-c/2*(Math.cos(Math.PI*a/d)-1)+b}],"ease-in-expo":["cubic-bezier(0.950, 0.050, 0.795, 0.035)",function(a,b,c,d){return 0===a?b:c*Math.pow(2,10*(a/d-1))+b}],"ease-out-expo":["cubic-bezier(0.190, 1, 0.220, 1)",function(a,b,c,d){return a===d?b+c:c*(-Math.pow(2,-10*a/d)+1)+b}],"ease-in-out-expo":["cubic-bezier(1, 0, 0, 1)",function(a,b,c,d){return 0===a?b:a===d?b+c:(a/=d/2)<1?c/2*Math.pow(2,10*(a-1))+b:c/2*(-Math.pow(2,-10*--a)+2)+b}],"ease-in-circ":["cubic-bezier(0.600, 0.040, 0.980, 0.335)",function(a,b,c,d){return-c*(Math.sqrt(1-(a/=d)*a)-1)+b}],"ease-out-circ":["cubic-bezier(0.075, 0.820, 0.165, 1)",function(a,b,c,d){return c*Math.sqrt(1-(a=a/d-1)*a)+b}],"ease-in-out-circ":["cubic-bezier(0.785, 0.135, 0.150, 0.860)",function(a,b,c,d){return(a/=d/2)<1?-c/2*(Math.sqrt(1-a*a)-1)+b:c/2*(Math.sqrt(1-(a-=2)*a)+1)+b}],"ease-in-back":["cubic-bezier(0.600, -0.280, 0.735, 0.045)",function(a,b,c,d,e){return void 0===e&&(e=1.70158),c*(a/=d)*a*((e+1)*a-e)+b}],"ease-out-back":["cubic-bezier(0.175, 0.885, 0.320, 1.275)",function(a,b,c,d,e){return void 0===e&&(e=1.70158),c*((a=a/d-1)*a*((e+1)*a+e)+1)+b}],"ease-in-out-back":["cubic-bezier(0.680, -0.550, 0.265, 1.550)",function(a,b,c,d,e){return void 0===e&&(e=1.70158),(a/=d/2)<1?c/2*a*a*(((e*=1.525)+1)*a-e)+b:c/2*((a-=2)*a*(((e*=1.525)+1)*a+e)+2)+b}]},m={"ease-in-back":"cubic-bezier(0.600, 0, 0.735, 0.045)","ease-out-back":"cubic-bezier(0.175, 0.885, 0.320, 1)","ease-in-out-back":"cubic-bezier(0.680, 0, 0.265, 1)"},n=document,o=window,p="bkwld-tram",q=/[\-\.0-9]/g,r=/[A-Z]/,s="number",t=/^(rgb|#)/,u=/(em|cm|mm|in|pt|pc|px)$/,v=/(em|cm|mm|in|pt|pc|px|%)$/,w=/(deg|rad|turn)$/,x="unitless",y=/(all|none) 0s ease 0s/,z=/^(width|height)$/,A=" ",B=n.createElement("a"),C=["Webkit","Moz","O","ms"],D=["-webkit-","-moz-","-o-","-ms-"],E=function(a){if(a in B.style)return{dom:a,css:a};var b,c,d="",e=a.split("-");for(b=0;b<e.length;b++)d+=e[b].charAt(0).toUpperCase()+e[b].slice(1);for(b=0;b<C.length;b++)if(c=C[b]+d,c in B.style)return{dom:c,css:D[b]+a}},F=b.support={bind:Function.prototype.bind,transform:E("transform"),transition:E("transition"),backface:E("backface-visibility"),timing:E("transition-timing-function")};if(F.transition){var G=F.timing.dom;if(B.style[G]=l["ease-in-back"][0],!B.style[G])for(var H in m)l[H][0]=m[H]}var I=b.frame=function(){var a=o.requestAnimationFrame||o.webkitRequestAnimationFrame||o.mozRequestAnimationFrame||o.oRequestAnimationFrame||o.msRequestAnimationFrame;return a&&F.bind?a.bind(o):function(a){o.setTimeout(a,16)}}(),J=b.now=function(){var a=o.performance,b=a&&(a.now||a.webkitNow||a.msNow||a.mozNow);return b&&F.bind?b.bind(a):Date.now||function(){return+new Date}}(),K=k(function(b){function d(a,b){var c=j((""+a).split(A)),d=c[0];b=b||{};var e=X[d];if(!e)return _("Unsupported property: "+d);if(!b.weak||!this.props[d]){var f=e[0],g=this.props[d];return g||(g=this.props[d]=new f.Bare),g.init(this.$el,c,e,b),g}}function e(a,b,c){if(a){var e=typeof a;if(b||(this.timer&&this.timer.destroy(),this.queue=[],this.active=!1),"number"==e&&b)return this.timer=new R({duration:a,context:this,complete:h}),void(this.active=!0);if("string"==e&&b){switch(a){case"hide":n.call(this);break;case"stop":k.call(this);break;case"redraw":o.call(this);break;default:d.call(this,a,c&&c[1])}return h.call(this)}if("function"==e)return void a.call(this,this);if("object"==e){var f=0;t.call(this,a,function(a,b){a.span>f&&(f=a.span),a.stop(),a.animate(b)},function(a){"wait"in a&&(f=i(a.wait,0))}),s.call(this),f>0&&(this.timer=new R({duration:f,context:this}),this.active=!0,b&&(this.timer.complete=h));var g=this,j=!1,l={};I(function(){t.call(g,a,function(a){a.active&&(j=!0,l[a.name]=a.nextStyle)}),j&&g.$el.css(l)})}}}function f(a){a=i(a,0),this.active?this.queue.push({options:a}):(this.timer=new R({duration:a,context:this,complete:h}),this.active=!0)}function g(a){return this.active?(this.queue.push({options:a,args:arguments}),void(this.timer.complete=h)):_("No active transition timer. Use start() or wait() before then().")}function h(){if(this.timer&&this.timer.destroy(),this.active=!1,this.queue.length){var a=this.queue.shift();e.call(this,a.options,!0,a.args)}}function k(a){this.timer&&this.timer.destroy(),this.queue=[],this.active=!1;var b;"string"==typeof a?(b={},b[a]=1):b="object"==typeof a&&null!=a?a:this.props,t.call(this,b,u),s.call(this)}function l(a){k.call(this,a),t.call(this,a,v,w)}function m(a){"string"!=typeof a&&(a="block"),this.el.style.display=a}function n(){k.call(this),this.el.style.display="none"}function o(){this.el.offsetHeight}function q(){k.call(this),a.removeData(this.el,p),this.$el=this.el=null}function s(){var a,b,c=[];this.upstream&&c.push(this.upstream);for(a in this.props)b=this.props[a],b.active&&c.push(b.string);c=c.join(","),this.style!==c&&(this.style=c,this.el.style[F.transition.dom]=c)}function t(a,b,e){var f,g,h,i,j=b!==u,k={};for(f in a)h=a[f],f in Y?(k.transform||(k.transform={}),k.transform[f]=h):(r.test(f)&&(f=c(f)),f in X?k[f]=h:(i||(i={}),i[f]=h));for(f in k){if(h=k[f],g=this.props[f],!g){if(!j)continue;g=d.call(this,f)}b.call(this,g,h)}e&&i&&e.call(this,i)}function u(a){a.stop()}function v(a,b){a.set(b)}function w(a){this.$el.css(a)}function x(a,c){b[a]=function(){return this.children?z.call(this,c,arguments):(this.el&&c.apply(this,arguments),this)}}function z(a,b){var c,d=this.children.length;for(c=0;d>c;c++)a.apply(this.children[c],b);return this}b.init=function(b){if(this.$el=a(b),this.el=this.$el[0],this.props={},this.queue=[],this.style="",this.active=!1,T.keepInherited&&!T.fallback){var c=V(this.el,"transition");c&&!y.test(c)&&(this.upstream=c)}F.backface&&T.hideBackface&&U(this.el,F.backface.css,"hidden")},x("add",d),x("start",e),x("wait",f),x("then",g),x("next",h),x("stop",k),x("set",l),x("show",m),x("hide",n),x("redraw",o),x("destroy",q)}),L=k(K,function(b){function c(b,c){var d=a.data(b,p)||a.data(b,p,new K.Bare);return d.el||d.init(b),c?d.start(c):d}b.init=function(b,d){var e=a(b);if(!e.length)return this;if(1===e.length)return c(e[0],d);var f=[];return e.each(function(a,b){f.push(c(b,d))}),this.children=f,this}}),M=k(function(a){function b(){var a=this.get();this.update("auto");var b=this.get();return this.update(a),b}function c(a,b,c){return void 0!==b&&(c=b),a in l?a:c}function d(a){var b=/rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(a);return(b?e(b[1],b[2],b[3]):a).replace(/#(\w)(\w)(\w)$/,"#$1$1$2$2$3$3")}var f={duration:500,ease:"ease",delay:0};a.init=function(a,b,d,e){this.$el=a,this.el=a[0];var g=b[0];d[2]&&(g=d[2]),W[g]&&(g=W[g]),this.name=g,this.type=d[1],this.duration=i(b[1],this.duration,f.duration),this.ease=c(b[2],this.ease,f.ease),this.delay=i(b[3],this.delay,f.delay),this.span=this.duration+this.delay,this.active=!1,this.nextStyle=null,this.auto=z.test(this.name),this.unit=e.unit||this.unit||T.defaultUnit,this.angle=e.angle||this.angle||T.defaultAngle,T.fallback||e.fallback?this.animate=this.fallback:(this.animate=this.transition,this.string=this.name+A+this.duration+"ms"+("ease"!=this.ease?A+l[this.ease][0]:"")+(this.delay?A+this.delay+"ms":""))},a.set=function(a){a=this.convert(a,this.type),this.update(a),this.redraw()},a.transition=function(a){this.active=!0,a=this.convert(a,this.type),this.auto&&("auto"==this.el.style[this.name]&&(this.update(this.get()),this.redraw()),"auto"==a&&(a=b.call(this))),this.nextStyle=a},a.fallback=function(a){var c=this.el.style[this.name]||this.convert(this.get(),this.type);a=this.convert(a,this.type),this.auto&&("auto"==c&&(c=this.convert(this.get(),this.type)),"auto"==a&&(a=b.call(this))),this.tween=new Q({from:c,to:a,duration:this.duration,delay:this.delay,ease:this.ease,update:this.update,context:this})},a.get=function(){return V(this.el,this.name)},a.update=function(a){U(this.el,this.name,a)},a.stop=function(){(this.active||this.nextStyle)&&(this.active=!1,this.nextStyle=null,U(this.el,this.name,this.get()));var a=this.tween;a&&a.context&&a.destroy()},a.convert=function(a,b){if("auto"==a&&this.auto)return a;var c,e="number"==typeof a,f="string"==typeof a;switch(b){case s:if(e)return a;if(f&&""===a.replace(q,""))return+a;c="number(unitless)";break;case t:if(f){if(""===a&&this.original)return this.original;if(b.test(a))return"#"==a.charAt(0)&&7==a.length?a:d(a)}c="hex or rgb string";break;case u:if(e)return a+this.unit;if(f&&b.test(a))return a;c="number(px) or string(unit)";break;case v:if(e)return a+this.unit;if(f&&b.test(a))return a;c="number(px) or string(unit or %)";break;case w:if(e)return a+this.angle;if(f&&b.test(a))return a;c="number(deg) or string(angle)";break;case x:if(e)return a;if(f&&v.test(a))return a;c="number(unitless) or string(unit or %)"}return g(c,a),a},a.redraw=function(){this.el.offsetHeight}}),N=k(M,function(a,b){a.init=function(){b.init.apply(this,arguments),this.original||(this.original=this.convert(this.get(),t))}}),O=k(M,function(a,b){a.init=function(){b.init.apply(this,arguments),this.animate=this.fallback},a.get=function(){return this.$el[this.name]()},a.update=function(a){this.$el[this.name](a)}}),P=k(M,function(a,b){function c(a,b){var c,d,e,f,g;for(c in a)f=Y[c],e=f[0],d=f[1]||c,g=this.convert(a[c],e),b.call(this,d,g,e)}a.init=function(){b.init.apply(this,arguments),this.current||(this.current={},Y.perspective&&T.perspective&&(this.current.perspective=T.perspective,U(this.el,this.name,this.style(this.current)),this.redraw()))},a.set=function(a){c.call(this,a,function(a,b){this.current[a]=b}),U(this.el,this.name,this.style(this.current)),this.redraw()},a.transition=function(a){var b=this.values(a);this.tween=new S({current:this.current,values:b,duration:this.duration,delay:this.delay,ease:this.ease});var c,d={};for(c in this.current)d[c]=c in b?b[c]:this.current[c];this.active=!0,this.nextStyle=this.style(d)},a.fallback=function(a){var b=this.values(a);this.tween=new S({current:this.current,values:b,duration:this.duration,delay:this.delay,ease:this.ease,update:this.update,context:this})},a.update=function(){U(this.el,this.name,this.style(this.current))},a.style=function(a){var b,c="";for(b in a)c+=b+"("+a[b]+") ";return c},a.values=function(a){var b,d={};return c.call(this,a,function(a,c,e){d[a]=c,void 0===this.current[a]&&(b=0,~a.indexOf("scale")&&(b=1),this.current[a]=this.convert(b,e))}),d}}),Q=k(function(b){function c(a){1===n.push(a)&&I(g)}function g(){var a,b,c,d=n.length;if(d)for(I(g),b=J(),a=d;a--;)c=n[a],c&&c.render(b)}function i(b){var c,d=a.inArray(b,n);d>=0&&(c=n.slice(d+1),n.length=d,c.length&&(n=n.concat(c)))}function j(a){return Math.round(a*o)/o}function k(a,b,c){return e(a[0]+c*(b[0]-a[0]),a[1]+c*(b[1]-a[1]),a[2]+c*(b[2]-a[2]))}var m={ease:l.ease[1],from:0,to:1};b.init=function(a){this.duration=a.duration||0,this.delay=a.delay||0;var b=a.ease||m.ease;l[b]&&(b=l[b][1]),"function"!=typeof b&&(b=m.ease),this.ease=b,this.update=a.update||f,this.complete=a.complete||f,this.context=a.context||this,this.name=a.name;var c=a.from,d=a.to;void 0===c&&(c=m.from),void 0===d&&(d=m.to),this.unit=a.unit||"","number"==typeof c&&"number"==typeof d?(this.begin=c,this.change=d-c):this.format(d,c),this.value=this.begin+this.unit,this.start=J(),a.autoplay!==!1&&this.play()},b.play=function(){this.active||(this.start||(this.start=J()),this.active=!0,c(this))},b.stop=function(){this.active&&(this.active=!1,i(this))},b.render=function(a){var b,c=a-this.start;if(this.delay){if(c<=this.delay)return;c-=this.delay}if(c<this.duration){var d=this.ease(c,0,1,this.duration);return b=this.startRGB?k(this.startRGB,this.endRGB,d):j(this.begin+d*this.change),this.value=b+this.unit,void this.update.call(this.context,this.value)}b=this.endHex||this.begin+this.change,this.value=b+this.unit,this.update.call(this.context,this.value),this.complete.call(this.context),this.destroy()},b.format=function(a,b){if(b+="",a+="","#"==a.charAt(0))return this.startRGB=d(b),this.endRGB=d(a),this.endHex=a,this.begin=0,void(this.change=1);if(!this.unit){var c=b.replace(q,""),e=a.replace(q,"");c!==e&&h("tween",b,a),this.unit=c}b=parseFloat(b),a=parseFloat(a),this.begin=this.value=b,this.change=a-b},b.destroy=function(){this.stop(),this.context=null,this.ease=this.update=this.complete=f};var n=[],o=1e3}),R=k(Q,function(a){a.init=function(a){this.duration=a.duration||0,this.complete=a.complete||f,this.context=a.context,this.play()},a.render=function(a){var b=a-this.start;b<this.duration||(this.complete.call(this.context),this.destroy())}}),S=k(Q,function(a,b){a.init=function(a){this.context=a.context,this.update=a.update,this.tweens=[],this.current=a.current;var b,c;for(b in a.values)c=a.values[b],this.current[b]!==c&&this.tweens.push(new Q({name:b,from:this.current[b],to:c,duration:a.duration,delay:a.delay,ease:a.ease,autoplay:!1}));this.play()},a.render=function(a){var b,c,d=this.tweens.length,e=!1;for(b=d;b--;)c=this.tweens[b],c.context&&(c.render(a),this.current[c.name]=c.value,e=!0);return e?void(this.update&&this.update.call(this.context)):this.destroy()},a.destroy=function(){if(b.destroy.call(this),this.tweens){var a,c=this.tweens.length;for(a=c;a--;)this.tweens[a].destroy();this.tweens=null,this.current=null}}}),T=b.config={defaultUnit:"px",defaultAngle:"deg",keepInherited:!1,hideBackface:!1,perspective:"",fallback:!F.transition,agentTests:[]};b.fallback=function(a){if(!F.transition)return T.fallback=!0;T.agentTests.push("("+a+")");var b=new RegExp(T.agentTests.join("|"),"i");T.fallback=b.test(navigator.userAgent)},b.fallback("6.0.[2-5] Safari"),b.tween=function(a){return new Q(a)},b.delay=function(a,b,c){return new R({complete:b,duration:a,context:c})},a.fn.tram=function(a){return b.call(null,this,a)};var U=a.style,V=a.css,W={transform:F.transform&&F.transform.css},X={color:[N,t],background:[N,t,"background-color"],"outline-color":[N,t],"border-color":[N,t],"border-top-color":[N,t],"border-right-color":[N,t],"border-bottom-color":[N,t],"border-left-color":[N,t],"border-width":[M,u],"border-top-width":[M,u],"border-right-width":[M,u],"border-bottom-width":[M,u],"border-left-width":[M,u],"border-spacing":[M,u],"letter-spacing":[M,u],margin:[M,u],"margin-top":[M,u],"margin-right":[M,u],"margin-bottom":[M,u],"margin-left":[M,u],padding:[M,u],"padding-top":[M,u],"padding-right":[M,u],"padding-bottom":[M,u],"padding-left":[M,u],"outline-width":[M,u],opacity:[M,s],top:[M,v],right:[M,v],bottom:[M,v],left:[M,v],"font-size":[M,v],"text-indent":[M,v],"word-spacing":[M,v],width:[M,v],"min-width":[M,v],"max-width":[M,v],height:[M,v],"min-height":[M,v],"max-height":[M,v],"line-height":[M,x],"scroll-top":[O,s,"scrollTop"],"scroll-left":[O,s,"scrollLeft"]},Y={};F.transform&&(X.transform=[P],Y={x:[v,"translateX"],y:[v,"translateY"],rotate:[w],rotateX:[w],rotateY:[w],scale:[s],scaleX:[s],scaleY:[s],skew:[w],skewX:[w],skewY:[w]}),F.transform&&F.backface&&(Y.z=[v,"translateZ"],Y.rotateZ=[w],Y.scaleZ=[s],Y.perspective=[u]);var Z=/ms/,$=/s|\./,_=function(){var a="warn",b=window.console;return b&&b[a]?function(c){b[a](c)}:f}();return a.tram=b}(window.jQuery);
/*!
* jQuery-ajaxTransport-XDomainRequest - v1.0.3 - 2014-06-06
* https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
* Copyright (c) 2014 Jason Moon (@JSONMOON)
* Licensed MIT (/blob/master/LICENSE.txt)
*/
(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else if(typeof exports==='object'){module.exports=a(require('jquery'))}else{a(jQuery)}}(function($){if($.support.cors||!$.ajaxTransport||!window.XDomainRequest){return}var n=/^https?:\/\//i;var o=/^get|post$/i;var p=new RegExp('^'+location.protocol,'i');$.ajaxTransport('* text html xml json',function(j,k,l){if(!j.crossDomain||!j.async||!o.test(j.type)||!n.test(j.url)||!p.test(j.url)){return}var m=null;return{send:function(f,g){var h='';var i=(k.dataType||'').toLowerCase();m=new XDomainRequest();if(/^\d+$/.test(k.timeout)){m.timeout=k.timeout}m.ontimeout=function(){g(500,'timeout')};m.onload=function(){var a='Content-Length: '+m.responseText.length+'\r\nContent-Type: '+m.contentType;var b={code:200,message:'success'};var c={text:m.responseText};try{if(i==='html'||/text\/html/i.test(m.contentType)){c.html=m.responseText}else if(i==='json'||(i!=='text'&&/\/json/i.test(m.contentType))){try{c.json=$.parseJSON(m.responseText)}catch(e){b.code=500;b.message='parseerror'}}else if(i==='xml'||(i!=='text'&&/\/xml/i.test(m.contentType))){var d=new ActiveXObject('Microsoft.XMLDOM');d.async=false;try{d.loadXML(m.responseText)}catch(e){d=undefined}if(!d||!d.documentElement||d.getElementsByTagName('parsererror').length){b.code=500;b.message='parseerror';throw'Invalid XML: '+m.responseText;}c.xml=d}}catch(parseMessage){throw parseMessage;}finally{g(b.code,b.message,c,a)}};m.onprogress=function(){};m.onerror=function(){g(500,'error',{text:m.responseText})};if(k.data){h=($.type(k.data)==='string')?k.data:$.param(k.data)}m.open(j.type,j.url);m.send(h)},abort:function(){if(m){m.abort()}}}})}));
/* jshint ignore:end */
/**
* ----------------------------------------------------------------------
* Init lib after plugins
*/
Webflow.init();
/**
* ----------------------------------------------------------------------
* Webflow: Interactions
*/
Webflow.define('ix', function($, _) {
'use strict';
var api = {};
var designer;
var $win = $(window);
var namespace = '.w-ix';
var tram = window.tram;
var env = Webflow.env;
var ios = env.ios;
var inApp = env();
var emptyFix = env.chrome && env.chrome < 35;
var transNone = 'none 0s ease 0s';
var introEvent = 'w-ix-intro' + namespace;
var outroEvent = 'w-ix-outro' + namespace;
var fallbackProps = /width|height/;
var eventQueue = [];
var $subs = $();
var config = {};
var anchors = [];
var loads = [];
var readys = [];
var unique = 0;
var destroyed;
var store;
// Component types and proxy selectors
var components = {
tabs: '.w-tab-link, .w-tab-pane',
dropdown: '.w-dropdown',
slider: '.w-slide',
navbar: '.w-nav'
};
// -----------------------------------
// Module methods
api.init = function(list) {
setTimeout(function() { configure(list); }, 1);
};
api.preview = function() {
designer = false;
setTimeout(function() { configure(window.__wf_ix); }, 1);
};
api.design = function() {
designer = true;
api.destroy();
};
api.destroy = function() {
destroyed = true;
$subs.each(teardown);
Webflow.scroll.off(scroll);
asyncEvents();
anchors = [];
loads = [];
readys = [];
};
api.ready = function() {
// Ready should only be used after destroy, as a way to re-init
if (config && destroyed) {
destroyed = false;
init();
}
};
api.run = run;
api.events = {};
api.style = inApp ? styleApp : stylePub;
// -----------------------------------
// Private methods
function configure(list) {
if (!list) return;
store = {};
// Map all interactions to a hash using slug as key.
config = {};
_.each(list, function(item) {
config[item.slug] = item.value;
});
// Init ix after config
init();
}
function init() {
// Build each element's interaction keying from data attribute
var els = $('[data-ix]');
if (!els.length) return;
els.each(teardown);
els.each(build);
// Listen for scroll events if any anchors exist
if (anchors.length) {
Webflow.scroll.on(scroll);
setTimeout(scroll, 1);
}
// Handle loads or readys if they exist
if (loads.length) Webflow.load(runLoads);
if (readys.length) setTimeout(runReadys, 1);
// Trigger queued events, must happen after init
initEvents();
}
function build(i, el) {
var $el = $(el);
var id = $el.attr('data-ix');
var ix = config[id];
if (!ix) return;
var triggers = ix.triggers;
if (!triggers) return;
var state = store[id] || (store[id] = {});
// Set initial styles, unless we detect an iOS device + any non-iOS triggers
var setStyles = !(ios && _.any(triggers, isNonIOS));
if (setStyles) api.style($el, ix.style);
_.each(triggers, function(trigger) {
var type = trigger.type;
var stepsB = trigger.stepsB && trigger.stepsB.length;
function runA() { run(trigger, $el, { group: 'A' }); }
function runB() { run(trigger, $el, { group: 'B' }); }
if (type == 'load') {
(trigger.preload && !inApp) ? loads.push(runA) : readys.push(runA);
return;
}
if (type == 'click') {
var stateKey = 'click:' + unique++;
if (trigger.descend) stateKey += ':descend';
if (trigger.siblings) stateKey += ':siblings';
if (trigger.selector) stateKey += ':' + trigger.selector;
$el.on('click' + namespace, function(evt) {
if ($el.attr('href') === '#') evt.preventDefault();
run(trigger, $el, { group: state[stateKey] ? 'B' : 'A' });
if (stepsB) state[stateKey] = !state[stateKey];
});
$subs = $subs.add($el);
return;
}
if (type == 'hover') {
$el.on('mouseenter' + namespace, runA);
$el.on('mouseleave' + namespace, runB);
$subs = $subs.add($el);
return;
}
// Check for a component proxy selector
var proxy = components[type];
if (proxy) {
var $proxy = $el.closest(proxy);
$proxy.on(introEvent, runA).on(outroEvent, runB);
$subs = $subs.add($proxy);
return;
}
// Ignore the following triggers on iOS devices
if (ios) return;
if (type == 'scroll') {
anchors.push({
el: $el, trigger: trigger, state: { active: false },
offsetTop: convert(trigger.offsetTop),
offsetBot: convert(trigger.offsetBot)
});
return;
}
});
}
function isNonIOS(trigger) {
return trigger.type == 'scroll';
}
function convert(offset) {
if (!offset) return 0;
offset = offset + '';
var result = parseInt(offset, 10);
if (result !== result) return 0;
if (offset.indexOf('%') > 0) {
result = result / 100;
if (result >= 1) result = 0.999;
}
return result;
}
function teardown(i, el) {
$(el).off(namespace);
}
function scroll() {
var viewTop = $win.scrollTop();
var viewHeight = $win.height();
// Check each anchor for a valid scroll trigger
var count = anchors.length;
for (var i = 0; i < count; i++) {
var anchor = anchors[i];
var $el = anchor.el;
var trigger = anchor.trigger;
var stepsB = trigger.stepsB && trigger.stepsB.length;
var state = anchor.state;
var top = $el.offset().top;
var height = $el.outerHeight();
var offsetTop = anchor.offsetTop;
var offsetBot = anchor.offsetBot;
if (offsetTop < 1 && offsetTop > 0) offsetTop *= viewHeight;
if (offsetBot < 1 && offsetBot > 0) offsetBot *= viewHeight;
var active = (top + height - offsetTop >= viewTop && top + offsetBot <= viewTop + viewHeight);
if (active === state.active) continue;
if (active === false && !stepsB) continue;
state.active = active;
run(trigger, $el, { group: active ? 'A' : 'B' });
}
}
function runLoads() {
var count = loads.length;
for (var i = 0; i < count; i++) {
loads[i]();
}
}
function runReadys() {
var count = readys.length;
for (var i = 0; i < count; i++) {
readys[i]();
}
}
function run(trigger, $el, opts, replay) {
opts = opts || {};
var done = opts.done;
// Do not run in designer unless forced
if (designer && !opts.force) return;
// Operate on a set of grouped steps
var group = opts.group || 'A';
var loop = trigger['loop' + group];
var steps = trigger['steps' + group];
if (!steps || !steps.length) return;
if (steps.length < 2) loop = false;
// One-time init before any loops
if (!replay) {
// Find selector within element descendants, siblings, or query whole document
var selector = trigger.selector;
if (selector) {
$el = (
trigger.descend ? $el.find(selector) :
trigger.siblings ? $el.siblings(selector) :
$(selector)
);
if (inApp) $el.attr('data-ix-affect', 1);
}
// Apply empty fix for certain Chrome versions
if (emptyFix) $el.addClass('w-ix-emptyfix');
}
var _tram = tram($el);
// Add steps
var meta = {};
for (var i = 0; i < steps.length; i++) {
addStep(_tram, steps[i], meta);
}
function fin() {
// Run trigger again if looped
if (loop) return run(trigger, $el, opts, true);
// Reset any 'auto' values
if (meta.width == 'auto') _tram.set({ width: 'auto' });
if (meta.height == 'auto') _tram.set({ height: 'auto' });
// Run callback
done && done();
}
// Add final step to queue if tram has started
meta.start ? _tram.then(fin) : fin();
}
function addStep(_tram, step, meta) {
var addMethod = 'add';
var startMethod = 'start';
// Once the transition has started, we will always use then() to add to the queue.
if (meta.start) addMethod = startMethod = 'then';
// Parse transitions string on the current step
var transitions = step.transition;
if (transitions) {
transitions = transitions.split(',');
for (var i = 0; i < transitions.length; i++) {
var transition = transitions[i];
var options = fallbackProps.test(transition) ? { fallback: true } : null;
_tram[addMethod](transition, options);
}
}
// Build a clean object to pass to the tram method
var clean = tramify(step) || {};
// Store last width and height values
if (clean.width != null) meta.width = clean.width;
if (clean.height != null) meta.height = clean.height;
// When transitions are not present, set values immediately and continue queue.
if (transitions == null) {
// If we have started, wrap set() in then() and reset queue
if (meta.start) {
_tram.then(function() {
var queue = this.queue;
this.set(clean);
if (clean.display) {
_tram.redraw();
Webflow.redraw.up();
}
this.queue = queue;
this.next();
});
} else {
_tram.set(clean);
// Always redraw after setting display
if (clean.display) {
_tram.redraw();
Webflow.redraw.up();
}
}
// Use the wait() method to kick off queue in absence of transitions.
var wait = clean.wait;
if (wait != null) {
_tram.wait(wait);
meta.start = true;
}
// Otherwise, when transitions are present
} else {
// If display is present, handle it separately
if (clean.display) {
var display = clean.display;
delete clean.display;
// If we've already started, we need to wrap it in a then()
if (meta.start) {
_tram.then(function() {
var queue = this.queue;
this.set({ display: display }).redraw();
Webflow.redraw.up();
this.queue = queue;
this.next();
});
} else {
_tram.set({ display: display }).redraw();
Webflow.redraw.up();
}
}
// Otherwise, start a transition using the current start method.
_tram[startMethod](clean);
meta.start = true;
}
}
// (In app) Set styles immediately and manage upstream transition
function styleApp(el, data) {
var _tram = tram(el);
// Get computed transition value
el.css('transition', '');
var computed = el.css('transition');
// If computed is disabled, clear upstream
if (computed === transNone) computed = _tram.upstream = null;
// Disable upstream temporarily
_tram.upstream = transNone;
// Set values immediately
_tram.set(tramify(data));
// Only restore upstream in preview mode
_tram.upstream = computed;
}
// (Published) Set styles immediately on specified jquery element
function stylePub(el, data) {
tram(el).set(tramify(data));
}
// Build a clean object for tram
function tramify(obj) {
var result = {};
var found = false;
for (var x in obj) {
if (x === 'transition') continue;
result[x] = obj[x];
found = true;
}
// If empty, return null for tram.set/stop compliance
return found ? result : null;
}
// Events used by other webflow modules
var events = {
reset: function(i, el) {
el.__wf_intro = null;
},
intro: function(i, el) {
if (el.__wf_intro) return;
el.__wf_intro = true;
$(el).triggerHandler(introEvent);
},
outro: function(i, el) {
if (!el.__wf_intro) return;
el.__wf_intro = null;
$(el).triggerHandler(outroEvent);
}
};
// Trigger events in queue + point to sync methods
function initEvents() {
var count = eventQueue.length;
for (var i = 0; i < count; i++) {
var memo = eventQueue[i];
memo[0](0, memo[1]);
}
eventQueue = [];
$.extend(api.events, events);
}
// Replace events with async methods prior to init
function asyncEvents() {
_.each(events, function(func, name) {
api.events[name] = function(i, el) {
eventQueue.push([func, el]);
};
});
}
asyncEvents();
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Touch events
*/
Webflow.define('touch', function($, _) {
'use strict';
var api = {};
var fallback = !document.addEventListener;
var getSelection = window.getSelection;
// Fallback to click events in old IE
if (fallback) {
$.event.special.tap = { bindType: 'click', delegateType: 'click' };
}
api.init = function(el) {
if (fallback) return null;
el = typeof el === 'string' ? $(el).get(0) : el;
return el ? new Touch(el) : null;
};
function Touch(el) {
var active = false;
var dirty = false;
var useTouch = false;
var thresholdX = Math.min(Math.round(window.innerWidth * 0.04), 40);
var startX, startY, lastX;
el.addEventListener('touchstart', start, false);
el.addEventListener('touchmove', move, false);
el.addEventListener('touchend', end, false);
el.addEventListener('touchcancel', cancel, false);
el.addEventListener('mousedown', start, false);
el.addEventListener('mousemove', move, false);
el.addEventListener('mouseup', end, false);
el.addEventListener('mouseout', cancel, false);
function start(evt) {
// We dont handle multi-touch events yet.
var touches = evt.touches;
if (touches && touches.length > 1) {
return;
}
active = true;
dirty = false;
if (touches) {
useTouch = true;
startX = touches[0].clientX;
startY = touches[0].clientY;
} else {
startX = evt.clientX;
startY = evt.clientY;
}
lastX = startX;
}
function move(evt) {
if (!active) return;
if (useTouch && evt.type === 'mousemove') {
evt.preventDefault();
evt.stopPropagation();
return;
}
var touches = evt.touches;
var x = touches ? touches[0].clientX : evt.clientX;
var y = touches ? touches[0].clientY : evt.clientY;
var velocityX = x - lastX;
lastX = x;
// Allow swipes while pointer is down, but prevent them during text selection
if (Math.abs(velocityX) > thresholdX && getSelection && getSelection() + '' === '') {
triggerEvent('swipe', evt, { direction: velocityX > 0 ? 'right' : 'left' });
cancel();
}
// If pointer moves more than 10px flag to cancel tap
if (Math.abs(x - startX) > 10 || Math.abs(y - startY) > 10) {
dirty = true;
}
}
function end(evt) {
if (!active) return;
active = false;
if (useTouch && evt.type === 'mouseup') {
evt.preventDefault();
evt.stopPropagation();
useTouch = false;
return;
}
if (!dirty) triggerEvent('tap', evt);
}
function cancel(evt) {
active = false;
}
function destroy() {
el.removeEventListener('touchstart', start, false);
el.removeEventListener('touchmove', move, false);
el.removeEventListener('touchend', end, false);
el.removeEventListener('touchcancel', cancel, false);
el.removeEventListener('mousedown', start, false);
el.removeEventListener('mousemove', move, false);
el.removeEventListener('mouseup', end, false);
el.removeEventListener('mouseout', cancel, false);
el = null;
}
// Public instance methods
this.destroy = destroy;
}
// Wrap native event to supoprt preventdefault + stopPropagation
function triggerEvent(type, evt, data) {
var newEvent = $.Event(type, { originalEvent: evt });
$(evt.target).trigger(newEvent, data);
}
// Listen for touch events on all nodes by default.
api.instance = api.init(document);
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Forms
*/
Webflow.define('forms', function($, _) {
'use strict';
var api = {};
var FORM_API_HOST = 'https://webflow.com';
var FORM_SUBMIT_HOST = 'https://webflow.com';
var FORM_OLDIE_HOST = 'http://formdata.webflow.com';
var $doc = $(document);
var $forms;
var loc = window.location;
var retro = window.XDomainRequest && !window.atob;
var namespace = '.w-form';
var siteId;
var emailField = /e(\-)?mail/i;
var emailValue = /^\S+@\S+$/;
var alert = window.alert;
var listening;
// MailChimp domains: list-manage.com + mirrors
var chimpRegex = /list-manage[1-9]?.com/i;
api.ready = function() {
// Init forms
init();
// Wire document events once
if (!listening) addListeners();
};
api.preview = api.design = function() {
init();
};
function init() {
siteId = $('html').attr('data-wf-site');
$forms = $(namespace + ' form');
if (!$forms.length) return;
$forms.each(build);
}
function build(i, el) {
// Store form state using namespace
var $el = $(el);
var data = $.data(el, namespace);
if (!data) data = $.data(el, namespace, { form: $el }); // data.form
reset(data);
var wrap = $el.closest('div.w-form');
data.done = wrap.find('> .w-form-done');
data.fail = wrap.find('> .w-form-fail');
var action = data.action = $el.attr('action');
data.handler = null;
data.redirect = $el.attr('data-redirect');
// MailChimp form
if (chimpRegex.test(action)) { data.handler = submitMailChimp; return; }
// Custom form action
if (action) return;
// Webflow form
if (siteId) { data.handler = submitWebflow; return; }
// Alert for disconnected Webflow forms
disconnected();
}
function addListeners() {
listening = true;
// Handle form submission for Webflow forms
$doc.on('submit', namespace + ' form', function(evt) {
var data = $.data(this, namespace);
if (data.handler) {
data.evt = evt;
data.handler(data);
}
});
}
// Reset data common to all submit handlers
function reset(data) {
var btn = data.btn = data.form.find(':input[type="submit"]');
data.wait = data.btn.attr('data-wait') || null;
data.success = false;
btn.prop('disabled', false);
data.label && btn.val(data.label);
}
// Disable submit button
function disableBtn(data) {
var btn = data.btn;
var wait = data.wait;
btn.prop('disabled', true);
// Show wait text and store previous label
if (wait) {
data.label = btn.val();
btn.val(wait);
}
}
// Find form fields, validate, and set value pairs
function findFields(form, result) {
var status = null;
result = result || {};
// The ":input" selector is a jQuery shortcut to select all inputs, selects, textareas
form.find(':input:not([type="submit"])').each(function(i, el) {
var field = $(el);
var type = field.attr('type');
var name = field.attr('data-name') || field.attr('name') || ('Field ' + (i + 1));
var value = field.val();
if (type == 'checkbox') {
value = field.is(':checked');
} if (type == 'radio') {
// Radio group value already processed
if (result[name] === null || typeof result[name] == 'string') {
return;
}
value = form.find('input[name="' + field.attr('name') + '"]:checked').val() || null;
}
if (typeof value == 'string') value = $.trim(value);
result[name] = value;
status = status || getStatus(field, name, value);
});
return status;
}
function getStatus(field, name, value) {
var status = null;
if (!field.attr('required')) return null;
if (!value) status = 'Please fill out the required field: ' + name;
else if (emailField.test(name) || emailField.test(field.attr('type'))) {
if (!emailValue.test(value)) status = 'Please enter a valid email address for: ' + name;
}
return status;
}
// Submit form to Webflow
function submitWebflow(data) {
reset(data);
var form = data.form;
var payload = {
name: form.attr('data-name') || form.attr('name') || 'Untitled Form',
source: loc.href,
test: Webflow.env(),
fields: {}
};
preventDefault(data);
// Find & populate all fields
var status = findFields(form, payload.fields);
if (status) return alert(status);
// Disable submit button
disableBtn(data);
// Read site ID
// NOTE: If this site is exported, the HTML tag must retain the data-wf-site attribute for forms to work
if (!siteId) { afterSubmit(data); return; }
var url = FORM_API_HOST + '/api/v1/form/' + siteId;
// Work around same-protocol IE XDR limitation - without this IE9 and below forms won't submit
if (retro && url.indexOf(FORM_SUBMIT_HOST) >= 0) {
url = url.replace(FORM_SUBMIT_HOST, FORM_OLDIE_HOST);
}
$.ajax({
url: url,
type: 'POST',
data: payload,
dataType: 'json',
crossDomain: true
}).done(function() {
data.success = true;
afterSubmit(data);
}).fail(function() {
afterSubmit(data);
});
}
// Submit form to MailChimp
function submitMailChimp(data) {
reset(data);
var form = data.form;
var payload = {};
// Skip Ajax submission if http/s mismatch, fallback to POST instead
if (/^https/.test(loc.href) && !/^https/.test(data.action)) {
form.attr('method', 'post');
return;
}
preventDefault(data);
// Find & populate all fields
var status = findFields(form, payload);
if (status) return alert(status);
// Disable submit button
disableBtn(data);
// Use special format for MailChimp params
var fullName;
_.each(payload, function(value, key) {
if (emailField.test(key)) payload.EMAIL = value;
if (/^((full[ _-]?)?name)$/i.test(key)) fullName = value;
if (/^(first[ _-]?name)$/i.test(key)) payload.FNAME = value;
if (/^(last[ _-]?name)$/i.test(key)) payload.LNAME = value;
});
if (fullName && !payload.FNAME) {
fullName = fullName.split(' ');
payload.FNAME = fullName[0];
payload.LNAME = payload.LNAME || fullName[1];
}
// Use the (undocumented) MailChimp jsonp api
var url = data.action.replace('/post?', '/post-json?') + '&c=?';
// Add special param to prevent bot signups
var userId = url.indexOf('u=')+2;
userId = url.substring(userId, url.indexOf('&', userId));
var listId = url.indexOf('id=')+3;
listId = url.substring(listId, url.indexOf('&', listId));
payload['b_' + userId + '_' + listId] = '';
$.ajax({
url: url,
data: payload,
dataType: 'jsonp'
}).done(function(resp) {
data.success = (resp.result == 'success' || /already/.test(resp.msg));
if (!data.success) console.info('MailChimp error: ' + resp.msg);
afterSubmit(data);
}).fail(function() {
afterSubmit(data);
});
}
// Common callback which runs after all Ajax submissions
function afterSubmit(data) {
var form = data.form;
var wrap = form.closest('div.w-form');
var redirect = data.redirect;
var success = data.success;
// Redirect to a success url if defined
if (success && redirect) {
Webflow.location(redirect);
return;
}
// Show or hide status divs
data.done.toggle(success);
data.fail.toggle(!success);
// Hide form on success
form.toggle(!success);
// Reset data and enable submit button
reset(data);
}
function preventDefault(data) {
data.evt && data.evt.preventDefault();
data.evt = null;
}
var disconnected = _.debounce(function() {
alert('Oops! This page has a form that is powered by Webflow, but important code was removed that is required to make the form work. Please contact support@webflow.com to fix this issue.');
}, 100);
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Maps widget
*/
Webflow.define('maps', function($, _) {
'use strict';
var api = {};
var $doc = $(document);
var google = null;
var $maps;
var namespace = '.w-widget-map';
// -----------------------------------
// Module methods
api.ready = function() {
// Init Maps on the front-end
if (!Webflow.env()) initMaps();
};
api.preview = function() {
// Update active map nodes
$maps = $doc.find(namespace);
// Listen for resize events
Webflow.resize.off(triggerRedraw);
if ($maps.length) {
Webflow.resize.on(triggerRedraw);
triggerRedraw();
}
};
api.design = function(evt) {
// Update active map nodes
$maps = $doc.find(namespace);
// Stop listening for resize events
Webflow.resize.off(triggerRedraw);
// Redraw to account for page changes
$maps.length && _.defer(triggerRedraw);
};
api.destroy = removeListeners;
// -----------------------------------
// Private methods
// Trigger redraw in designer or preview mode
function triggerRedraw() {
if ($maps.length && Webflow.app) {
$maps.each(Webflow.app.redrawElement);
}
}
function initMaps() {
$maps = $doc.find(namespace);
if (!$maps.length) return;
if (google === null) {
$.getScript('https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=_wf_maps_loaded');
window._wf_maps_loaded = mapsLoaded;
} else {
mapsLoaded();
}
function mapsLoaded() {
window._wf_maps_loaded = function() {};
google = window.google;
$maps.each(renderMap);
removeListeners();
addListeners();
}
}
function removeListeners() {
Webflow.resize.off(resizeMaps);
Webflow.redraw.off(resizeMaps);
}
function addListeners() {
Webflow.resize.on(resizeMaps);
Webflow.redraw.on(resizeMaps);
}
// Render map onto each element
function renderMap(i, el) {
var data = $(el).data();
getState(el, data);
}
function resizeMaps() {
$maps.each(resizeMap);
}
// Resize map when window changes
function resizeMap(i, el) {
var state = getState(el);
google.maps.event.trigger(state.map, 'resize');
state.setMapPosition();
}
// Store state on element data
var store = 'w-widget-map';
function getState(el, data) {
var state = $.data(el, store);
if (state) return state;
var $el = $(el);
state = $.data(el, store, {
// Default options
latLng: '51.511214,-0.119824',
tooltip: '',
style: 'roadmap',
zoom: 12,
// Marker
marker: new google.maps.Marker({
draggable: false
}),
// Tooltip infowindow
infowindow: new google.maps.InfoWindow({
disableAutoPan: true
})
});
// LatLng center point
var latLng = data.widgetLatlng || state.latLng;
state.latLng = latLng;
var coords = latLng.split(',');
var latLngObj = new google.maps.LatLng(coords[0], coords[1]);
state.latLngObj = latLngObj;
// Disable touch events
var mapDraggable = (Webflow.env.touch && data.disableTouch) ? false : true;
// Map instance
state.map = new google.maps.Map(el, {
center: state.latLngObj,
zoom: state.zoom,
maxZoom: 18,
mapTypeControl: false,
panControl: false,
streetViewControl: false,
scrollwheel: !data.disableScroll,
draggable: mapDraggable,
zoomControl: true,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL
},
mapTypeId: state.style
});
state.marker.setMap(state.map);
// Set map position and offset
state.setMapPosition = function() {
state.map.setCenter(state.latLngObj);
var offsetX = 0;
var offsetY = 0;
var padding = $el.css(['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft']);
offsetX -= parseInt(padding.paddingLeft, 10);
offsetX += parseInt(padding.paddingRight, 10);
offsetY -= parseInt(padding.paddingTop, 10);
offsetY += parseInt(padding.paddingBottom, 10);
if (offsetX || offsetY) {
state.map.panBy(offsetX, offsetY);
}
$el.css('position', ''); // Remove injected position
};
// Fix position after first tiles have loaded
google.maps.event.addListener(state.map, 'tilesloaded', function() {
google.maps.event.clearListeners(state.map, 'tilesloaded');
state.setMapPosition();
});
// Set initial position
state.setMapPosition();
state.marker.setPosition(state.latLngObj);
state.infowindow.setPosition(state.latLngObj);
// Draw tooltip
var tooltip = data.widgetTooltip;
if (tooltip) {
state.tooltip = tooltip;
state.infowindow.setContent(tooltip);
if (!state.infowindowOpen) {
state.infowindow.open(state.map, state.marker);
state.infowindowOpen = true;
}
}
// Map style - options.style
var style = data.widgetStyle;
if (style) {
state.map.setMapTypeId(style);
}
// Zoom - options.zoom
var zoom = data.widgetZoom;
if (zoom != null) {
state.zoom = zoom;
state.map.setZoom(+zoom);
}
// Click marker to open in google maps
google.maps.event.addListener(state.marker, 'click', function() {
window.open('https://maps.google.com/?z=' + state.zoom + '&daddr=' + state.latLng);
});
return state;
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Google+ widget
*/
Webflow.define('gplus', function($) {
'use strict';
var $doc = $(document);
var api = {};
var loaded;
api.ready = function() {
// Load Google+ API on the front-end
if (!Webflow.env() && !loaded) init();
};
function init() {
$doc.find('.w-widget-gplus').length && load();
}
function load() {
loaded = true;
$.getScript('https://apis.google.com/js/plusone.js');
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Smooth scroll
*/
Webflow.define('scroll', function($) {
'use strict';
var $doc = $(document);
var win = window;
var loc = win.location;
var history = win.history;
var validHash = /^[a-zA-Z][\w:.-]*$/;
function ready() {
// If hash is already present on page load, scroll to it right away
if (loc.hash) {
findEl(loc.hash.substring(1));
}
// When clicking on a link, check if it links to another part of the page
$doc.on('click', 'a', function(e) {
if (Webflow.env('design')) {
return;
}
// Ignore links being used by jQuery mobile
if (window.$.mobile && $(e.currentTarget).hasClass('ui-link')) return;
// Ignore empty # links
if (this.getAttribute('href') === '#') {
e.preventDefault();
return;
}
var hash = this.hash ? this.hash.substring(1) : null;
if (hash) {
findEl(hash, e);
}
});
}
function findEl(hash, e) {
if (!validHash.test(hash)) return;
var el = $('#' + hash);
if (!el.length) {
return;
}
if (e) {
e.preventDefault();
e.stopPropagation();
}
// Push new history state
if (loc.hash !== hash && history && history.pushState) {
var oldHash = history.state && history.state.hash;
if (oldHash !== hash) {
history.pushState({ hash: hash }, '', '#' + hash);
}
}
// If a fixed header exists, offset for the height
var header = $('header, body > .header, body > .w-nav');
var offset = header.css('position') === 'fixed' ? header.outerHeight() : 0;
win.setTimeout(function() {
scroll(el, offset);
}, e ? 0 : 300);
}
function scroll(el, offset){
var start = $(win).scrollTop();
var end = el.offset().top - offset;
// If specified, scroll so that the element ends up in the middle of the viewport
if (el.data('scroll') == 'mid') {
var available = $(win).height() - offset;
var elHeight = el.outerHeight();
if (elHeight < available) {
end -= Math.round((available - elHeight) / 2);
}
}
var mult = 1;
// Check for custom time multiplier on the body and the element
$('body').add(el).each(function(i) {
var time = parseFloat($(this).attr('data-scroll-time'), 10);
if (!isNaN(time) && (time === 0 || time > 0)) {
mult = time;
}
});
// Shim for IE8 and below
if (!Date.now) {
Date.now = function() { return new Date().getTime(); };
}
var clock = Date.now();
var animate = win.requestAnimationFrame || win.mozRequestAnimationFrame || win.webkitRequestAnimationFrame || function(fn) { win.setTimeout(fn, 15); };
var duration = (472.143 * Math.log(Math.abs(start - end) +125) - 2000) * mult;
var step = function() {
var elapsed = Date.now() - clock;
win.scroll(0, getY(start, end, elapsed, duration));
if (elapsed <= duration) {
animate(step);
}
};
step();
}
function getY(start, end, elapsed, duration) {
if (elapsed > duration) {
return end;
}
return start + (end - start) * ease(elapsed / duration);
}
function ease(t) {
return t<0.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1;
}
// Export module
return { ready: ready };
});
/**
* ----------------------------------------------------------------------
* Webflow: Auto-select links to current page or section
*/
Webflow.define('links', function($, _) {
'use strict';
var api = {};
var $win = $(window);
var designer;
var inApp = Webflow.env();
var location = window.location;
var linkCurrent = 'w--current';
var validHash = /^#[a-zA-Z][\w:.-]*$/;
var indexPage = /index\.(html|php)$/;
var dirList = /\/$/;
var anchors;
var slug;
// -----------------------------------
// Module methods
api.ready = api.design = api.preview = init;
// -----------------------------------
// Private methods
function init() {
designer = inApp && Webflow.env('design');
slug = Webflow.env('slug') || location.pathname || '';
// Reset scroll listener, init anchors
Webflow.scroll.off(scroll);
anchors = [];
// Test all links for a selectable href
var links = document.links;
for (var i = 0; i < links.length; ++i) {
select(links[i]);
}
// Listen for scroll if any anchors exist
if (anchors.length) {
Webflow.scroll.on(scroll);
scroll();
}
}
function select(link) {
var href = link.getAttribute('href');
// Ignore any hrefs with a colon to safely avoid all uri schemes
if (href.indexOf(':') >= 0) return;
var $link = $(link);
// Check for valid hash links w/ sections and use scroll anchor
if (href.indexOf('#') === 0 && validHash.test(href)) {
// Ignore #edit anchors
if (href === '#edit') return;
var $section = $(href);
$section.length && anchors.push({ link: $link, sec: $section, active: false });
return;
}
// Ignore empty # links
if (href === '#') return;
// Determine whether the link should be selected
var match = (link.href === location.href) || (href === slug) || (indexPage.test(href) && dirList.test(slug));
setClass($link, linkCurrent, match);
}
function scroll() {
var viewTop = $win.scrollTop();
var viewHeight = $win.height();
// Check each anchor for a section in view
_.each(anchors, function(anchor) {
var $link = anchor.link;
var $section = anchor.sec;
var top = $section.offset().top;
var height = $section.outerHeight();
var offset = viewHeight * 0.5;
var active = ($section.is(':visible') &&
top + height - offset >= viewTop &&
top + offset <= viewTop + viewHeight);
if (anchor.active === active) return;
anchor.active = active;
setClass($link, linkCurrent, active);
if (designer) $link[0].__wf_current = active;
});
}
function setClass($elem, className, add) {
var exists = $elem.hasClass(className);
if (add && exists) return;
if (!add && !exists) return;
add ? $elem.addClass(className) : $elem.removeClass(className);
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Slider component
*/
Webflow.define('slider', function($, _) {
'use strict';
var api = {};
var tram = window.tram;
var $doc = $(document);
var $sliders;
var designer;
var inApp = Webflow.env();
var namespace = '.w-slider';
var dot = '<div class="w-slider-dot" data-wf-ignore />';
var ix = Webflow.ixEvents();
var fallback;
var redraw;
// -----------------------------------
// Module methods
api.ready = function() {
init();
};
api.design = function() {
designer = true;
init();
};
api.preview = function() {
designer = false;
init();
};
api.redraw = function() {
redraw = true;
init();
};
api.destroy = removeListeners;
// -----------------------------------
// Private methods
function init() {
// Find all sliders on the page
$sliders = $doc.find(namespace);
if (!$sliders.length) return;
$sliders.filter(':visible').each(build);
redraw = null;
if (fallback) return;
// Wire events
removeListeners();
addListeners();
}
function removeListeners() {
Webflow.resize.off(renderAll);
Webflow.redraw.off(api.redraw);
}
function addListeners() {
Webflow.resize.on(renderAll);
Webflow.redraw.on(api.redraw);
}
function renderAll() {
$sliders.filter(':visible').each(render);
}
function build(i, el) {
var $el = $(el);
// Store slider state in data
var data = $.data(el, namespace);
if (!data) data = $.data(el, namespace, {
index: 0,
depth: 1,
el: $el,
config: {}
});
data.mask = $el.children('.w-slider-mask');
data.left = $el.children('.w-slider-arrow-left');
data.right = $el.children('.w-slider-arrow-right');
data.nav = $el.children('.w-slider-nav');
data.slides = data.mask.children('.w-slide');
data.slides.each(ix.reset);
if (redraw) data.maskWidth = 0;
// Disable in old browsers
if (!tram.support.transform) {
data.left.hide();
data.right.hide();
data.nav.hide();
fallback = true;
return;
}
// Remove old events
data.el.off(namespace);
data.left.off(namespace);
data.right.off(namespace);
data.nav.off(namespace);
// Set config from data attributes
configure(data);
// Add events based on mode
if (designer) {
data.el.on('setting' + namespace, handler(data));
stopTimer(data);
data.hasTimer = false;
} else {
data.el.on('swipe' + namespace, handler(data));
data.left.on('tap' + namespace, previous(data));
data.right.on('tap' + namespace, next(data));
// Start timer if autoplay is true, only once
if (data.config.autoplay && !data.hasTimer) {
data.hasTimer = true;
data.timerCount = 1;
startTimer(data);
}
}
// Listen to nav events
data.nav.on('tap' + namespace, '> div', handler(data));
// Remove gaps from formatted html (for inline-blocks)
if (!inApp) {
data.mask.contents().filter(function() {
return this.nodeType === 3;
}).remove();
}
// Run first render
render(i, el);
}
function configure(data) {
var config = {};
config.crossOver = 0;
// Set config options from data attributes
config.animation = data.el.attr('data-animation') || 'slide';
if (config.animation == 'outin') {
config.animation = 'cross';
config.crossOver = 0.5;
}
config.easing = data.el.attr('data-easing') || 'ease';
var duration = data.el.attr('data-duration');
config.duration = duration != null ? +duration : 500;
if (+data.el.attr('data-infinite')) config.infinite = true;
if (+data.el.attr('data-hide-arrows')) {
config.hideArrows = true;
} else if (data.config.hideArrows) {
data.left.show();
data.right.show();
}
if (+data.el.attr('data-autoplay')) {
config.autoplay = true;
config.delay = +data.el.attr('data-delay') || 2000;
config.timerMax = +data.el.attr('data-autoplay-limit');
// Disable timer on first touch or mouse down
var touchEvents = 'mousedown' + namespace + ' touchstart' + namespace;
if (!designer) data.el.off(touchEvents).one(touchEvents, function() {
stopTimer(data);
});
}
// Use edge buffer to help calculate page count
var arrowWidth = data.right.width();
config.edge = arrowWidth ? arrowWidth + 40 : 100;
// Store config in data
data.config = config;
}
function previous(data) {
return function(evt) {
change(data, { index: data.index - 1, vector: -1 });
};
}
function next(data) {
return function(evt) {
change(data, { index: data.index + 1, vector: 1 });
};
}
function select(data, value) {
// Select page based on slide element index
var found = null;
if (value === data.slides.length) {
init(); layout(data); // Rebuild and find new slides
}
_.each(data.anchors, function(anchor, index) {
$(anchor.els).each(function(i, el) {
if ($(el).index() === value) found = index;
});
});
if (found != null) change(data, { index: found, immediate: true });
}
function startTimer(data) {
stopTimer(data);
var config = data.config;
var timerMax = config.timerMax;
if (timerMax && data.timerCount++ > timerMax) return;
data.timerId = window.setTimeout(function() {
if (data.timerId == null || designer) return;
next(data)();
startTimer(data);
}, config.delay);
}
function stopTimer(data) {
window.clearTimeout(data.timerId);
data.timerId = null;
}
function handler(data) {
return function(evt, options) {
options = options || {};
// Designer settings
if (designer && evt.type == 'setting') {
if (options.select == 'prev') return previous(data)();
if (options.select == 'next') return next(data)();
configure(data);
layout(data);
if (options.select == null) return;
select(data, options.select);
return;
}
// Swipe event
if (evt.type == 'swipe') {
if (Webflow.env('editor')) return;
if (options.direction == 'left') return next(data)();
if (options.direction == 'right') return previous(data)();
return;
}
// Page buttons
if (data.nav.has(evt.target).length) {
change(data, { index: $(evt.target).index() });
}
};
}
function change(data, options) {
options = options || {};
var config = data.config;
var anchors = data.anchors;
// Set new index
data.previous = data.index;
var index = options.index;
var shift = {};
if (index < 0) {
index = anchors.length-1;
if (config.infinite) {
// Shift first slide to the end
shift.x = -data.endX;
shift.from = 0;
shift.to = anchors[0].width;
}
} else if (index >= anchors.length) {
index = 0;
if (config.infinite) {
// Shift last slide to the start
shift.x = anchors[anchors.length-1].width;
shift.from = -anchors[anchors.length-1].x;
shift.to = shift.from - shift.x;
}
}
data.index = index;
// Select page nav
var active = data.nav.children().eq(data.index).addClass('w-active');
data.nav.children().not(active).removeClass('w-active');
// Hide arrows
if (config.hideArrows) {
data.index === anchors.length-1 ? data.right.hide() : data.right.show();
data.index === 0 ? data.left.hide() : data.left.show();
}
// Get page offset from anchors
var lastOffsetX = data.offsetX || 0;
var offsetX = data.offsetX = -anchors[data.index].x;
var resetConfig = { x: offsetX, opacity: 1, visibility: '' };
// Transition slides
var targets = $(anchors[data.index].els);
var previous = $(anchors[data.previous] && anchors[data.previous].els);
var others = data.slides.not(targets);
var animation = config.animation;
var easing = config.easing;
var duration = Math.round(config.duration);
var vector = options.vector || (data.index > data.previous ? 1 : -1);
var fadeRule = 'opacity ' + duration + 'ms ' + easing;
var slideRule = 'transform ' + duration + 'ms ' + easing;
// Trigger IX events
if (!designer) {
targets.each(ix.intro);
others.each(ix.outro);
}
// Set immediately after layout changes (but not during redraw)
if (options.immediate && !redraw) {
tram(targets).set(resetConfig);
resetOthers();
return;
}
// Exit early if index is unchanged
if (data.index == data.previous) return;
// Cross Fade / Out-In
if (animation == 'cross') {
var reduced = Math.round(duration - duration * config.crossOver);
var wait = Math.round(duration - reduced);
fadeRule = 'opacity ' + reduced + 'ms ' + easing;
tram(previous)
.set({ visibility: '' })
.add(fadeRule)
.start({ opacity: 0 });
tram(targets)
.set({ visibility: '', x: offsetX, opacity: 0, zIndex: data.depth++ })
.add(fadeRule)
.wait(wait)
.then({ opacity: 1 })
.then(resetOthers);
return;
}
// Fade Over
if (animation == 'fade') {
tram(previous)
.set({ visibility: '' })
.stop();
tram(targets)
.set({ visibility: '', x: offsetX, opacity: 0, zIndex: data.depth++ })
.add(fadeRule)
.start({ opacity: 1 })
.then(resetOthers);
return;
}
// Slide Over
if (animation == 'over') {
resetConfig = { x: data.endX };
tram(previous)
.set({ visibility: '' })
.stop();
tram(targets)
.set({ visibility: '', zIndex: data.depth++, x: offsetX + anchors[data.index].width * vector })
.add(slideRule)
.start({ x: offsetX })
.then(resetOthers);
return;
}
// Slide - infinite scroll
if (config.infinite && shift.x) {
tram(data.slides.not(previous))
.set({ visibility: '', x: shift.x })
.add(slideRule)
.start({ x: offsetX });
tram(previous)
.set({ visibility: '', x: shift.from })
.add(slideRule)
.start({ x: shift.to });
data.shifted = previous;
} else {
if (config.infinite && data.shifted) {
tram(data.shifted).set({ visibility: '', x: lastOffsetX });
data.shifted = null;
}
// Slide - basic scroll
tram(data.slides)
.set({ visibility: '' })
.add(slideRule)
.start({ x: offsetX });
}
// Helper to move others out of view
function resetOthers() {
var targets = $(anchors[data.index].els);
var others = data.slides.not(targets);
if (animation != 'slide') resetConfig.visibility = 'hidden';
tram(others).set(resetConfig);
}
}
function render(i, el) {
var data = $.data(el, namespace);
if (maskChanged(data)) return layout(data);
if (designer && slidesChanged(data)) layout(data);
}
function layout(data) {
// Determine page count from width of slides
var pages = 1;
var offset = 0;
var anchor = 0;
var width = 0;
var maskWidth = data.maskWidth;
var threshold = maskWidth - data.config.edge;
if (threshold < 0) threshold = 0;
data.anchors = [{ els: [], x: 0, width: 0 }];
data.slides.each(function(i, el) {
if (anchor - offset > threshold) {
pages++;
offset += maskWidth;
// Store page anchor for transition
data.anchors[pages-1] = { els: [], x: anchor, width: 0 };
}
// Set next anchor using current width + margin
width = $(el).outerWidth(true);
anchor += width;
data.anchors[pages-1].width += width;
data.anchors[pages-1].els.push(el);
});
data.endX = anchor;
// Build dots if nav exists and needs updating
if (designer) data.pages = null;
if (data.nav.length && data.pages !== pages){
data.pages = pages;
buildNav(data);
}
// Make sure index is still within range and call change handler
var index = data.index;
if (index >= pages) index = pages-1;
change(data, { immediate: true, index: index });
}
function buildNav(data) {
var dots = [];
var $dot;
var spacing = data.el.attr('data-nav-spacing');
if (spacing) spacing = parseFloat(spacing) + 'px';
for (var i=0; i<data.pages; i++) {
$dot = $(dot);
if (data.nav.hasClass('w-num')) $dot.text(i+1);
if (spacing != null) $dot.css({
'margin-left': spacing,
'margin-right': spacing
});
dots.push($dot);
}
data.nav.empty().append(dots);
}
function maskChanged(data) {
var maskWidth = data.mask.width();
if (data.maskWidth !== maskWidth) {
data.maskWidth = maskWidth;
return true;
}
return false;
}
function slidesChanged(data) {
var slidesWidth = 0;
data.slides.each(function(i, el) {
slidesWidth += $(el).outerWidth(true);
});
if (data.slidesWidth !== slidesWidth) {
data.slidesWidth = slidesWidth;
return true;
}
return false;
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Lightbox component
*/
var lightbox = (function (window, document, $, tram, undefined) {
'use strict';
var isArray = Array.isArray;
var namespace = 'w-lightbox';
var prefix = namespace + '-';
var prefixRegex = /(^|\s+)/g;
// Array of objects describing items to be displayed.
var items = [];
// Index of the currently displayed item.
var currentIndex;
// Object holding references to jQuery wrapped nodes.
var $refs;
// Instance of Spinner
var spinner;
function lightbox(thing, index) {
items = isArray(thing) ? thing : [thing];
if (!$refs) {
lightbox.build();
}
if (items.length > 1) {
$refs.items = $refs.empty;
items.forEach(function (item) {
var $thumbnail = dom('thumbnail');
var $item = dom('item').append($thumbnail);
$refs.items = $refs.items.add($item);
loadImage(item.thumbnailUrl || item.url, function ($image) {
if ($image.prop('width') > $image.prop('height')) {
addClass($image, 'wide');
}
else {
addClass($image, 'tall');
}
$thumbnail.append(addClass($image, 'thumbnail-image'));
});
});
$refs.strip.empty().append($refs.items);
addClass($refs.content, 'group');
}
tram(
// Focus the lightbox to receive keyboard events.
removeClass($refs.lightbox, 'hide').focus()
)
.add('opacity .3s')
.start({opacity: 1});
// Prevent document from scrolling while lightbox is active.
addClass($refs.html, 'noscroll');
return lightbox.show(index || 0);
}
/**
* Creates the DOM structure required by the lightbox.
*/
lightbox.build = function () {
// In case `build` is called more than once.
lightbox.destroy();
$refs = {
html: $(document.documentElement),
// Empty jQuery object can be used to build new ones using `.add`.
empty: $()
};
$refs.arrowLeft = dom('control left inactive');
$refs.arrowRight = dom('control right inactive');
$refs.close = dom('control close');
$refs.spinner = dom('spinner');
$refs.strip = dom('strip');
spinner = new Spinner($refs.spinner, prefixed('hide'));
$refs.content = dom('content')
.append($refs.spinner, $refs.arrowLeft, $refs.arrowRight, $refs.close);
$refs.container = dom('container')
.append($refs.content, $refs.strip);
$refs.lightbox = dom('backdrop hide')
.append($refs.container);
// We are delegating events for performance reasons and also
// to not have to reattach handlers when images change.
$refs.strip.on('tap', selector('item'), itemTapHandler);
$refs.content
.on('swipe', swipeHandler)
.on('tap', selector('left'), handlerPrev)
.on('tap', selector('right'), handlerNext)
.on('tap', selector('close'), handlerHide)
.on('tap', selector('image, caption'), handlerNext);
$refs.container
.on('tap', selector('view, strip'), handlerHide)
// Prevent images from being dragged around.
.on('dragstart', selector('img'), preventDefault);
$refs.lightbox
.on('keydown', keyHandler)
// IE loses focus to inner nodes without letting us know.
.on('focusin', focusThis);
// The `tabindex` attribute is needed to enable non-input elements
// to receive keyboard events.
$('body').append($refs.lightbox.prop('tabIndex', 0));
return lightbox;
};
/**
* Dispose of DOM nodes created by the lightbox.
*/
lightbox.destroy = function () {
if (!$refs) {
return;
}
// Event handlers are also removed.
$refs.lightbox.remove();
$refs = undefined;
};
/**
* Show a specific item.
*/
lightbox.show = function (index) {
// Bail if we are already showing this item.
if (index === currentIndex) {
return;
}
var item = items[index];
var previousIndex = currentIndex;
currentIndex = index;
spinner.show();
// For videos, load an empty SVG with the video dimensions to preserve
// the videos aspect ratio while being responsive.
var url = item.html && svgDataUri(item.width, item.height) || item.url;
loadImage(url, function ($image) {
// Make sure this is the last item requested to be shown since
// images can finish loading in a different order than they were
// requested in.
if (index != currentIndex) {
return;
}
var $figure = dom('figure', 'figure').append(addClass($image, 'image'));
var $frame = dom('frame').append($figure);
var $newView = dom('view').append($frame);
var $html, isIframe;
if (item.html) {
$html = $(item.html);
isIframe = $html.is('iframe');
if (isIframe) {
$html.on('load', transitionToNewView);
}
$figure.append(addClass($html, 'embed'));
}
if (item.caption) {
$figure.append(dom('caption', 'figcaption').text(item.caption));
}
$refs.spinner.before($newView);
if (!isIframe) {
transitionToNewView();
}
function transitionToNewView() {
spinner.hide();
if (index != currentIndex) {
$newView.remove();
return;
}
toggleClass($refs.arrowLeft, 'inactive', index <= 0);
toggleClass($refs.arrowRight, 'inactive', index >= items.length - 1);
if ($refs.view) {
tram($refs.view)
.add('opacity .3s')
.start({opacity: 0})
.then(remover($refs.view));
tram($newView)
.add('opacity .3s')
.add('transform .3s')
.set({x: index > previousIndex ? '80px' : '-80px'})
.start({opacity: 1, x: 0});
}
else {
$newView.css('opacity', 1);
}
$refs.view = $newView;
if ($refs.items) {
// Mark proper thumbnail as active
addClass(removeClass($refs.items, 'active').eq(index), 'active');
}
}
});
return lightbox;
};
/**
* Hides the lightbox.
*/
lightbox.hide = function () {
tram($refs.lightbox)
.add('opacity .3s')
.start({opacity: 0})
.then(hideLightbox);
return lightbox;
};
lightbox.prev = function () {
if (currentIndex > 0) {
lightbox.show(currentIndex - 1);
}
};
lightbox.next = function () {
if (currentIndex < items.length - 1) {
lightbox.show(currentIndex + 1);
}
};
function createHandler(action) {
return function (event) {
// We only care about events triggered directly on the bound selectors.
if (this != event.target) {
return;
}
event.stopPropagation();
event.preventDefault();
action();
};
}
var handlerPrev = createHandler(lightbox.prev);
var handlerNext = createHandler(lightbox.next);
var handlerHide = createHandler(lightbox.hide);
var itemTapHandler = function(event) {
var index = $(this).index();
event.preventDefault();
lightbox.show(index);
};
var swipeHandler = function (event, data) {
// Prevent scrolling.
event.preventDefault();
if (data.direction == 'left') {
lightbox.next();
}
else if (data.direction == 'right') {
lightbox.prev();
}
};
var focusThis = function () {
this.focus();
};
function preventDefault(event) {
event.preventDefault();
}
function keyHandler(event) {
var keyCode = event.keyCode;
// [esc]
if (keyCode == 27) {
lightbox.hide();
}
// [◀]
else if (keyCode == 37) {
lightbox.prev();
}
// [▶]
else if (keyCode == 39) {
lightbox.next();
}
}
function hideLightbox() {
removeClass($refs.html, 'noscroll');
addClass($refs.lightbox, 'hide');
$refs.strip.empty();
$refs.view && $refs.view.remove();
// Reset some stuff
removeClass($refs.content, 'group');
addClass($refs.arrowLeft, 'inactive');
addClass($refs.arrowRight, 'inactive');
currentIndex = $refs.view = undefined;
}
function loadImage(url, callback) {
var $image = dom('img', 'img');
$image.one('load', function () {
callback($image);
});
// Start loading image.
$image.attr('src', url);
return $image;
}
function remover($element) {
return function () {
$element.remove();
};
}
/**
* Spinner
*/
function Spinner($spinner, className, delay) {
this.$element = $spinner;
this.className = className;
this.delay = delay || 200;
this.hide();
}
Spinner.prototype.show = function () {
var spinner = this;
// Bail if we are already showing the spinner.
if (spinner.timeoutId) {
return;
}
spinner.timeoutId = setTimeout(function () {
spinner.$element.removeClass(spinner.className);
delete spinner.timeoutId;
}, spinner.delay);
};
Spinner.prototype.hide = function () {
var spinner = this;
if (spinner.timeoutId) {
clearTimeout(spinner.timeoutId);
delete spinner.timeoutId;
return;
}
spinner.$element.addClass(spinner.className);
};
function prefixed(string, isSelector) {
return string.replace(prefixRegex, (isSelector ? ' .' : ' ') + prefix);
}
function selector(string) {
return prefixed(string, true);
}
/**
* jQuery.addClass with auto-prefixing
* @param {jQuery} Element to add class to
* @param {string} Class name that will be prefixed and added to element
* @return {jQuery}
*/
function addClass($element, className) {
return $element.addClass(prefixed(className));
}
/**
* jQuery.removeClass with auto-prefixing
* @param {jQuery} Element to remove class from
* @param {string} Class name that will be prefixed and removed from element
* @return {jQuery}
*/
function removeClass($element, className) {
return $element.removeClass(prefixed(className));
}
/**
* jQuery.toggleClass with auto-prefixing
* @param {jQuery} Element where class will be toggled
* @param {string} Class name that will be prefixed and toggled
* @param {boolean} Optional boolean that determines if class will be added or removed
* @return {jQuery}
*/
function toggleClass($element, className, shouldAdd) {
return $element.toggleClass(prefixed(className), shouldAdd);
}
/**
* Create a new DOM element wrapped in a jQuery object,
* decorated with our custom methods.
* @param {string} className
* @param {string} [tag]
* @return {jQuery}
*/
function dom(className, tag) {
return addClass($(document.createElement(tag || 'div')), className);
}
function isObject(value) {
return typeof value == 'object' && null != value && !isArray(value);
}
function svgDataUri(width, height) {
var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '"/>';
return 'data:image/svg+xml;charset=utf-8,' + encodeURI(svg);
}
// Compute some dimensions manually for iOS, because of buggy support for VH.
// Also, Android built-in browser does not support viewport units.
(function () {
var ua = window.navigator.userAgent;
var iOS = /(iPhone|iPod|iPad).+AppleWebKit/i.test(ua);
var android = ua.indexOf('Android ') > -1 && ua.indexOf('Chrome') == -1;
if (!iOS && !android) {
return;
}
var styleNode = document.createElement('style');
document.head.appendChild(styleNode);
window.addEventListener('orientationchange', refresh, true);
function refresh() {
var vh = window.innerHeight;
var vw = window.innerWidth;
var content =
'.w-lightbox-content, .w-lightbox-view, .w-lightbox-view:before {' +
'height:' + vh + 'px' +
'}' +
'.w-lightbox-view {' +
'width:' + vw + 'px' +
'}' +
'.w-lightbox-group, .w-lightbox-group .w-lightbox-view, .w-lightbox-group .w-lightbox-view:before {' +
'height:' + (0.86 * vh) + 'px' +
'}' +
'.w-lightbox-image {' +
'max-width:' + vw + 'px;' +
'max-height:' + vh + 'px' +
'}' +
'.w-lightbox-group .w-lightbox-image {' +
'max-height:' + (0.86 * vh) + 'px' +
'}' +
'.w-lightbox-strip {' +
'padding: 0 ' + (0.01 * vh) + 'px' +
'}' +
'.w-lightbox-item {' +
'width:' + (0.1 * vh) + 'px;' +
'padding:' + (0.02 * vh) + 'px ' + (0.01 * vh) + 'px' +
'}' +
'.w-lightbox-thumbnail {' +
'height:' + (0.1 * vh) + 'px' +
'}' +
'@media (min-width: 768px) {' +
'.w-lightbox-content, .w-lightbox-view, .w-lightbox-view:before {' +
'height:' + (0.96 * vh) + 'px' +
'}' +
'.w-lightbox-content {' +
'margin-top:' + (0.02 * vh) + 'px' +
'}' +
'.w-lightbox-group, .w-lightbox-group .w-lightbox-view, .w-lightbox-group .w-lightbox-view:before {' +
'height:' + (0.84 * vh) + 'px' +
'}' +
'.w-lightbox-image {' +
'max-width:' + (0.96 * vw) + 'px;' +
'max-height:' + (0.96 * vh) + 'px' +
'}' +
'.w-lightbox-group .w-lightbox-image {' +
'max-width:' + (0.823 * vw) + 'px;' +
'max-height:' + (0.84 * vh) + 'px' +
'}' +
'}';
styleNode.textContent = content;
}
refresh();
})();
return lightbox;
})(window, document, jQuery, window.tram);
Webflow.define('lightbox', function ($, _) {
'use strict';
var api = {};
var $doc = $(document);
var $body;
var $lightboxes;
var designer;
var inApp = Webflow.env();
var namespace = '.w-lightbox';
var groups;
// -----------------------------------
// Module methods
api.ready = api.design = api.preview = init;
// -----------------------------------
// Private methods
function init() {
designer = inApp && Webflow.env('design');
$body = $(document.body);
// Reset Lightbox
lightbox.destroy();
// Reset groups
groups = {};
// Find all instances on the page
$lightboxes = $doc.find(namespace);
$lightboxes.each(build);
}
function build(i, el) {
var $el = $(el);
// Store state in data
var data = $.data(el, namespace);
if (!data) data = $.data(el, namespace, {
el: $el,
mode: 'images',
images: [],
embed: ''
});
// Remove old events
data.el.off(namespace);
// Set config from json script tag
configure(data);
// Add events based on mode
if (designer) {
data.el.on('setting' + namespace, configure.bind(null, data));
}
else {
data.el
.on('tap' + namespace, tapHandler(data))
// Prevent page scrolling to top when clicking on lightbox triggers.
.on('click' + namespace, function (e) { e.preventDefault(); });
}
}
function configure(data) {
var json = data.el.children('.w-json').html();
var groupName, groupItems;
if (!json) {
data.items = [];
return;
}
try {
json = JSON.parse(json);
supportOldLightboxJson(json);
groupName = json.group;
if (groupName) {
groupItems = groups[groupName];
if (!groupItems) {
groupItems = groups[groupName] = [];
}
data.items = groupItems;
if (json.items.length) {
data.index = groupItems.length;
groupItems.push.apply(groupItems, json.items);
}
}
else {
data.items = json.items;
}
}
catch (e) {
console.error('Malformed lightbox JSON configuration.', e.message);
}
}
function tapHandler(data) {
return function () {
data.items.length && lightbox(data.items, data.index || 0);
};
}
function supportOldLightboxJson(data) {
if (data.images) {
data.images.forEach(function (item) {
item.type = 'image';
});
data.items = data.images;
}
if (data.embed) {
data.embed.type = 'video';
data.items = [data.embed];
}
if (data.groupId) {
data.group = data.groupId;
}
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Navbar component
*/
Webflow.define('navbar', function($, _) {
'use strict';
var api = {};
var tram = window.tram;
var $win = $(window);
var $doc = $(document);
var $body;
var $navbars;
var designer;
var inApp = Webflow.env();
var touch = Webflow.env.touch;
var overlay = '<div class="w-nav-overlay" data-wf-ignore />';
var namespace = '.w-nav';
var buttonOpen = 'w--open';
var menuOpen = 'w--nav-menu-open';
var linkOpen = 'w--nav-link-open';
var ix = Webflow.ixEvents();
// -----------------------------------
// Module methods
api.ready = api.design = api.preview = init;
api.destroy = removeListeners;
// -----------------------------------
// Private methods
function init() {
designer = inApp && Webflow.env('design');
$body = $(document.body);
// Find all instances on the page
$navbars = $doc.find(namespace);
if (!$navbars.length) return;
$navbars.each(build);
// Wire events
removeListeners();
addListeners();
}
function removeListeners() {
Webflow.resize.off(resizeAll);
}
function addListeners() {
Webflow.resize.on(resizeAll);
}
function resizeAll() {
$navbars.each(resize);
}
function build(i, el) {
var $el = $(el);
// Store state in data
var data = $.data(el, namespace);
if (!data) data = $.data(el, namespace, { open: false, el: $el, config: {} });
data.menu = $el.find('.w-nav-menu');
data.links = data.menu.find('.w-nav-link');
data.dropdowns = data.menu.find('.w-dropdown');
data.button = $el.find('.w-nav-button');
data.container = $el.find('.w-container');
data.outside = outside(data);
// Remove old events
data.el.off(namespace);
data.button.off(namespace);
data.menu.off(namespace);
// Set config from data attributes
configure(data);
// Add events based on mode
if (designer) {
removeOverlay(data);
data.el.on('setting' + namespace, handler(data));
} else {
addOverlay(data);
data.button.on('tap' + namespace, toggle(data));
data.menu.on('tap' + namespace, 'a', navigate(data));
// Prevent delayed menu clicks on touch devices
if (touch) {
data.menu.on('touchstart' + namespace, 'a', touchStart(data));
data.menu.on('click' + namespace, 'a', touchClick(data));
}
}
// Trigger initial resize
resize(i, el);
}
function removeOverlay(data) {
if (!data.overlay) return;
close(data, true);
data.overlay.remove();
data.overlay = null;
}
function addOverlay(data) {
if (data.overlay) return;
data.overlay = $(overlay).appendTo(data.el);
data.parent = data.menu.parent();
close(data, true);
}
function configure(data) {
var config = {};
var old = data.config || {};
// Set config options from data attributes
var animation = config.animation = data.el.attr('data-animation') || 'default';
config.animOver = /^over/.test(animation);
config.animDirect = /left$/.test(animation) ? -1 : 1;
// Re-open menu if the animation type changed
if (old.animation != animation) {
data.open && _.defer(reopen, data);
}
config.easing = data.el.attr('data-easing') || 'ease';
config.easing2 = data.el.attr('data-easing2') || 'ease';
var duration = data.el.attr('data-duration');
config.duration = duration != null ? +duration : 400;
config.docHeight = data.el.attr('data-doc-height');
// Store config in data
data.config = config;
}
function handler(data) {
return function(evt, options) {
options = options || {};
var winWidth = $win.width();
configure(data);
options.open === true && open(data, true);
options.open === false && close(data, true);
// Reopen if media query changed after setting
data.open && _.defer(function() {
if (winWidth != $win.width()) reopen(data);
});
};
}
function closeEach(i, el) {
var data = $.data(el, namespace);
data.open && close(data);
}
function reopen(data) {
if (!data.open) return;
close(data, true);
open(data, true);
}
function toggle(data) {
// Debounce toggle to wait for accurate open state
return _.debounce(function(evt) {
data.open ? close(data) : open(data);
// Reset touch target to prevent late menu clicks
data.touchTarget = null;
});
}
function navigate(data) {
return function(evt) {
var link = $(this);
var href = link.attr('href');
// Avoid late clicks on touch devices
if (touch && data.touchTarget !== evt.currentTarget) return;
// Close when navigating to an in-page anchor
if (href && href.indexOf('#') === 0 && data.open) {
// Trigger click before tap closes menu
link.trigger('click');
close(data);
}
};
}
function outside(data) {
// Unbind previous tap handler if it exists
if (data.outside) $doc.off('tap' + namespace, data.outside);
// Close menu when tapped outside, debounced to wait for state
return _.debounce(function(evt) {
if (!data.open) return;
var menu = $(evt.target).closest('.w-nav-menu');
if (!data.menu.is(menu)) {
close(data);
}
});
}
function touchStart(data) {
return function(evt) {
data.touchTarget = evt.currentTarget;
};
}
function touchClick(data) {
return function(evt) {
if (data.touchTarget !== evt.currentTarget) evt.preventDefault();
};
}
function resize(i, el) {
var data = $.data(el, namespace);
// Check for collapsed state based on button display
var collapsed = data.collapsed = data.button.css('display') != 'none';
// Close menu if button is no longer visible (and not in designer)
if (data.open && !collapsed && !designer) close(data, true);
// Set max-width of links + dropdowns to match container
if (data.container.length) {
var updateEachMax = updateMax(data);
data.links.each(updateEachMax);
data.dropdowns.each(updateEachMax);
}
// If currently open, update height to match body
if (data.open) {
setOverlayHeight(data);
}
}
var maxWidth = 'max-width';
function updateMax(data) {
// Set max-width of each element to match container
var containMax = data.container.css(maxWidth);
if (containMax == 'none') containMax = '';
return function(i, link) {
link = $(link);
link.css(maxWidth, '');
// Don't set the max-width if an upstream value exists
if (link.css(maxWidth) == 'none') link.css(maxWidth, containMax);
};
}
function open(data, immediate) {
if (data.open) return;
data.open = true;
data.menu.addClass(menuOpen);
data.links.addClass(linkOpen);
data.button.addClass(buttonOpen);
var config = data.config;
var animation = config.animation;
if (animation == 'none' || !tram.support.transform) immediate = true;
var bodyHeight = setOverlayHeight(data);
var menuHeight = data.menu.outerHeight(true);
var menuWidth = data.menu.outerWidth(true);
var navHeight = data.el.height();
var navbarEl = data.el[0];
resize(0, navbarEl);
ix.intro(0, navbarEl);
Webflow.redraw.up();
// Listen for tap outside events
if (!designer) $doc.on('tap' + namespace, data.outside);
// No transition for immediate
if (immediate) return;
var transConfig = 'transform ' + config.duration + 'ms ' + config.easing;
// Add menu to overlay
if (data.overlay) {
data.overlay.show().append(data.menu);
}
// Over left/right
if (config.animOver) {
tram(data.menu)
.add(transConfig)
.set({ x: config.animDirect * menuWidth, height: bodyHeight }).start({ x: 0 });
data.overlay && data.overlay.width(menuWidth);
return;
}
// Drop Down
var offsetY = navHeight + menuHeight;
tram(data.menu)
.add(transConfig)
.set({ y: -offsetY }).start({ y: 0 });
}
function setOverlayHeight(data) {
var config = data.config;
var bodyHeight = config.docHeight ? $doc.height() : $body.height();
if (config.animOver) {
data.menu.height(bodyHeight);
} else if (data.el.css('position') != 'fixed') {
bodyHeight -= data.el.height();
}
data.overlay && data.overlay.height(bodyHeight);
return bodyHeight;
}
function close(data, immediate) {
if (!data.open) return;
data.open = false;
data.button.removeClass(buttonOpen);
var config = data.config;
if (config.animation == 'none' || !tram.support.transform) immediate = true;
var animation = config.animation;
ix.outro(0, data.el[0]);
// Stop listening for tap outside events
$doc.off('tap' + namespace, data.outside);
if (immediate) {
tram(data.menu).stop();
complete();
return;
}
var transConfig = 'transform ' + config.duration + 'ms ' + config.easing2;
var menuHeight = data.menu.outerHeight(true);
var menuWidth = data.menu.outerWidth(true);
var navHeight = data.el.height();
// Over left/right
if (config.animOver) {
tram(data.menu)
.add(transConfig)
.start({ x: menuWidth * config.animDirect }).then(complete);
return;
}
// Drop Down
var offsetY = navHeight + menuHeight;
tram(data.menu)
.add(transConfig)
.start({ y: -offsetY }).then(complete);
function complete() {
data.menu.height('');
tram(data.menu).set({ x: 0, y: 0 });
data.menu.removeClass(menuOpen);
data.links.removeClass(linkOpen);
if (data.overlay && data.overlay.children().length) {
// Move menu back to parent
data.menu.appendTo(data.parent);
data.overlay.attr('style', '').hide();
}
// Trigger event so other components can hook in (dropdown)
data.el.triggerHandler('w-close');
}
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Dropdown component
*/
Webflow.define('dropdown', function($, _) {
'use strict';
var api = {};
var tram = window.tram;
var $doc = $(document);
var $dropdowns;
var designer;
var inApp = Webflow.env();
var namespace = '.w-dropdown';
var stateOpen = 'w--open';
var closeEvent = 'w-close' + namespace;
var ix = Webflow.ixEvents();
// -----------------------------------
// Module methods
api.ready = api.design = api.preview = init;
// -----------------------------------
// Private methods
function init() {
designer = inApp && Webflow.env('design');
// Find all instances on the page
$dropdowns = $doc.find(namespace);
$dropdowns.each(build);
}
function build(i, el) {
var $el = $(el);
// Store state in data
var data = $.data(el, namespace);
if (!data) data = $.data(el, namespace, { open: false, el: $el, config: {} });
data.list = $el.children('.w-dropdown-list');
data.toggle = $el.children('.w-dropdown-toggle');
data.links = data.list.children('.w-dropdown-link');
data.outside = outside(data);
data.complete = complete(data);
// Remove old events
$el.off(namespace);
data.toggle.off(namespace);
// Set config from data attributes
configure(data);
if (data.nav) data.nav.off(namespace);
data.nav = $el.closest('.w-nav');
data.nav.on(closeEvent, handler(data));
// Add events based on mode
if (designer) {
$el.on('setting' + namespace, handler(data));
} else {
data.toggle.on('tap' + namespace, toggle(data));
$el.on(closeEvent, handler(data));
// Close in preview mode
inApp && close(data);
}
}
function configure(data) {
data.config = {
hover: +data.el.attr('data-hover'),
delay: +data.el.attr('data-delay') || 0
};
}
function handler(data) {
return function(evt, options) {
options = options || {};
if (evt.type == 'w-close') {
return close(data);
}
if (evt.type == 'setting') {
configure(data);
options.open === true && open(data, true);
options.open === false && close(data, true);
return;
}
};
}
function toggle(data) {
return _.debounce(function(evt) {
data.open ? close(data) : open(data);
});
}
function open(data, immediate) {
if (data.open) return;
closeOthers(data);
data.open = true;
data.list.addClass(stateOpen);
data.toggle.addClass(stateOpen);
ix.intro(0, data.el[0]);
Webflow.redraw.up();
// Listen for tap outside events
if (!designer) $doc.on('tap' + namespace, data.outside);
// Clear previous delay
window.clearTimeout(data.delayId);
}
function close(data, immediate) {
if (!data.open) return;
data.open = false;
var config = data.config;
ix.outro(0, data.el[0]);
// Stop listening for tap outside events
$doc.off('tap' + namespace, data.outside);
// Clear previous delay
window.clearTimeout(data.delayId);
// Skip delay during immediate
if (!config.delay || immediate) return data.complete();
// Optionally wait for delay before close
data.delayId = window.setTimeout(data.complete, config.delay);
}
function closeOthers(data) {
var self = data.el[0];
$dropdowns.each(function(i, other) {
var $other = $(other);
if ($other.is(self) || $other.has(self).length) return;
$other.triggerHandler(closeEvent);
});
}
function outside(data) {
// Unbind previous tap handler if it exists
if (data.outside) $doc.off('tap' + namespace, data.outside);
// Close menu when tapped outside
return _.debounce(function(evt) {
if (!data.open) return;
var $target = $(evt.target);
if ($target.closest('.w-dropdown-toggle').length) return;
if (!data.el.is($target.closest(namespace))) {
close(data);
}
});
}
function complete(data) {
return function() {
data.list.removeClass(stateOpen);
data.toggle.removeClass(stateOpen);
};
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Tabs component
*/
Webflow.define('tabs', function($, _) {
'use strict';
var api = {};
var tram = window.tram;
var $win = $(window);
var $doc = $(document);
var $tabs;
var design;
var env = Webflow.env;
var safari = env.safari;
var inApp = env();
var tabAttr = 'data-w-tab';
var namespace = '.w-tabs';
var linkCurrent = 'w--current';
var tabActive = 'w--tab-active';
var ix = Webflow.ixEvents();
// -----------------------------------
// Module methods
api.ready = api.design = api.preview = init;
// -----------------------------------
// Private methods
function init() {
design = inApp && Webflow.env('design');
// Find all instances on the page
$tabs = $doc.find(namespace);
if (!$tabs.length) return;
$tabs.each(build);
}
function build(i, el) {
var $el = $(el);
// Store state in data
var data = $.data(el, namespace);
if (!data) data = $.data(el, namespace, { el: $el, config: {} });
data.current = null;
data.menu = $el.children('.w-tab-menu');
data.links = data.menu.children('.w-tab-link');
data.content = $el.children('.w-tab-content');
data.panes = data.content.children('.w-tab-pane');
// Remove old events
data.el.off(namespace);
data.links.off(namespace);
// Set config from data attributes
configure(data);
// Wire up events when not in design mode
if (!design) {
data.links.on('click' + namespace, linkSelect(data));
// Trigger first intro event from current tab
var $link = data.links.filter('.' + linkCurrent);
var tab = $link.attr(tabAttr);
tab && changeTab(data, { tab: tab, immediate: true });
}
}
function configure(data) {
var config = {};
var old = data.config || {};
// Set config options from data attributes
config.easing = data.el.attr('data-easing') || 'ease';
var intro = +data.el.attr('data-duration-in');
intro = config.intro = intro === intro ? intro : 0;
var outro = +data.el.attr('data-duration-out');
outro = config.outro = outro === outro ? outro : 0;
config.immediate = !intro && !outro;
// Store config in data
data.config = config;
}
function linkSelect(data) {
return function(evt) {
var tab = evt.currentTarget.getAttribute(tabAttr);
tab && changeTab(data, { tab: tab });
};
}
function changeTab(data, options) {
options = options || {};
var config = data.config;
var easing = config.easing;
var tab = options.tab;
// Don't select the same tab twice
if (tab === data.current) return;
data.current = tab;
// Select the current link
data.links.each(function(i, el) {
var $el = $(el);
if (el.getAttribute(tabAttr) === tab) $el.addClass(linkCurrent).each(ix.intro);
else if ($el.hasClass(linkCurrent)) $el.removeClass(linkCurrent).each(ix.outro);
});
// Find the new tab panes and keep track of previous
var targets = [];
var previous = [];
data.panes.each(function(i, el) {
var $el = $(el);
if (el.getAttribute(tabAttr) === tab) {
targets.push(el);
} else if ($el.hasClass(tabActive)) {
previous.push(el);
}
});
var $targets = $(targets);
var $previous = $(previous);
// Switch tabs immediately and bypass transitions
if (options.immediate || config.immediate) {
$targets.addClass(tabActive).each(ix.intro);
$previous.removeClass(tabActive);
Webflow.redraw.up();
return;
}
// Fade out the currently active tab before intro
if ($previous.length && config.outro) {
$previous.each(ix.outro);
tram($previous)
.add('opacity ' + config.outro + 'ms ' + easing, { fallback: safari })
.start({ opacity: 0 })
.then(intro);
} else {
// Skip the outro and play intro
intro();
}
// Fade in the new target
function intro() {
// Clear previous active class + inline style
$previous.removeClass(tabActive).removeAttr('style');
// Add active class to new target
$targets.addClass(tabActive).each(ix.intro);
Webflow.redraw.up();
// Set opacity immediately if intro is zero
if (!config.intro) return tram($targets).set({ opacity: 1 });
// Otherwise fade in opacity
tram($targets)
.set({ opacity: 0 })
.redraw()
.add('opacity ' + config.intro + 'ms ' + easing, { fallback: safari })
.start({ opacity: 1 });
}
}
// Export module
return api;
});
/**
* ----------------------------------------------------------------------
* Webflow: Interactions: Init
*/
Webflow.require('ix').init([
{"slug":"display-login","name":"Display-login","value":{"style":{},"triggers":[{"type":"click","selector":".modal-background","stepsA":[{"display":"block"}],"stepsB":[]}]}},
{"slug":"close-overlay","name":"Close overlay","value":{"style":{},"triggers":[{"type":"click","selector":".modal-background","stepsA":[{"display":"none"}],"stepsB":[]}]}},
{"slug":"display-login-key-review","name":"Display Login Key Review","value":{"style":{},"triggers":[{"type":"click","selector":".review-details-modal","stepsA":[{"display":"block"}],"stepsB":[]}]}},
{"slug":"display-review-account","name":"Display Review Account","value":{"style":{},"triggers":[{"type":"click","selector":".review-account-details","stepsA":[{"display":"block"}],"stepsB":[]}]}},
{"slug":"close-login-key","name":"Close-Login-Key","value":{"style":{},"triggers":[{"type":"click","selector":".review-details-modal","stepsA":[{"display":"none"}],"stepsB":[]}]}},
{"slug":"close-review-details","name":"Close-Review-Details","value":{"style":{},"triggers":[{"type":"click","selector":".review-account-details","stepsA":[{"display":"none"}],"stepsB":[]}]}}
]);