/*!
* ----------------------------------------------------------------------
* 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;
};
// 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 = (window.chrome || /chrome/.test(userAgent)) && 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.destroy() - Trigger a cleanup event for all modules
api.destroy = function() {
$win.triggerHandler('__wf_destroy');
};
// Listen for domready
$(api.ready);
/*!
* 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.1 - 2013-10-17
* https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
* Copyright (c) 2013 Jason Moon (@JSONMOON)
* Licensed MIT (/blob/master/LICENSE.txt)
*/
(function($) {
if (!$.support.cors && $.ajaxTransport && window.XDomainRequest) {
var n = /^https?:\/\//i;
var o = /^get|post$/i;
var p = new RegExp('^' + location.protocol, 'i');
var q = /text\/html/i;
var r = /\/json/i;
var s = /\/xml/i;
$.ajaxTransport('* text html xml json', function(i, j, k) {
if (i.crossDomain && i.async && o.test(i.type) && n.test(i.url) && p.test(i.url)) {
var l = null;
var m = (j.dataType || '').toLowerCase();
return {
send: function(f, g) {
l = new XDomainRequest();
if (/^\d+$/.test(j.timeout)) {
l.timeout = j.timeout
}
l.ontimeout = function() {
g(500, 'timeout')
};
l.onload = function() {
var a = 'Content-Length: ' + l.responseText.length + '\r\nContent-Type: ' + l.contentType;
var b = {
code: 200,
message: 'success'
};
var c = {
text: l.responseText
};
try {
if (m === 'html' || q.test(l.contentType)) {
c.html = l.responseText
} else if (m === 'json' || (m !== 'text' && r.test(l.contentType))) {
try {
c.json = $.parseJSON(l.responseText)
} catch (e) {
b.code = 500;
b.message = 'parseerror'
}
} else if (m === 'xml' || (m !== 'text' && s.test(l.contentType))) {
var d = new ActiveXObject('Microsoft.XMLDOM');
d.async = false;
try {
d.loadXML(l.responseText)
} catch (e) {
d = undefined
}
if (!d || !d.documentElement || d.getElementsByTagName('parsererror').length) {
b.code = 500;
b.message = 'parseerror';
throw 'Invalid XML: ' + l.responseText;
}
c.xml = d
}
} catch (parseMessage) {
throw parseMessage;
} finally {
g(b.code, b.message, c, a)
}
};
l.onprogress = function() {};
l.onerror = function() {
g(500, 'error', {
text: l.responseText
})
};
var h = '';
if (j.data) {
h = ($.type(j.data) === 'string') ? j.data : $.param(j.data)
}
l.open(i.type, i.url);
l.send(h)
},
abort: function() {
if (l) {
l.abort()
}
}
}
}
})
}
})(jQuery);
/* 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 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() {
init(list);
}, 1);
};
api.preview = function() {
designer = false;
setTimeout(function() {
init(window.__wf_ix);
}, 1);
};
api.design = function() {
designer = true;
$subs.each(teardown);
Webflow.scroll.off(scroll);
asyncEvents();
anchors = [];
loads = [];
readys = [];
};
api.run = run;
api.events = {};
api.style = inApp ? styleApp : stylePub;
// -----------------------------------
// Private methods
function init(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;
});
// 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) $win.on('load', runLoads);
if (readys.length) setTimeout(runReadys, 1);
// Init module events
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;
var _move = _.throttle(move);
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 don’t 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 $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 = 'https://webflow.com/api/v1/form/' + siteId;
// Work around same-protocol IE XDR limitation
if (retro && url.indexOf('https://webflow.com') >= 0) {
url = url.replace('https://webflow.com/', 'http://data.webflow.com/');
}
$.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 [email protected] to fix this issue.');
}, 100);
// 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;
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;
}
// 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 (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;
data.anchors = [{
els: [],
x: 0,
width: 0
}];
data.slides.each(function(i, el) {
if (anchor - offset > data.maskWidth - data.config.edge) {
pages++;
offset += data.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;
var matchMedia = window.matchMedia || function(media) {
// IE9 polyfill
return {
matches: window.styleMedia.matchMedium(media)
};
};
var pixelRatio = window.devicePixelRatio || 1;
var breakpoint = '(min-width: 1025px)';
// 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.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.controls = $refs.empty.add($refs.arrowLeft).add($refs.arrowRight).add($refs.close);
$refs.spinner = dom('spinner');
$refs.strip = dom('strip');
spinner = new Spinner($refs.spinner, prefixed('hide'));
$refs.content = dom('content')
.append($refs.spinner, $refs.controls);
$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'), preventDefaultAnd(lightbox.prev))
.on('tap', selector('right'), preventDefaultAnd(lightbox.next))
.on('tap', selector('close'), preventDefaultAnd(lightbox.hide))
.on('tap', selector('image, caption'), toggleControlsOr(lightbox.next));
$refs.container.on(
'tap', selector('view, strip'), toggleControlsOr(lightbox.hide)
)
// Prevent images from being dragged around.
.on('dragstart', selector('img'), preventDefault);
$refs.lightbox
.on('keydown', keyHandler)
// While visible, prevent lightbox from loosing focus to other nodes.
// IE looses focus without letting us know.
.on('focusin', focusThis)
// Unfortunately setTimeout is needed because of a 14 year old
// Firefox bug (https://bugzilla.mozilla.org/show_bug.cgi?id=53579#c4).
.on('blur', function() {
setTimeout(focusThis.bind(this), 0);
});
// 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 video’s 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);
if (item.html) {
$figure.append(addClass($(item.html), 'embed'));
}
if (item.caption) {
$figure.append(dom('caption', 'figcaption').text(item.caption));
}
spinner.hide();
toggleClass($refs.arrowLeft, 'inactive', index <= 0);
toggleClass($refs.arrowRight, 'inactive', index >= items.length - 1);
$refs.spinner.before($newView);
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({
opacity: 0,
x: index > previousIndex ? '80px' : '-80px'
})
.start({
opacity: 1,
x: 0
});
}
$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 toggleControlsOr(callback) {
return function(event) {
// We only care about events triggered directly on the bound selectors.
if (this != event.target) {
return;
}
event.stopPropagation();
event.preventDefault();
if (matchMedia(breakpoint).matches) {
callback();
} else {
toggleClass($refs.controls, 'visible');
}
};
}
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();
}
};
function preventDefaultAnd(action) {
return function(event) {
// Prevents click events and zooming.
event.preventDefault();
action();
};
}
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');
removeClass($refs.controls, 'visible');
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' +
'}';
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 groupId, group;
if (!json) {
data.images = [];
return;
}
try {
json = JSON.parse(json);
data.mode = json.mode;
if (json.mode == 'video') {
data.embed = json.embed;
} else {
groupId = json.groupId;
if (groupId) {
group = groups[groupId];
if (!group) {
group = groups[groupId] = [];
}
data.images = group;
if (json.images.length) {
data.index = group.length;
group.push.apply(group, json.images);
}
} else {
data.images = json.images;
}
}
} catch (e) {
console.error('Malformed lightbox JSON configuration.', e.message);
}
}
function tapHandler(data) {
return function() {
if (data.mode == 'video') {
data.embed && lightbox(data.embed);
} else {
data.images.length && lightbox(data.images, data.index || 0);
}
};
}
// 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 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));
}
// 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) {
return _.debounce(function(evt) {
data.open ? close(data) : open(data);
});
}
function navigate(data) {
return function(evt) {
var link = $(this);
var href = link.attr('href');
// Close when navigating to an in-page anchor
if (href && href.indexOf('#') === 0 && data.open) {
// Avoid empty hash links
if (href.length === 1) evt.preventDefault();
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
return _.debounce(function(evt) {
if (!data.open) return;
var menu = $(evt.target).closest('.w-nav-menu');
if (!data.menu.is(menu)) {
close(data);
}
});
}
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);
// 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]);
// 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;
});