;(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define([], function() {
return (factory());
});
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.whatinput = factory();
}
} (this, function() {
'use strict';
/*
---------------
variables
---------------
*/
// array of actively pressed keys
var activekeys = [];
// cache document.body
var body = document.body;
// boolean: true if touch buffer timer is running
var buffer = false;
// the last used input type
var currentinput = null;
// array of form elements that take keyboard input
var forminputs = [
'input',
'select',
'textarea'
];
// user-set flag to allow typing in form fields to be recorded
var formtyping = body.hasattribute('data-whatinput-formtyping');
// mapping of events to input types
var inputmap = {
'keydown': 'keyboard',
'mousedown': 'mouse',
'mouseenter': 'mouse',
'touchstart': 'touch',
'pointerdown': 'pointer',
'mspointerdown': 'pointer'
};
// array of all used input types
var inputtypes = [];
// mapping of key codes to common name
var keymap = {
9: 'tab',
13: 'enter',
16: 'shift',
27: 'esc',
32: 'space',
37: 'left',
38: 'up',
39: 'right',
40: 'down'
};
// map of ie 10 pointer events
var pointermap = {
2: 'touch',
3: 'touch', // treat pen like touch
4: 'mouse'
};
// touch buffer timer
var timer;
/*
---------------
functions
---------------
*/
function bufferinput(event) {
cleartimeout(timer);
setinput(event);
buffer = true;
timer = settimeout(function() {
buffer = false;
}, 1000);
}
function immediateinput(event) {
if (!buffer) setinput(event);
}
function setinput(event) {
var eventkey = key(event);
var eventtarget = target(event);
var value = inputmap[event.type];
if (value === 'pointer') value = pointertype(event);
if (currentinput !== value) {
if (
// only if the user flag isn't set
!formtyping &&
// only if currentinput has a value
currentinput &&
// only if the input is `keyboard`
value === 'keyboard' &&
// not if the key is `tab`
keymap[eventkey] !== 'tab' &&
// only if the target is one of the elements in `forminputs`
forminputs.indexof(eventtarget.nodename.tolowercase()) >= 0
) {
// ignore keyboard typing on form elements
} else {
currentinput = value;
body.setattribute('data-whatinput', currentinput);
if (inputtypes.indexof(currentinput) === -1) inputtypes.push(currentinput);
}
}
if (value === 'keyboard') logkeys(eventkey);
}
function key(event) {
return (event.keycode) ? event.keycode : event.which;
}
function target(event) {
return event.target || event.srcelement;
}
function pointertype(event) {
return (typeof event.pointertype === 'number') ? pointermap[event.pointertype] : event.pointertype;
}
// keyboard logging
function logkeys(eventkey) {
if (activekeys.indexof(keymap[eventkey]) === -1 && keymap[eventkey]) activekeys.push(keymap[eventkey]);
}
function unlogkeys(event) {
var eventkey = key(event);
var arraypos = activekeys.indexof(keymap[eventkey]);
if (arraypos !== -1) activekeys.splice(arraypos, 1);
}
function bindevents() {
// pointer/mouse
var mouseevent = 'mousedown';
if (window.pointerevent) {
mouseevent = 'pointerdown';
} else if (window.mspointerevent) {
mouseevent = 'mspointerdown';
}
body.addeventlistener(mouseevent, immediateinput);
body.addeventlistener('mouseenter', immediateinput);
// touch
if ('ontouchstart' in window) {
body.addeventlistener('touchstart', bufferinput);
}
// keyboard
body.addeventlistener('keydown', immediateinput);
document.addeventlistener('keyup', unlogkeys);
}
/*
---------------
init
don't start script unless browser cuts the mustard,
also passes if polyfills are used
---------------
*/
if ('addeventlistener' in window && array.prototype.indexof) {
bindevents();
}
/*
---------------
api
---------------
*/
return {
// returns string: the current input type
ask: function() { return currentinput; },
// returns array: currently pressed keys
keys: function() { return activekeys; },
// returns array: all the detected input types
types: function() { return inputtypes; },
// accepts string: manually set the input type
set: setinput
};
}));
!function ($) {
"use strict";
var foundation_version = '6.2.2';
// global foundation object
// this is attached to the window, or used as a module for amd/browserify
var foundation = {
version: foundation_version,
/**
* stores initialized plugins.
*/
_plugins: {},
/**
* stores generated unique ids for plugin instances
*/
_uuids: [],
/**
* returns a boolean for rtl support
*/
rtl: function () {
return $('html').attr('dir') === 'rtl';
},
/**
* defines a foundation plugin, adding it to the `foundation` namespace and the list of plugins to initialize when reflowing.
* @param {object} plugin - the constructor of the plugin.
*/
plugin: function (plugin, name) {
// object key to use when adding to global foundation object
// examples: foundation.reveal, foundation.offcanvas
var classname = name || functionname(plugin);
// object key to use when storing the plugin, also used to create the identifying data attribute for the plugin
// examples: data-reveal, data-off-canvas
var attrname = hyphenate(classname);
// add to the foundation object and the plugins list (for reflowing)
this._plugins[attrname] = this[classname] = plugin;
},
/**
* @function
* populates the _uuids array with pointers to each individual plugin instance.
* adds the `zfplugin` data-attribute to programmatically created plugins to allow use of $(selector).foundation(method) calls.
* also fires the initialization event for each plugin, consolidating repetitive code.
* @param {object} plugin - an instance of a plugin, usually `this` in context.
* @param {string} name - the name of the plugin, passed as a camelcased string.
* @fires plugin#init
*/
registerplugin: function (plugin, name) {
var pluginname = name ? hyphenate(name) : functionname(plugin.constructor).tolowercase();
plugin.uuid = this.getyodigits(6, pluginname);
if (!plugin.$element.attr('data-' + pluginname)) {
plugin.$element.attr('data-' + pluginname, plugin.uuid);
}
if (!plugin.$element.data('zfplugin')) {
plugin.$element.data('zfplugin', plugin);
}
/**
* fires when the plugin has initialized.
* @event plugin#init
*/
plugin.$element.trigger('init.zf.' + pluginname);
this._uuids.push(plugin.uuid);
return;
},
/**
* @function
* removes the plugins uuid from the _uuids array.
* removes the zfplugin data attribute, as well as the data-plugin-name attribute.
* also fires the destroyed event for the plugin, consolidating repetitive code.
* @param {object} plugin - an instance of a plugin, usually `this` in context.
* @fires plugin#destroyed
*/
unregisterplugin: function (plugin) {
var pluginname = hyphenate(functionname(plugin.$element.data('zfplugin').constructor));
this._uuids.splice(this._uuids.indexof(plugin.uuid), 1);
plugin.$element.removeattr('data-' + pluginname).removedata('zfplugin')
/**
* fires when the plugin has been destroyed.
* @event plugin#destroyed
*/
.trigger('destroyed.zf.' + pluginname);
for (var prop in plugin) {
plugin[prop] = null; //clean up script to prep for garbage collection.
}
return;
},
/**
* @function
* causes one or more active plugins to re-initialize, resetting event listeners, recalculating positions, etc.
* @param {string} plugins - optional string of an individual plugin key, attained by calling `$(element).data('pluginname')`, or string of a plugin class i.e. `'dropdown'`
* @default if no argument is passed, reflow all currently active plugins.
*/
reinit: function (plugins) {
var isjq = plugins instanceof $;
try {
if (isjq) {
plugins.each(function () {
$(this).data('zfplugin')._init();
});
} else {
var type = typeof plugins,
_this = this,
fns = {
'object': function (plgs) {
plgs.foreach(function (p) {
p = hyphenate(p);
$('[data-' + p + ']').foundation('_init');
});
},
'string': function () {
plugins = hyphenate(plugins);
$('[data-' + plugins + ']').foundation('_init');
},
'undefined': function () {
this['object'](object.keys(_this._plugins));
}
};
fns[type](plugins);
}
} catch (err) {
console.error(err);
} finally {
return plugins;
}
},
/**
* returns a random base-36 uid with namespacing
* @function
* @param {number} length - number of random base-36 digits desired. increase for more random strings.
* @param {string} namespace - name of plugin to be incorporated in uid, optional.
* @default {string} '' - if no plugin name is provided, nothing is appended to the uid.
* @returns {string} - unique id
*/
getyodigits: function (length, namespace) {
length = length || 6;
return math.round(math.pow(36, length + 1) - math.random() * math.pow(36, length)).tostring(36).slice(1) + (namespace ? '-' + namespace : '');
},
/**
* initialize plugins on any elements within `elem` (and `elem` itself) that aren't already initialized.
* @param {object} elem - jquery object containing the element to check inside. also checks the element itself, unless it's the `document` object.
* @param {string|array} plugins - a list of plugins to initialize. leave this out to initialize everything.
*/
reflow: function (elem, plugins) {
// if plugins is undefined, just grab everything
if (typeof plugins === 'undefined') {
plugins = object.keys(this._plugins);
}
// if plugins is a string, convert it to an array with one item
else if (typeof plugins === 'string') {
plugins = [plugins];
}
var _this = this;
// iterate through each plugin
$.each(plugins, function (i, name) {
// get the current plugin
var plugin = _this._plugins[name];
// localize the search to all elements inside elem, as well as elem itself, unless elem === document
var $elem = $(elem).find('[data-' + name + ']').addback('[data-' + name + ']');
// for each plugin found, initialize it
$elem.each(function () {
var $el = $(this),
opts = {};
// don't double-dip on plugins
if ($el.data('zfplugin')) {
console.warn("tried to initialize " + name + " on an element that already has a foundation plugin.");
return;
}
if ($el.attr('data-options')) {
var thing = $el.attr('data-options').split(';').foreach(function (e, i) {
var opt = e.split(':').map(function (el) {
return el.trim();
});
if (opt[0]) opts[opt[0]] = parsevalue(opt[1]);
});
}
try {
$el.data('zfplugin', new plugin($(this), opts));
} catch (er) {
console.error(er);
} finally {
return;
}
});
});
},
getfnname: functionname,
transitionend: function ($elem) {
var transitions = {
'transition': 'transitionend',
'webkittransition': 'webkittransitionend',
'moztransition': 'transitionend',
'otransition': 'otransitionend'
};
var elem = document.createelement('div'),
end;
for (var t in transitions) {
if (typeof elem.style[t] !== 'undefined') {
end = transitions[t];
}
}
if (end) {
return end;
} else {
end = settimeout(function () {
$elem.triggerhandler('transitionend', [$elem]);
}, 1);
return 'transitionend';
}
}
};
foundation.util = {
/**
* function for applying a debounce effect to a function call.
* @function
* @param {function} func - function to be called at end of timeout.
* @param {number} delay - time in ms to delay the call of `func`.
* @returns function
*/
throttle: function (func, delay) {
var timer = null;
return function () {
var context = this,
args = arguments;
if (timer === null) {
timer = settimeout(function () {
func.apply(context, args);
timer = null;
}, delay);
}
};
}
};
// todo: consider not making this a jquery function
// todo: need way to reflow vs. re-initialize
/**
* the foundation jquery method.
* @param {string|array} method - an action to perform on the current jquery object.
*/
var foundation = function (method) {
var type = typeof method,
$meta = $('meta.foundation-mq'),
$nojs = $('.no-js');
if (!$meta.length) {
$(' ').appendto(document.head);
}
if ($nojs.length) {
$nojs.removeclass('no-js');
}
if (type === 'undefined') {
//needs to initialize the foundation object, or an individual plugin.
foundation.mediaquery._init();
foundation.reflow(this);
} else if (type === 'string') {
//an individual method to invoke on a plugin or group of plugins
var args = array.prototype.slice.call(arguments, 1); //collect all the arguments, if necessary
var plugclass = this.data('zfplugin'); //determine the class of plugin
if (plugclass !== undefined && plugclass[method] !== undefined) {
//make sure both the class and method exist
if (this.length === 1) {
//if there's only one, call it directly.
plugclass[method].apply(plugclass, args);
} else {
this.each(function (i, el) {
//otherwise loop through the jquery collection and invoke the method on each
plugclass[method].apply($(el).data('zfplugin'), args);
});
}
} else {
//error for no class or no method
throw new referenceerror("we're sorry, '" + method + "' is not an available method for " + (plugclass ? functionname(plugclass) : 'this element') + '.');
}
} else {
//error for invalid argument type
throw new typeerror('we\'re sorry, ' + type + ' is not a valid parameter. you must use a string representing the method you wish to invoke.');
}
return this;
};
window.foundation = foundation;
$.fn.foundation = foundation;
// polyfill for requestanimationframe
(function () {
if (!date.now || !window.date.now) window.date.now = date.now = function () {
return new date().gettime();
};
var vendors = ['webkit', 'moz'];
for (var i = 0; i < vendors.length && !window.requestanimationframe; ++i) {
var vp = vendors[i];
window.requestanimationframe = window[vp + 'requestanimationframe'];
window.cancelanimationframe = window[vp + 'cancelanimationframe'] || window[vp + 'cancelrequestanimationframe'];
}
if (/ip(ad|hone|od).*os 6/.test(window.navigator.useragent) || !window.requestanimationframe || !window.cancelanimationframe) {
var lasttime = 0;
window.requestanimationframe = function (callback) {
var now = date.now();
var nexttime = math.max(lasttime + 16, now);
return settimeout(function () {
callback(lasttime = nexttime);
}, nexttime - now);
};
window.cancelanimationframe = cleartimeout;
}
/**
* polyfill for performance.now, required by raf
*/
if (!window.performance || !window.performance.now) {
window.performance = {
start: date.now(),
now: function () {
return date.now() - this.start;
}
};
}
})();
if (!function.prototype.bind) {
function.prototype.bind = function (othis) {
if (typeof this !== 'function') {
// closest thing possible to the ecmascript 5
// internal iscallable function
throw new typeerror('function.prototype.bind - what is trying to be bound is not callable');
}
var aargs = array.prototype.slice.call(arguments, 1),
ftobind = this,
fnop = function () {},
fbound = function () {
return ftobind.apply(this instanceof fnop ? this : othis, aargs.concat(array.prototype.slice.call(arguments)));
};
if (this.prototype) {
// native functions don't have a prototype
fnop.prototype = this.prototype;
}
fbound.prototype = new fnop();
return fbound;
};
}
// polyfill to get the name of a function in ie9
function functionname(fn) {
if (function.prototype.name === undefined) {
var funcnameregex = /function\s([^(]{1,})\(/;
var results = funcnameregex.exec(fn.tostring());
return results && results.length > 1 ? results[1].trim() : "";
} else if (fn.prototype === undefined) {
return fn.constructor.name;
} else {
return fn.prototype.constructor.name;
}
}
function parsevalue(str) {
if (/true/.test(str)) return true;else if (/false/.test(str)) return false;else if (!isnan(str * 1)) return parsefloat(str);
return str;
}
// convert pascalcase to kebab-case
// thank you: http://stackoverflow.com/a/8955580
function hyphenate(str) {
return str.replace(/([a-z])([a-z])/g, '$1-$2').tolowercase();
}
}(jquery);
'use strict';
!function ($) {
foundation.box = {
imnottouchingyou: imnottouchingyou,
getdimensions: getdimensions,
getoffsets: getoffsets
};
/**
* compares the dimensions of an element to a container and determines collision events with container.
* @function
* @param {jquery} element - jquery object to test for collisions.
* @param {jquery} parent - jquery object to use as bounding container.
* @param {boolean} lronly - set to true to check left and right values only.
* @param {boolean} tbonly - set to true to check top and bottom values only.
* @default if no parent object passed, detects collisions with `window`.
* @returns {boolean} - true if collision free, false if a collision in any direction.
*/
function imnottouchingyou(element, parent, lronly, tbonly) {
var eledims = getdimensions(element),
top,
bottom,
left,
right;
if (parent) {
var pardims = getdimensions(parent);
bottom = eledims.offset.top + eledims.height <= pardims.height + pardims.offset.top;
top = eledims.offset.top >= pardims.offset.top;
left = eledims.offset.left >= pardims.offset.left;
right = eledims.offset.left + eledims.width <= pardims.width + pardims.offset.left;
} else {
bottom = eledims.offset.top + eledims.height <= eledims.windowdims.height + eledims.windowdims.offset.top;
top = eledims.offset.top >= eledims.windowdims.offset.top;
left = eledims.offset.left >= eledims.windowdims.offset.left;
right = eledims.offset.left + eledims.width <= eledims.windowdims.width;
}
var alldirs = [bottom, top, left, right];
if (lronly) {
return left === right === true;
}
if (tbonly) {
return top === bottom === true;
}
return alldirs.indexof(false) === -1;
};
/**
* uses native methods to return an object of dimension values.
* @function
* @param {jquery || html} element - jquery object or dom element for which to get the dimensions. can be any element other that document or window.
* @returns {object} - nested object of integer pixel values
* todo - if element is window, return only those values.
*/
function getdimensions(elem, test) {
elem = elem.length ? elem[0] : elem;
if (elem === window || elem === document) {
throw new error("i'm sorry, dave. i'm afraid i can't do that.");
}
var rect = elem.getboundingclientrect(),
parrect = elem.parentnode.getboundingclientrect(),
winrect = document.body.getboundingclientrect(),
winy = window.pageyoffset,
winx = window.pagexoffset;
return {
width: rect.width,
height: rect.height,
offset: {
top: rect.top + winy,
left: rect.left + winx
},
parentdims: {
width: parrect.width,
height: parrect.height,
offset: {
top: parrect.top + winy,
left: parrect.left + winx
}
},
windowdims: {
width: winrect.width,
height: winrect.height,
offset: {
top: winy,
left: winx
}
}
};
}
/**
* returns an object of top and left integer pixel values for dynamically rendered elements,
* such as: tooltip, reveal, and dropdown
* @function
* @param {jquery} element - jquery object for the element being positioned.
* @param {jquery} anchor - jquery object for the element's anchor point.
* @param {string} position - a string relating to the desired position of the element, relative to it's anchor
* @param {number} voffset - integer pixel value of desired vertical separation between anchor and element.
* @param {number} hoffset - integer pixel value of desired horizontal separation between anchor and element.
* @param {boolean} isoverflow - if a collision event is detected, sets to true to default the element to full width - any desired offset.
* todo alter/rewrite to work with `em` values as well/instead of pixels
*/
function getoffsets(element, anchor, position, voffset, hoffset, isoverflow) {
var $eledims = getdimensions(element),
$anchordims = anchor ? getdimensions(anchor) : null;
switch (position) {
case 'top':
return {
left: foundation.rtl() ? $anchordims.offset.left - $eledims.width + $anchordims.width : $anchordims.offset.left,
top: $anchordims.offset.top - ($eledims.height + voffset)
};
break;
case 'left':
return {
left: $anchordims.offset.left - ($eledims.width + hoffset),
top: $anchordims.offset.top
};
break;
case 'right':
return {
left: $anchordims.offset.left + $anchordims.width + hoffset,
top: $anchordims.offset.top
};
break;
case 'center top':
return {
left: $anchordims.offset.left + $anchordims.width / 2 - $eledims.width / 2,
top: $anchordims.offset.top - ($eledims.height + voffset)
};
break;
case 'center bottom':
return {
left: isoverflow ? hoffset : $anchordims.offset.left + $anchordims.width / 2 - $eledims.width / 2,
top: $anchordims.offset.top + $anchordims.height + voffset
};
break;
case 'center left':
return {
left: $anchordims.offset.left - ($eledims.width + hoffset),
top: $anchordims.offset.top + $anchordims.height / 2 - $eledims.height / 2
};
break;
case 'center right':
return {
left: $anchordims.offset.left + $anchordims.width + hoffset + 1,
top: $anchordims.offset.top + $anchordims.height / 2 - $eledims.height / 2
};
break;
case 'center':
return {
left: $eledims.windowdims.offset.left + $eledims.windowdims.width / 2 - $eledims.width / 2,
top: $eledims.windowdims.offset.top + $eledims.windowdims.height / 2 - $eledims.height / 2
};
break;
case 'reveal':
return {
left: ($eledims.windowdims.width - $eledims.width) / 2,
top: $eledims.windowdims.offset.top + voffset
};
case 'reveal full':
return {
left: $eledims.windowdims.offset.left,
top: $eledims.windowdims.offset.top
};
break;
case 'left bottom':
return {
left: $anchordims.offset.left - ($eledims.width + hoffset),
top: $anchordims.offset.top + $anchordims.height
};
break;
case 'right bottom':
return {
left: $anchordims.offset.left + $anchordims.width + hoffset - $eledims.width,
top: $anchordims.offset.top + $anchordims.height
};
break;
default:
return {
left: foundation.rtl() ? $anchordims.offset.left - $eledims.width + $anchordims.width : $anchordims.offset.left,
top: $anchordims.offset.top + $anchordims.height + voffset
};
}
}
}(jquery);
/*******************************************
* *
* this util was created by marius olbertz *
* please thank marius on github /owlbertz *
* or the web http://www.mariusolbertz.de/ *
* *
******************************************/
'use strict';
!function ($) {
var keycodes = {
9: 'tab',
13: 'enter',
27: 'escape',
32: 'space',
37: 'arrow_left',
38: 'arrow_up',
39: 'arrow_right',
40: 'arrow_down'
};
var commands = {};
var keyboard = {
keys: getkeycodes(keycodes),
/**
* parses the (keyboard) event and returns a string that represents its key
* can be used like foundation.parsekey(event) === foundation.keys.space
* @param {event} event - the event generated by the event handler
* @return string key - string that represents the key pressed
*/
parsekey: function (event) {
var key = keycodes[event.which || event.keycode] || string.fromcharcode(event.which).touppercase();
if (event.shiftkey) key = 'shift_' + key;
if (event.ctrlkey) key = 'ctrl_' + key;
if (event.altkey) key = 'alt_' + key;
return key;
},
/**
* handles the given (keyboard) event
* @param {event} event - the event generated by the event handler
* @param {string} component - foundation component's name, e.g. slider or reveal
* @param {objects} functions - collection of functions that are to be executed
*/
handlekey: function (event, component, functions) {
var commandlist = commands[component],
keycode = this.parsekey(event),
cmds,
command,
fn;
if (!commandlist) return console.warn('component not defined!');
if (typeof commandlist.ltr === 'undefined') {
// this component does not differentiate between ltr and rtl
cmds = commandlist; // use plain list
} else {
// merge ltr and rtl: if document is rtl, rtl overwrites ltr and vice versa
if (foundation.rtl()) cmds = $.extend({}, commandlist.ltr, commandlist.rtl);else cmds = $.extend({}, commandlist.rtl, commandlist.ltr);
}
command = cmds[keycode];
fn = functions[command];
if (fn && typeof fn === 'function') {
// execute function if exists
var returnvalue = fn.apply();
if (functions.handled || typeof functions.handled === 'function') {
// execute function when event was handled
functions.handled(returnvalue);
}
} else {
if (functions.unhandled || typeof functions.unhandled === 'function') {
// execute function when event was not handled
functions.unhandled();
}
}
},
/**
* finds all focusable elements within the given `$element`
* @param {jquery} $element - jquery object to search within
* @return {jquery} $focusable - all focusable elements within `$element`
*/
findfocusable: function ($element) {
return $element.find('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]').filter(function () {
if (!$(this).is(':visible') || $(this).attr('tabindex') < 0) {
return false;
} //only have visible elements and those that have a tabindex greater or equal 0
return true;
});
},
/**
* returns the component name name
* @param {object} component - foundation component, e.g. slider or reveal
* @return string componentname
*/
register: function (componentname, cmds) {
commands[componentname] = cmds;
}
};
/*
* constants for easier comparing.
* can be used like foundation.parsekey(event) === foundation.keys.space
*/
function getkeycodes(kcs) {
var k = {};
for (var kc in kcs) {
k[kcs[kc]] = kcs[kc];
}return k;
}
foundation.keyboard = keyboard;
}(jquery);
'use strict';
!function ($) {
// default set of media queries
var defaultqueries = {
'default': 'only screen',
landscape: 'only screen and (orientation: landscape)',
portrait: 'only screen and (orientation: portrait)',
retina: 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 'only screen and (min--moz-device-pixel-ratio: 2),' + 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 'only screen and (min-device-pixel-ratio: 2),' + 'only screen and (min-resolution: 192dpi),' + 'only screen and (min-resolution: 2dppx)'
};
var mediaquery = {
queries: [],
current: '',
/**
* initializes the media query helper, by extracting the breakpoint list from the css and activating the breakpoint watcher.
* @function
* @private
*/
_init: function () {
var self = this;
var extractedstyles = $('.foundation-mq').css('font-family');
var namedqueries;
namedqueries = parsestyletoobject(extractedstyles);
for (var key in namedqueries) {
if (namedqueries.hasownproperty(key)) {
self.queries.push({
name: key,
value: 'only screen and (min-width: ' + namedqueries[key] + ')'
});
}
}
this.current = this._getcurrentsize();
this._watcher();
},
/**
* checks if the screen is at least as wide as a breakpoint.
* @function
* @param {string} size - name of the breakpoint to check.
* @returns {boolean} `true` if the breakpoint matches, `false` if it's smaller.
*/
atleast: function (size) {
var query = this.get(size);
if (query) {
return window.matchmedia(query).matches;
}
return false;
},
/**
* gets the media query of a breakpoint.
* @function
* @param {string} size - name of the breakpoint to get.
* @returns {string|null} - the media query of the breakpoint, or `null` if the breakpoint doesn't exist.
*/
get: function (size) {
for (var i in this.queries) {
if (this.queries.hasownproperty(i)) {
var query = this.queries[i];
if (size === query.name) return query.value;
}
}
return null;
},
/**
* gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one).
* @function
* @private
* @returns {string} name of the current breakpoint.
*/
_getcurrentsize: function () {
var matched;
for (var i = 0; i < this.queries.length; i++) {
var query = this.queries[i];
if (window.matchmedia(query.value).matches) {
matched = query;
}
}
if (typeof matched === 'object') {
return matched.name;
} else {
return matched;
}
},
/**
* activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes.
* @function
* @private
*/
_watcher: function () {
var _this = this;
$(window).on('resize.zf.mediaquery', function () {
var newsize = _this._getcurrentsize(),
currentsize = _this.current;
if (newsize !== currentsize) {
// change the current media query
_this.current = newsize;
// broadcast the media query change on the window
$(window).trigger('changed.zf.mediaquery', [newsize, currentsize]);
}
});
}
};
foundation.mediaquery = mediaquery;
// matchmedia() polyfill - test a css media type/query in js.
// authors & copyright (c) 2012: scott jehl, paul irish, nicholas zakas, david knight. dual mit/bsd license
window.matchmedia || (window.matchmedia = function () {
'use strict';
// for browsers that support matchmedium api such as ie 9 and webkit
var stylemedia = window.stylemedia || window.media;
// for those that don't support matchmedium
if (!stylemedia) {
var style = document.createelement('style'),
script = document.getelementsbytagname('script')[0],
info = null;
style.type = 'text/css';
style.id = 'matchmediajs-test';
script.parentnode.insertbefore(style, script);
// 'style.currentstyle' is used by ie <= 8 and 'window.getcomputedstyle' for all other browsers
info = 'getcomputedstyle' in window && window.getcomputedstyle(style, null) || style.currentstyle;
stylemedia = {
matchmedium: function (media) {
var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
// 'style.stylesheet' is used by ie <= 8 and 'style.textcontent' for all other browsers
if (style.stylesheet) {
style.stylesheet.csstext = text;
} else {
style.textcontent = text;
}
// test if media query is true or false
return info.width === '1px';
}
};
}
return function (media) {
return {
matches: stylemedia.matchmedium(media || 'all'),
media: media || 'all'
};
};
}());
// thank you: https://github.com/sindresorhus/query-string
function parsestyletoobject(str) {
var styleobject = {};
if (typeof str !== 'string') {
return styleobject;
}
str = str.trim().slice(1, -1); // browsers re-quote string style values
if (!str) {
return styleobject;
}
styleobject = str.split('&').reduce(function (ret, param) {
var parts = param.replace(/\+/g, ' ').split('=');
var key = parts[0];
var val = parts[1];
key = decodeuricomponent(key);
// missing `=` should be `null`:
// http://w3.org/tr/2012/wd-url-20120524/#collect-url-parameters
val = val === undefined ? null : decodeuricomponent(val);
if (!ret.hasownproperty(key)) {
ret[key] = val;
} else if (array.isarray(ret[key])) {
ret[key].push(val);
} else {
ret[key] = [ret[key], val];
}
return ret;
}, {});
return styleobject;
}
foundation.mediaquery = mediaquery;
}(jquery);
'use strict';
!function ($) {
/**
* motion module.
* @module foundation.motion
*/
var initclasses = ['mui-enter', 'mui-leave'];
var activeclasses = ['mui-enter-active', 'mui-leave-active'];
var motion = {
animatein: function (element, animation, cb) {
animate(true, element, animation, cb);
},
animateout: function (element, animation, cb) {
animate(false, element, animation, cb);
}
};
function move(duration, elem, fn) {
var anim,
prog,
start = null;
// console.log('called');
function move(ts) {
if (!start) start = window.performance.now();
// console.log(start, ts);
prog = ts - start;
fn.apply(elem);
if (prog < duration) {
anim = window.requestanimationframe(move, elem);
} else {
window.cancelanimationframe(anim);
elem.trigger('finished.zf.animate', [elem]).triggerhandler('finished.zf.animate', [elem]);
}
}
anim = window.requestanimationframe(move);
}
/**
* animates an element in or out using a css transition class.
* @function
* @private
* @param {boolean} isin - defines if the animation is in or out.
* @param {object} element - jquery or html object to animate.
* @param {string} animation - css class to use.
* @param {function} cb - callback to run when animation is finished.
*/
function animate(isin, element, animation, cb) {
element = $(element).eq(0);
if (!element.length) return;
var initclass = isin ? initclasses[0] : initclasses[1];
var activeclass = isin ? activeclasses[0] : activeclasses[1];
// set up the animation
reset();
element.addclass(animation).css('transition', 'none');
requestanimationframe(function () {
element.addclass(initclass);
if (isin) element.show();
});
// start the animation
requestanimationframe(function () {
element[0].offsetwidth;
element.css('transition', '').addclass(activeclass);
});
// clean up the animation when it finishes
element.one(foundation.transitionend(element), finish);
// hides the element (for out animations), resets the element, and runs a callback
function finish() {
if (!isin) element.hide();
reset();
if (cb) cb.apply(element);
}
// resets transitions and removes motion-specific classes
function reset() {
element[0].style.transitionduration = 0;
element.removeclass(initclass + ' ' + activeclass + ' ' + animation);
}
}
foundation.move = move;
foundation.motion = motion;
}(jquery);
'use strict';
!function ($) {
var nest = {
feather: function (menu) {
var type = arguments.length <= 1 || arguments[1] === undefined ? 'zf' : arguments[1];
menu.attr('role', 'menubar');
var items = menu.find('li').attr({ 'role': 'menuitem' }),
submenuclass = 'is-' + type + '-submenu',
subitemclass = submenuclass + '-item',
hassubclass = 'is-' + type + '-submenu-parent';
menu.find('a:first').attr('tabindex', 0);
items.each(function () {
var $item = $(this),
$sub = $item.children('ul');
if ($sub.length) {
$item.addclass(hassubclass).attr({
'aria-haspopup': true,
'aria-expanded': false,
'aria-label': $item.children('a:first').text()
});
$sub.addclass('submenu ' + submenuclass).attr({
'data-submenu': '',
'aria-hidden': true,
'role': 'menu'
});
}
if ($item.parent('[data-submenu]').length) {
$item.addclass('is-submenu-item ' + subitemclass);
}
});
return;
},
burn: function (menu, type) {
var items = menu.find('li').removeattr('tabindex'),
submenuclass = 'is-' + type + '-submenu',
subitemclass = submenuclass + '-item',
hassubclass = 'is-' + type + '-submenu-parent';
menu.find('*').removeclass(submenuclass + ' ' + subitemclass + ' ' + hassubclass + ' is-submenu-item submenu is-active').removeattr('data-submenu').css('display', '');
// console.log( menu.find('.' + submenuclass + ', .' + subitemclass + ', .has-submenu, .is-submenu-item, .submenu, [data-submenu]')
// .removeclass(submenuclass + ' ' + subitemclass + ' has-submenu is-submenu-item submenu')
// .removeattr('data-submenu'));
// items.each(function(){
// var $item = $(this),
// $sub = $item.children('ul');
// if($item.parent('[data-submenu]').length){
// $item.removeclass('is-submenu-item ' + subitemclass);
// }
// if($sub.length){
// $item.removeclass('has-submenu');
// $sub.removeclass('submenu ' + submenuclass).removeattr('data-submenu');
// }
// });
}
};
foundation.nest = nest;
}(jquery);
'use strict';
!function ($) {
function timer(elem, options, cb) {
var _this = this,
duration = options.duration,
//options is an object for easily adding features later.
namespace = object.keys(elem.data())[0] || 'timer',
remain = -1,
start,
timer;
this.ispaused = false;
this.restart = function () {
remain = -1;
cleartimeout(timer);
this.start();
};
this.start = function () {
this.ispaused = false;
// if(!elem.data('paused')){ return false; }//maybe implement this sanity check if used for other things.
cleartimeout(timer);
remain = remain <= 0 ? duration : remain;
elem.data('paused', false);
start = date.now();
timer = settimeout(function () {
if (options.infinite) {
_this.restart(); //rerun the timer.
}
cb();
}, remain);
elem.trigger('timerstart.zf.' + namespace);
};
this.pause = function () {
this.ispaused = true;
//if(elem.data('paused')){ return false; }//maybe implement this sanity check if used for other things.
cleartimeout(timer);
elem.data('paused', true);
var end = date.now();
remain = remain - (end - start);
elem.trigger('timerpaused.zf.' + namespace);
};
}
/**
* runs a callback function when images are fully loaded.
* @param {object} images - image(s) to check if loaded.
* @param {func} callback - function to execute when image is fully loaded.
*/
function onimagesloaded(images, callback) {
var self = this,
unloaded = images.length;
if (unloaded === 0) {
callback();
}
images.each(function () {
if (this.complete) {
singleimageloaded();
} else if (typeof this.naturalwidth !== 'undefined' && this.naturalwidth > 0) {
singleimageloaded();
} else {
$(this).one('load', function () {
singleimageloaded();
});
}
});
function singleimageloaded() {
unloaded--;
if (unloaded === 0) {
callback();
}
}
}
foundation.timer = timer;
foundation.onimagesloaded = onimagesloaded;
}(jquery);
//**************************************************
//**work inspired by multiple jquery swipe plugins**
//**done by yohai ararat ***************************
//**************************************************
(function ($) {
$.spotswipe = {
version: '1.0.0',
enabled: 'ontouchstart' in document.documentelement,
preventdefault: false,
movethreshold: 75,
timethreshold: 200
};
var startposx,
startposy,
starttime,
elapsedtime,
ismoving = false;
function ontouchend() {
// alert(this);
this.removeeventlistener('touchmove', ontouchmove);
this.removeeventlistener('touchend', ontouchend);
ismoving = false;
}
function ontouchmove(e) {
if ($.spotswipe.preventdefault) {
e.preventdefault();
}
if (ismoving) {
var x = e.touches[0].pagex;
var y = e.touches[0].pagey;
var dx = startposx - x;
var dy = startposy - y;
var dir;
elapsedtime = new date().gettime() - starttime;
if (math.abs(dx) >= $.spotswipe.movethreshold && elapsedtime <= $.spotswipe.timethreshold) {
dir = dx > 0 ? 'left' : 'right';
}
// else if(math.abs(dy) >= $.spotswipe.movethreshold && elapsedtime <= $.spotswipe.timethreshold) {
// dir = dy > 0 ? 'down' : 'up';
// }
if (dir) {
e.preventdefault();
ontouchend.call(this);
$(this).trigger('swipe', dir).trigger('swipe' + dir);
}
}
}
function ontouchstart(e) {
if (e.touches.length == 1) {
startposx = e.touches[0].pagex;
startposy = e.touches[0].pagey;
ismoving = true;
starttime = new date().gettime();
this.addeventlistener('touchmove', ontouchmove, false);
this.addeventlistener('touchend', ontouchend, false);
}
}
function init() {
this.addeventlistener && this.addeventlistener('touchstart', ontouchstart, false);
}
function teardown() {
this.removeeventlistener('touchstart', ontouchstart);
}
$.event.special.swipe = { setup: init };
$.each(['left', 'up', 'down', 'right'], function () {
$.event.special['swipe' + this] = { setup: function () {
$(this).on('swipe', $.noop);
} };
});
})(jquery);
/****************************************************
* method for adding psuedo drag events to elements *
***************************************************/
!function ($) {
$.fn.addtouch = function () {
this.each(function (i, el) {
$(el).bind('touchstart touchmove touchend touchcancel', function () {
//we pass the original event object because the jquery event
//object is normalized to w3c specs and does not provide the touchlist
handletouch(event);
});
});
var handletouch = function (event) {
var touches = event.changedtouches,
first = touches[0],
eventtypes = {
touchstart: 'mousedown',
touchmove: 'mousemove',
touchend: 'mouseup'
},
type = eventtypes[event.type],
simulatedevent;
if ('mouseevent' in window && typeof window.mouseevent === 'function') {
simulatedevent = new window.mouseevent(type, {
'bubbles': true,
'cancelable': true,
'screenx': first.screenx,
'screeny': first.screeny,
'clientx': first.clientx,
'clienty': first.clienty
});
} else {
simulatedevent = document.createevent('mouseevent');
simulatedevent.initmouseevent(type, true, true, window, 1, first.screenx, first.screeny, first.clientx, first.clienty, false, false, false, false, 0 /*left*/, null);
}
first.target.dispatchevent(simulatedevent);
};
};
}(jquery);
//**********************************
//**from the jquery mobile library**
//**need to recreate functionality**
//**and try to improve if possible**
//**********************************
/* removing the jquery function ****
************************************
(function( $, window, undefined ) {
var $document = $( document ),
// supporttouch = $.mobile.support.touch,
touchstartevent = 'touchstart'//supporttouch ? "touchstart" : "mousedown",
touchstopevent = 'touchend'//supporttouch ? "touchend" : "mouseup",
touchmoveevent = 'touchmove'//supporttouch ? "touchmove" : "mousemove";
// setup new event shortcuts
$.each( ( "touchstart touchmove touchend " +
"swipe swipeleft swiperight" ).split( " " ), function( i, name ) {
$.fn[ name ] = function( fn ) {
return fn ? this.bind( name, fn ) : this.trigger( name );
};
// jquery < 1.8
if ( $.attrfn ) {
$.attrfn[ name ] = true;
}
});
function triggercustomevent( obj, eventtype, event, bubble ) {
var originaltype = event.type;
event.type = eventtype;
if ( bubble ) {
$.event.trigger( event, undefined, obj );
} else {
$.event.dispatch.call( obj, event );
}
event.type = originaltype;
}
// also handles taphold
// also handles swipeleft, swiperight
$.event.special.swipe = {
// more than this horizontal displacement, and we will suppress scrolling.
scrollsupressionthreshold: 30,
// more time than this, and it isn't a swipe.
durationthreshold: 1000,
// swipe horizontal displacement must be more than this.
horizontaldistancethreshold: window.devicepixelratio >= 2 ? 15 : 30,
// swipe vertical displacement must be less than this.
verticaldistancethreshold: window.devicepixelratio >= 2 ? 15 : 30,
getlocation: function ( event ) {
var winpagex = window.pagexoffset,
winpagey = window.pageyoffset,
x = event.clientx,
y = event.clienty;
if ( event.pagey === 0 && math.floor( y ) > math.floor( event.pagey ) ||
event.pagex === 0 && math.floor( x ) > math.floor( event.pagex ) ) {
// ios4 clientx/clienty have the value that should have been
// in pagex/pagey. while pagex/page/ have the value 0
x = x - winpagex;
y = y - winpagey;
} else if ( y < ( event.pagey - winpagey) || x < ( event.pagex - winpagex ) ) {
// some android browsers have totally bogus values for clientx/y
// when scrolling/zooming a page. detectable since clientx/clienty
// should never be smaller than pagex/pagey minus page scroll
x = event.pagex - winpagex;
y = event.pagey - winpagey;
}
return {
x: x,
y: y
};
},
start: function( event ) {
var data = event.originalevent.touches ?
event.originalevent.touches[ 0 ] : event,
location = $.event.special.swipe.getlocation( data );
return {
time: ( new date() ).gettime(),
coords: [ location.x, location.y ],
origin: $( event.target )
};
},
stop: function( event ) {
var data = event.originalevent.touches ?
event.originalevent.touches[ 0 ] : event,
location = $.event.special.swipe.getlocation( data );
return {
time: ( new date() ).gettime(),
coords: [ location.x, location.y ]
};
},
handleswipe: function( start, stop, thisobject, origtarget ) {
if ( stop.time - start.time < $.event.special.swipe.durationthreshold &&
math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontaldistancethreshold &&
math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticaldistancethreshold ) {
var direction = start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight";
triggercustomevent( thisobject, "swipe", $.event( "swipe", { target: origtarget, swipestart: start, swipestop: stop }), true );
triggercustomevent( thisobject, direction,$.event( direction, { target: origtarget, swipestart: start, swipestop: stop } ), true );
return true;
}
return false;
},
// this serves as a flag to ensure that at most one swipe event event is
// in work at any given time
eventinprogress: false,
setup: function() {
var events,
thisobject = this,
$this = $( thisobject ),
context = {};
// retrieve the events data for this element and add the swipe context
events = $.data( this, "mobile-events" );
if ( !events ) {
events = { length: 0 };
$.data( this, "mobile-events", events );
}
events.length++;
events.swipe = context;
context.start = function( event ) {
// bail if we're already working on a swipe event
if ( $.event.special.swipe.eventinprogress ) {
return;
}
$.event.special.swipe.eventinprogress = true;
var stop,
start = $.event.special.swipe.start( event ),
origtarget = event.target,
emitted = false;
context.move = function( event ) {
if ( !start || event.isdefaultprevented() ) {
return;
}
stop = $.event.special.swipe.stop( event );
if ( !emitted ) {
emitted = $.event.special.swipe.handleswipe( start, stop, thisobject, origtarget );
if ( emitted ) {
// reset the context to make way for the next swipe event
$.event.special.swipe.eventinprogress = false;
}
}
// prevent scrolling
if ( math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollsupressionthreshold ) {
event.preventdefault();
}
};
context.stop = function() {
emitted = true;
// reset the context to make way for the next swipe event
$.event.special.swipe.eventinprogress = false;
$document.off( touchmoveevent, context.move );
context.move = null;
};
$document.on( touchmoveevent, context.move )
.one( touchstopevent, context.stop );
};
$this.on( touchstartevent, context.start );
},
teardown: function() {
var events, context;
events = $.data( this, "mobile-events" );
if ( events ) {
context = events.swipe;
delete events.swipe;
events.length--;
if ( events.length === 0 ) {
$.removedata( this, "mobile-events" );
}
}
if ( context ) {
if ( context.start ) {
$( this ).off( touchstartevent, context.start );
}
if ( context.move ) {
$document.off( touchmoveevent, context.move );
}
if ( context.stop ) {
$document.off( touchstopevent, context.stop );
}
}
}
};
$.each({
swipeleft: "swipe.left",
swiperight: "swipe.right"
}, function( event, sourceevent ) {
$.event.special[ event ] = {
setup: function() {
$( this ).bind( sourceevent, $.noop );
},
teardown: function() {
$( this ).unbind( sourceevent );
}
};
});
})( jquery, this );
*/
'use strict';
!function ($) {
var mutationobserver = function () {
var prefixes = ['webkit', 'moz', 'o', 'ms', ''];
for (var i = 0; i < prefixes.length; i++) {
if (prefixes[i] + 'mutationobserver' in window) {
return window[prefixes[i] + 'mutationobserver'];
}
}
return false;
}();
var triggers = function (el, type) {
el.data(type).split(' ').foreach(function (id) {
$('#' + id)[type === 'close' ? 'trigger' : 'triggerhandler'](type + '.zf.trigger', [el]);
});
};
// elements with [data-open] will reveal a plugin that supports it when clicked.
$(document).on('click.zf.trigger', '[data-open]', function () {
triggers($(this), 'open');
});
// elements with [data-close] will close a plugin that supports it when clicked.
// if used without a value on [data-close], the event will bubble, allowing it to close a parent component.
$(document).on('click.zf.trigger', '[data-close]', function () {
var id = $(this).data('close');
if (id) {
triggers($(this), 'close');
} else {
$(this).trigger('close.zf.trigger');
}
});
// elements with [data-toggle] will toggle a plugin that supports it when clicked.
$(document).on('click.zf.trigger', '[data-toggle]', function () {
triggers($(this), 'toggle');
});
// elements with [data-closable] will respond to close.zf.trigger events.
$(document).on('close.zf.trigger', '[data-closable]', function (e) {
e.stoppropagation();
var animation = $(this).data('closable');
if (animation !== '') {
foundation.motion.animateout($(this), animation, function () {
$(this).trigger('closed.zf');
});
} else {
$(this).fadeout().trigger('closed.zf');
}
});
$(document).on('focus.zf.trigger blur.zf.trigger', '[data-toggle-focus]', function () {
var id = $(this).data('toggle-focus');
$('#' + id).triggerhandler('toggle.zf.trigger', [$(this)]);
});
/**
* fires once after all other scripts have loaded
* @function
* @private
*/
$(window).load(function () {
checklisteners();
});
function checklisteners() {
eventslistener();
resizelistener();
scrolllistener();
closemelistener();
}
//******** only fires this function once on load, if there's something to watch ********
function closemelistener(pluginname) {
var yetiboxes = $('[data-yeti-box]'),
plugnames = ['dropdown', 'tooltip', 'reveal'];
if (pluginname) {
if (typeof pluginname === 'string') {
plugnames.push(pluginname);
} else if (typeof pluginname === 'object' && typeof pluginname[0] === 'string') {
plugnames.concat(pluginname);
} else {
console.error('plugin names must be strings');
}
}
if (yetiboxes.length) {
var listeners = plugnames.map(function (name) {
return 'closeme.zf.' + name;
}).join(' ');
$(window).off(listeners).on(listeners, function (e, pluginid) {
var plugin = e.namespace.split('.')[0];
var plugins = $('[data-' + plugin + ']').not('[data-yeti-box="' + pluginid + '"]');
plugins.each(function () {
var _this = $(this);
_this.triggerhandler('close.zf.trigger', [_this]);
});
});
}
}
function resizelistener(debounce) {
var timer = void 0,
$nodes = $('[data-resize]');
if ($nodes.length) {
$(window).off('resize.zf.trigger').on('resize.zf.trigger', function (e) {
if (timer) {
cleartimeout(timer);
}
timer = settimeout(function () {
if (!mutationobserver) {
//fallback for ie 9
$nodes.each(function () {
$(this).triggerhandler('resizeme.zf.trigger');
});
}
//trigger all listening elements and signal a resize event
$nodes.attr('data-events', "resize");
}, debounce || 10); //default time to emit resize event
});
}
}
function scrolllistener(debounce) {
var timer = void 0,
$nodes = $('[data-scroll]');
if ($nodes.length) {
$(window).off('scroll.zf.trigger').on('scroll.zf.trigger', function (e) {
if (timer) {
cleartimeout(timer);
}
timer = settimeout(function () {
if (!mutationobserver) {
//fallback for ie 9
$nodes.each(function () {
$(this).triggerhandler('scrollme.zf.trigger');
});
}
//trigger all listening elements and signal a scroll event
$nodes.attr('data-events', "scroll");
}, debounce || 10); //default time to emit scroll event
});
}
}
function eventslistener() {
if (!mutationobserver) {
return false;
}
var nodes = document.queryselectorall('[data-resize], [data-scroll], [data-mutate]');
//element callback
var listeningelementsmutation = function (mutationrecordslist) {
var $target = $(mutationrecordslist[0].target);
//trigger the event handler for the element depending on type
switch ($target.attr("data-events")) {
case "resize":
$target.triggerhandler('resizeme.zf.trigger', [$target]);
break;
case "scroll":
$target.triggerhandler('scrollme.zf.trigger', [$target, window.pageyoffset]);
break;
// case "mutate" :
// console.log('mutate', $target);
// $target.triggerhandler('mutate.zf.trigger');
//
// //make sure we don't get stuck in an infinite loop from sloppy codeing
// if ($target.index('[data-mutate]') == $("[data-mutate]").length-1) {
// dommutationobserver();
// }
// break;
default:
return false;
//nothing
}
};
if (nodes.length) {
//for each element that needs to listen for resizing, scrolling, (or coming soon mutation) add a single observer
for (var i = 0; i <= nodes.length - 1; i++) {
var elementobserver = new mutationobserver(listeningelementsmutation);
elementobserver.observe(nodes[i], { attributes: true, childlist: false, characterdata: false, subtree: false, attributefilter: ["data-events"] });
}
}
}
// ------------------------------------
// [ph]
// foundation.checkwatchers = checkwatchers;
foundation.ihearyou = checklisteners;
// foundation.iseeyou = scrolllistener;
// foundation.ifeelyou = closemelistener;
}(jquery);
// function dommutationobserver(debounce) {
// // !!! this is coming soon and needs more work; not active !!! //
// var timer,
// nodes = document.queryselectorall('[data-mutate]');
// //
// if (nodes.length) {
// // var mutationobserver = (function () {
// // var prefixes = ['webkit', 'moz', 'o', 'ms', ''];
// // for (var i=0; i < prefixes.length; i++) {
// // if (prefixes[i] + 'mutationobserver' in window) {
// // return window[prefixes[i] + 'mutationobserver'];
// // }
// // }
// // return false;
// // }());
//
//
// //for the body, we need to listen for all changes effecting the style and class attributes
// var bodyobserver = new mutationobserver(bodymutation);
// bodyobserver.observe(document.body, { attributes: true, childlist: true, characterdata: false, subtree:true, attributefilter:["style", "class"]});
//
//
// //body callback
// function bodymutation(mutate) {
// //trigger all listening elements and signal a mutation event
// if (timer) { cleartimeout(timer); }
//
// timer = settimeout(function() {
// bodyobserver.disconnect();
// $('[data-mutate]').attr('data-events',"mutate");
// }, debounce || 150);
// }
// }
// }
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* abide module.
* @module foundation.abide
*/
var abide = function () {
/**
* creates a new instance of abide.
* @class
* @fires abide#init
* @param {object} element - jquery object to add the trigger to.
* @param {object} options - overrides to the default plugin settings.
*/
function abide(element) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
_classcallcheck(this, abide);
this.$element = element;
this.options = $.extend({}, abide.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'abide');
}
/**
* initializes the abide plugin and calls functions to get abide functioning on load.
* @private
*/
_createclass(abide, [{
key: '_init',
value: function _init() {
this.$inputs = this.$element.find('input, textarea, select');
this._events();
}
/**
* initializes events for abide.
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this2 = this;
this.$element.off('.abide').on('reset.zf.abide', function () {
_this2.resetform();
}).on('submit.zf.abide', function () {
return _this2.validateform();
});
if (this.options.validateon === 'fieldchange') {
this.$inputs.off('change.zf.abide').on('change.zf.abide', function (e) {
_this2.validateinput($(e.target));
});
}
if (this.options.livevalidate) {
this.$inputs.off('input.zf.abide').on('input.zf.abide', function (e) {
_this2.validateinput($(e.target));
});
}
}
/**
* calls necessary functions to update abide upon dom change
* @private
*/
}, {
key: '_reflow',
value: function _reflow() {
this._init();
}
/**
* checks whether or not a form element has the required attribute and if it's checked or not
* @param {object} element - jquery object to check for required attribute
* @returns {boolean} boolean value depends on whether or not attribute is checked or empty
*/
}, {
key: 'requiredcheck',
value: function requiredcheck($el) {
if (!$el.attr('required')) return true;
var isgood = true;
switch ($el[0].type) {
case 'checkbox':
isgood = $el[0].checked;
break;
case 'select':
case 'select-one':
case 'select-multiple':
var opt = $el.find('option:selected');
if (!opt.length || !opt.val()) isgood = false;
break;
default:
if (!$el.val() || !$el.val().length) isgood = false;
}
return isgood;
}
/**
* based on $el, get the first element with selector in this order:
* 1. the element's direct sibling('s).
* 3. the element's parent's children.
*
* this allows for multiple form errors per input, though if none are found, no form errors will be shown.
*
* @param {object} $el - jquery object to use as reference to find the form error selector.
* @returns {object} jquery object with the selector.
*/
}, {
key: 'findformerror',
value: function findformerror($el) {
var $error = $el.siblings(this.options.formerrorselector);
if (!$error.length) {
$error = $el.parent().find(this.options.formerrorselector);
}
return $error;
}
/**
* get the first element in this order:
* 2. the with the attribute `[for="someinputid"]`
* 3. the `.closest()`
*
* @param {object} $el - jquery object to check for required attribute
* @returns {boolean} boolean value depends on whether or not attribute is checked or empty
*/
}, {
key: 'findlabel',
value: function findlabel($el) {
var id = $el[0].id;
var $label = this.$element.find('label[for="' + id + '"]');
if (!$label.length) {
return $el.closest('label');
}
return $label;
}
/**
* get the set of labels associated with a set of radio els in this order
* 2. the with the attribute `[for="someinputid"]`
* 3. the `.closest()`
*
* @param {object} $el - jquery object to check for required attribute
* @returns {boolean} boolean value depends on whether or not attribute is checked or empty
*/
}, {
key: 'findradiolabels',
value: function findradiolabels($els) {
var _this3 = this;
var labels = $els.map(function (i, el) {
var id = el.id;
var $label = _this3.$element.find('label[for="' + id + '"]');
if (!$label.length) {
$label = $(el).closest('label');
}
return $label[0];
});
return $(labels);
}
/**
* adds the css error class as specified by the abide settings to the label, input, and the form
* @param {object} $el - jquery object to add the class to
*/
}, {
key: 'adderrorclasses',
value: function adderrorclasses($el) {
var $label = this.findlabel($el);
var $formerror = this.findformerror($el);
if ($label.length) {
$label.addclass(this.options.labelerrorclass);
}
if ($formerror.length) {
$formerror.addclass(this.options.formerrorclass);
}
$el.addclass(this.options.inputerrorclass).attr('data-invalid', '');
}
/**
* remove css error classes etc from an entire radio button group
* @param {string} groupname - a string that specifies the name of a radio button group
*
*/
}, {
key: 'removeradioerrorclasses',
value: function removeradioerrorclasses(groupname) {
var $els = this.$element.find(':radio[name="' + groupname + '"]');
var $labels = this.findradiolabels($els);
var $formerrors = this.findformerror($els);
if ($labels.length) {
$labels.removeclass(this.options.labelerrorclass);
}
if ($formerrors.length) {
$formerrors.removeclass(this.options.formerrorclass);
}
$els.removeclass(this.options.inputerrorclass).removeattr('data-invalid');
}
/**
* removes css error class as specified by the abide settings from the label, input, and the form
* @param {object} $el - jquery object to remove the class from
*/
}, {
key: 'removeerrorclasses',
value: function removeerrorclasses($el) {
// radios need to clear all of the els
if ($el[0].type == 'radio') {
return this.removeradioerrorclasses($el.attr('name'));
}
var $label = this.findlabel($el);
var $formerror = this.findformerror($el);
if ($label.length) {
$label.removeclass(this.options.labelerrorclass);
}
if ($formerror.length) {
$formerror.removeclass(this.options.formerrorclass);
}
$el.removeclass(this.options.inputerrorclass).removeattr('data-invalid');
}
/**
* goes through a form to find inputs and proceeds to validate them in ways specific to their type
* @fires abide#invalid
* @fires abide#valid
* @param {object} element - jquery object to validate, should be an html input
* @returns {boolean} goodtogo - if the input is valid or not.
*/
}, {
key: 'validateinput',
value: function validateinput($el) {
var clearrequire = this.requiredcheck($el),
validated = false,
customvalidator = true,
validator = $el.attr('data-validator'),
equalto = true;
// don't validate ignored inputs or hidden inputs
if ($el.is('[data-abide-ignore]') || $el.is('[type="hidden"]')) {
return true;
}
switch ($el[0].type) {
case 'radio':
validated = this.validateradio($el.attr('name'));
break;
case 'checkbox':
validated = clearrequire;
break;
case 'select':
case 'select-one':
case 'select-multiple':
validated = clearrequire;
break;
default:
validated = this.validatetext($el);
}
if (validator) {
customvalidator = this.matchvalidation($el, validator, $el.attr('required'));
}
if ($el.attr('data-equalto')) {
equalto = this.options.validators.equalto($el);
}
var goodtogo = [clearrequire, validated, customvalidator, equalto].indexof(false) === -1;
var message = (goodtogo ? 'valid' : 'invalid') + '.zf.abide';
this[goodtogo ? 'removeerrorclasses' : 'adderrorclasses']($el);
/**
* fires when the input is done checking for validation. event trigger is either `valid.zf.abide` or `invalid.zf.abide`
* trigger includes the dom element of the input.
* @event abide#valid
* @event abide#invalid
*/
$el.trigger(message, [$el]);
return goodtogo;
}
/**
* goes through a form and if there are any invalid inputs, it will display the form error element
* @returns {boolean} noerror - true if no errors were detected...
* @fires abide#formvalid
* @fires abide#forminvalid
*/
}, {
key: 'validateform',
value: function validateform() {
var acc = [];
var _this = this;
this.$inputs.each(function () {
acc.push(_this.validateinput($(this)));
});
var noerror = acc.indexof(false) === -1;
this.$element.find('[data-abide-error]').css('display', noerror ? 'none' : 'block');
/**
* fires when the form is finished validating. event trigger is either `formvalid.zf.abide` or `forminvalid.zf.abide`.
* trigger includes the element of the form.
* @event abide#formvalid
* @event abide#forminvalid
*/
this.$element.trigger((noerror ? 'formvalid' : 'forminvalid') + '.zf.abide', [this.$element]);
return noerror;
}
/**
* determines whether or a not a text input is valid based on the pattern specified in the attribute. if no matching pattern is found, returns true.
* @param {object} $el - jquery object to validate, should be a text input html element
* @param {string} pattern - string value of one of the regex patterns in abide.options.patterns
* @returns {boolean} boolean value depends on whether or not the input value matches the pattern specified
*/
}, {
key: 'validatetext',
value: function validatetext($el, pattern) {
// a pattern can be passed to this function, or it will be infered from the input's "pattern" attribute, or it's "type" attribute
pattern = pattern || $el.attr('pattern') || $el.attr('type');
var inputtext = $el.val();
var valid = false;
if (inputtext.length) {
// if the pattern attribute on the element is in abide's list of patterns, then test that regexp
if (this.options.patterns.hasownproperty(pattern)) {
valid = this.options.patterns[pattern].test(inputtext);
}
// if the pattern name isn't also the type attribute of the field, then test it as a regexp
else if (pattern !== $el.attr('type')) {
valid = new regexp(pattern).test(inputtext);
} else {
valid = true;
}
}
// an empty field is valid if it's not required
else if (!$el.prop('required')) {
valid = true;
}
return valid;
}
/**
* determines whether or a not a radio input is valid based on whether or not it is required and selected. although the function targets a single ` `, it validates by checking the `required` and `checked` properties of all radio buttons in its group.
* @param {string} groupname - a string that specifies the name of a radio button group
* @returns {boolean} boolean value depends on whether or not at least one radio input has been selected (if it's required)
*/
}, {
key: 'validateradio',
value: function validateradio(groupname) {
// if at least one radio in the group has the `required` attribute, the group is considered required
// per w3c spec, all radio buttons in a group should have `required`, but we're being nice
var $group = this.$element.find(':radio[name="' + groupname + '"]');
var valid = false,
required = false;
// for the group to be required, at least one radio needs to be required
$group.each(function (i, e) {
if ($(e).attr('required')) {
required = true;
}
});
if (!required) valid = true;
if (!valid) {
// for the group to be valid, at least one radio needs to be checked
$group.each(function (i, e) {
if ($(e).prop('checked')) {
valid = true;
}
});
};
return valid;
}
/**
* determines if a selected input passes a custom validation function. multiple validations can be used, if passed to the element with `data-validator="foo bar baz"` in a space separated listed.
* @param {object} $el - jquery input element.
* @param {string} validators - a string of function names matching functions in the abide.options.validators object.
* @param {boolean} required - self explanatory?
* @returns {boolean} - true if validations passed.
*/
}, {
key: 'matchvalidation',
value: function matchvalidation($el, validators, required) {
var _this4 = this;
required = required ? true : false;
var clear = validators.split(' ').map(function (v) {
return _this4.options.validators[v]($el, required, $el.parent());
});
return clear.indexof(false) === -1;
}
/**
* resets form inputs and styles
* @fires abide#formreset
*/
}, {
key: 'resetform',
value: function resetform() {
var $form = this.$element,
opts = this.options;
$('.' + opts.labelerrorclass, $form).not('small').removeclass(opts.labelerrorclass);
$('.' + opts.inputerrorclass, $form).not('small').removeclass(opts.inputerrorclass);
$(opts.formerrorselector + '.' + opts.formerrorclass).removeclass(opts.formerrorclass);
$form.find('[data-abide-error]').css('display', 'none');
$(':input', $form).not(':button, :submit, :reset, :hidden, :radio, :checkbox, [data-abide-ignore]').val('').removeattr('data-invalid');
$(':input:radio', $form).not('[data-abide-ignore]').prop('checked', false).removeattr('data-invalid');
$(':input:checkbox', $form).not('[data-abide-ignore]').prop('checked', false).removeattr('data-invalid');
/**
* fires when the form has been reset.
* @event abide#formreset
*/
$form.trigger('formreset.zf.abide', [$form]);
}
/**
* destroys an instance of abide.
* removes error styles and classes from elements, without resetting their values.
*/
}, {
key: 'destroy',
value: function destroy() {
var _this = this;
this.$element.off('.abide').find('[data-abide-error]').css('display', 'none');
this.$inputs.off('.abide').each(function () {
_this.removeerrorclasses($(this));
});
foundation.unregisterplugin(this);
}
}]);
return abide;
}();
/**
* default settings for plugin
*/
abide.defaults = {
/**
* the default event to validate inputs. checkboxes and radios validate immediately.
* remove or change this value for manual validation.
* @option
* @example 'fieldchange'
*/
validateon: 'fieldchange',
/**
* class to be applied to input labels on failed validation.
* @option
* @example 'is-invalid-label'
*/
labelerrorclass: 'is-invalid-label',
/**
* class to be applied to inputs on failed validation.
* @option
* @example 'is-invalid-input'
*/
inputerrorclass: 'is-invalid-input',
/**
* class selector to use to target form errors for show/hide.
* @option
* @example '.form-error'
*/
formerrorselector: '.form-error',
/**
* class added to form errors on failed validation.
* @option
* @example 'is-visible'
*/
formerrorclass: 'is-visible',
/**
* set to true to validate text inputs on any value change.
* @option
* @example false
*/
livevalidate: false,
patterns: {
alpha: /^[a-za-z]+$/,
alpha_numeric: /^[a-za-z0-9]+$/,
integer: /^[-+]?\d+$/,
number: /^[-+]?\d*(?:[\.\,]\d+)?$/,
// amex, visa, diners
card: /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,
cvv: /^([0-9]){3,4}$/,
// http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
email: /^[a-za-z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-za-z0-9](?:[a-za-z0-9-]{0,61}[a-za-z0-9])?(?:\.[a-za-z0-9](?:[a-za-z0-9-]{0,61}[a-za-z0-9])?)+$/,
url: /^(https?|ftp|file|ssh):\/\/(((([a-za-z]|\d|-|\.|_|~|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-za-z]|\d|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])|(([a-za-z]|\d|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])([a-za-z]|\d|-|\.|_|~|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])*([a-za-z]|\d|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])))\.)+(([a-za-z]|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])|(([a-za-z]|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])([a-za-z]|\d|-|\.|_|~|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])*([a-za-z]|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])))\.?)(:\d*)?)(\/((([a-za-z]|\d|-|\.|_|~|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-za-z]|\d|-|\.|_|~|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-za-z]|\d|-|\.|_|~|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\ue000-\uf8ff]|\/|\?)*)?(\#((([a-za-z]|\d|-|\.|_|~|[\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/,
// abc.de
domain: /^([a-za-z0-9]([a-za-z0-9\-]{0,61}[a-za-z0-9])?\.)+[a-za-z]{2,8}$/,
datetime: /^([0-2][0-9]{3})\-([0-1][0-9])\-([0-3][0-9])t([0-5][0-9])\:([0-5][0-9])\:([0-5][0-9])(z|([\-\+]([0-1][0-9])\:00))$/,
// yyyy-mm-dd
date: /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))$/,
// hh:mm:ss
time: /^(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}$/,
dateiso: /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/,
// mm/dd/yyyy
month_day_year: /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.]\d{4}$/,
// dd/mm/yyyy
day_month_year: /^(0[1-9]|[12][0-9]|3[01])[- \/.](0[1-9]|1[012])[- \/.]\d{4}$/,
// #fff or #ffffff
color: /^#?([a-fa-f0-9]{6}|[a-fa-f0-9]{3})$/
},
/**
* optional validation functions to be used. `equalto` being the only default included function.
* functions should return only a boolean if the input is valid or not. functions are given the following arguments:
* el : the jquery element to validate.
* required : boolean value of the required attribute be present or not.
* parent : the direct parent of the input.
* @option
*/
validators: {
equalto: function (el, required, parent) {
return $('#' + el.attr('data-equalto')).val() === el.val();
}
}
};
// window exports
foundation.plugin(abide, 'abide');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* accordion module.
* @module foundation.accordion
* @requires foundation.util.keyboard
* @requires foundation.util.motion
*/
var accordion = function () {
/**
* creates a new instance of an accordion.
* @class
* @fires accordion#init
* @param {jquery} element - jquery object to make into an accordion.
* @param {object} options - a plain object with settings to override the default options.
*/
function accordion(element, options) {
_classcallcheck(this, accordion);
this.$element = element;
this.options = $.extend({}, accordion.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'accordion');
foundation.keyboard.register('accordion', {
'enter': 'toggle',
'space': 'toggle',
'arrow_down': 'next',
'arrow_up': 'previous'
});
}
/**
* initializes the accordion by animating the preset active pane(s).
* @private
*/
_createclass(accordion, [{
key: '_init',
value: function _init() {
this.$element.attr('role', 'tablist');
this.$tabs = this.$element.children('li, [data-accordion-item]');
this.$tabs.each(function (idx, el) {
var $el = $(el),
$content = $el.children('[data-tab-content]'),
id = $content[0].id || foundation.getyodigits(6, 'accordion'),
linkid = el.id || id + '-label';
$el.find('a:first').attr({
'aria-controls': id,
'role': 'tab',
'id': linkid,
'aria-expanded': false,
'aria-selected': false
});
$content.attr({ 'role': 'tabpanel', 'aria-labelledby': linkid, 'aria-hidden': true, 'id': id });
});
var $initactive = this.$element.find('.is-active').children('[data-tab-content]');
if ($initactive.length) {
this.down($initactive, true);
}
this._events();
}
/**
* adds event handlers for items within the accordion.
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
this.$tabs.each(function () {
var $elem = $(this);
var $tabcontent = $elem.children('[data-tab-content]');
if ($tabcontent.length) {
$elem.children('a').off('click.zf.accordion keydown.zf.accordion').on('click.zf.accordion', function (e) {
// $(this).children('a').on('click.zf.accordion', function(e) {
e.preventdefault();
if ($elem.hasclass('is-active')) {
if (_this.options.allowallclosed || $elem.siblings().hasclass('is-active')) {
_this.up($tabcontent);
}
} else {
_this.down($tabcontent);
}
}).on('keydown.zf.accordion', function (e) {
foundation.keyboard.handlekey(e, 'accordion', {
toggle: function () {
_this.toggle($tabcontent);
},
next: function () {
var $a = $elem.next().find('a').focus();
if (!_this.options.multiexpand) {
$a.trigger('click.zf.accordion');
}
},
previous: function () {
var $a = $elem.prev().find('a').focus();
if (!_this.options.multiexpand) {
$a.trigger('click.zf.accordion');
}
},
handled: function () {
e.preventdefault();
e.stoppropagation();
}
});
});
}
});
}
/**
* toggles the selected content pane's open/close state.
* @param {jquery} $target - jquery object of the pane to toggle.
* @function
*/
}, {
key: 'toggle',
value: function toggle($target) {
if ($target.parent().hasclass('is-active')) {
if (this.options.allowallclosed || $target.parent().siblings().hasclass('is-active')) {
this.up($target);
} else {
return;
}
} else {
this.down($target);
}
}
/**
* opens the accordion tab defined by `$target`.
* @param {jquery} $target - accordion pane to open.
* @param {boolean} firsttime - flag to determine if reflow should happen.
* @fires accordion#down
* @function
*/
}, {
key: 'down',
value: function down($target, firsttime) {
var _this2 = this;
if (!this.options.multiexpand && !firsttime) {
var $currentactive = this.$element.children('.is-active').children('[data-tab-content]');
if ($currentactive.length) {
this.up($currentactive);
}
}
$target.attr('aria-hidden', false).parent('[data-tab-content]').addback().parent().addclass('is-active');
$target.slidedown(this.options.slidespeed, function () {
/**
* fires when the tab is done opening.
* @event accordion#down
*/
_this2.$element.trigger('down.zf.accordion', [$target]);
});
$('#' + $target.attr('aria-labelledby')).attr({
'aria-expanded': true,
'aria-selected': true
});
}
/**
* closes the tab defined by `$target`.
* @param {jquery} $target - accordion tab to close.
* @fires accordion#up
* @function
*/
}, {
key: 'up',
value: function up($target) {
var $aunts = $target.parent().siblings(),
_this = this;
var canclose = this.options.multiexpand ? $aunts.hasclass('is-active') : $target.parent().hasclass('is-active');
if (!this.options.allowallclosed && !canclose) {
return;
}
// foundation.move(this.options.slidespeed, $target, function(){
$target.slideup(_this.options.slidespeed, function () {
/**
* fires when the tab is done collapsing up.
* @event accordion#up
*/
_this.$element.trigger('up.zf.accordion', [$target]);
});
// });
$target.attr('aria-hidden', true).parent().removeclass('is-active');
$('#' + $target.attr('aria-labelledby')).attr({
'aria-expanded': false,
'aria-selected': false
});
}
/**
* destroys an instance of an accordion.
* @fires accordion#destroyed
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.find('[data-tab-content]').stop(true).slideup(0).css('display', '');
this.$element.find('a').off('.zf.accordion');
foundation.unregisterplugin(this);
}
}]);
return accordion;
}();
accordion.defaults = {
/**
* amount of time to animate the opening of an accordion pane.
* @option
* @example 250
*/
slidespeed: 250,
/**
* allow the accordion to have multiple open panes.
* @option
* @example false
*/
multiexpand: false,
/**
* allow the accordion to close all panes.
* @option
* @example false
*/
allowallclosed: false
};
// window exports
foundation.plugin(accordion, 'accordion');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* accordionmenu module.
* @module foundation.accordionmenu
* @requires foundation.util.keyboard
* @requires foundation.util.motion
* @requires foundation.util.nest
*/
var accordionmenu = function () {
/**
* creates a new instance of an accordion menu.
* @class
* @fires accordionmenu#init
* @param {jquery} element - jquery object to make into an accordion menu.
* @param {object} options - overrides to the default plugin settings.
*/
function accordionmenu(element, options) {
_classcallcheck(this, accordionmenu);
this.$element = element;
this.options = $.extend({}, accordionmenu.defaults, this.$element.data(), options);
foundation.nest.feather(this.$element, 'accordion');
this._init();
foundation.registerplugin(this, 'accordionmenu');
foundation.keyboard.register('accordionmenu', {
'enter': 'toggle',
'space': 'toggle',
'arrow_right': 'open',
'arrow_up': 'up',
'arrow_down': 'down',
'arrow_left': 'close',
'escape': 'closeall',
'tab': 'down',
'shift_tab': 'up'
});
}
/**
* initializes the accordion menu by hiding all nested menus.
* @private
*/
_createclass(accordionmenu, [{
key: '_init',
value: function _init() {
this.$element.find('[data-submenu]').not('.is-active').slideup(0); //.find('a').css('padding-left', '1rem');
this.$element.attr({
'role': 'tablist',
'aria-multiselectable': this.options.multiopen
});
this.$menulinks = this.$element.find('.is-accordion-submenu-parent');
this.$menulinks.each(function () {
var linkid = this.id || foundation.getyodigits(6, 'acc-menu-link'),
$elem = $(this),
$sub = $elem.children('[data-submenu]'),
subid = $sub[0].id || foundation.getyodigits(6, 'acc-menu'),
isactive = $sub.hasclass('is-active');
$elem.attr({
'aria-controls': subid,
'aria-expanded': isactive,
'role': 'tab',
'id': linkid
});
$sub.attr({
'aria-labelledby': linkid,
'aria-hidden': !isactive,
'role': 'tabpanel',
'id': subid
});
});
var initpanes = this.$element.find('.is-active');
if (initpanes.length) {
var _this = this;
initpanes.each(function () {
_this.down($(this));
});
}
this._events();
}
/**
* adds event handlers for items within the menu.
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
this.$element.find('li').each(function () {
var $submenu = $(this).children('[data-submenu]');
if ($submenu.length) {
$(this).children('a').off('click.zf.accordionmenu').on('click.zf.accordionmenu', function (e) {
e.preventdefault();
_this.toggle($submenu);
});
}
}).on('keydown.zf.accordionmenu', function (e) {
var $element = $(this),
$elements = $element.parent('ul').children('li'),
$prevelement,
$nextelement,
$target = $element.children('[data-submenu]');
$elements.each(function (i) {
if ($(this).is($element)) {
$prevelement = $elements.eq(math.max(0, i - 1)).find('a').first();
$nextelement = $elements.eq(math.min(i + 1, $elements.length - 1)).find('a').first();
if ($(this).children('[data-submenu]:visible').length) {
// has open sub menu
$nextelement = $element.find('li:first-child').find('a').first();
}
if ($(this).is(':first-child')) {
// is first element of sub menu
$prevelement = $element.parents('li').first().find('a').first();
} else if ($prevelement.children('[data-submenu]:visible').length) {
// if previous element has open sub menu
$prevelement = $prevelement.find('li:last-child').find('a').first();
}
if ($(this).is(':last-child')) {
// is last element of sub menu
$nextelement = $element.parents('li').first().next('li').find('a').first();
}
return;
}
});
foundation.keyboard.handlekey(e, 'accordionmenu', {
open: function () {
if ($target.is(':hidden')) {
_this.down($target);
$target.find('li').first().find('a').first().focus();
}
},
close: function () {
if ($target.length && !$target.is(':hidden')) {
// close active sub of this item
_this.up($target);
} else if ($element.parent('[data-submenu]').length) {
// close currently open sub
_this.up($element.parent('[data-submenu]'));
$element.parents('li').first().find('a').first().focus();
}
},
up: function () {
$prevelement.attr('tabindex', -1).focus();
return true;
},
down: function () {
$nextelement.attr('tabindex', -1).focus();
return true;
},
toggle: function () {
if ($element.children('[data-submenu]').length) {
_this.toggle($element.children('[data-submenu]'));
}
},
closeall: function () {
_this.hideall();
},
handled: function (preventdefault) {
if (preventdefault) {
e.preventdefault();
}
e.stopimmediatepropagation();
}
});
}); //.attr('tabindex', 0);
}
/**
* closes all panes of the menu.
* @function
*/
}, {
key: 'hideall',
value: function hideall() {
this.$element.find('[data-submenu]').slideup(this.options.slidespeed);
}
/**
* toggles the open/close state of a submenu.
* @function
* @param {jquery} $target - the submenu to toggle
*/
}, {
key: 'toggle',
value: function toggle($target) {
if (!$target.is(':animated')) {
if (!$target.is(':hidden')) {
this.up($target);
} else {
this.down($target);
}
}
}
/**
* opens the sub-menu defined by `$target`.
* @param {jquery} $target - sub-menu to open.
* @fires accordionmenu#down
*/
}, {
key: 'down',
value: function down($target) {
var _this = this;
if (!this.options.multiopen) {
this.up(this.$element.find('.is-active').not($target.parentsuntil(this.$element).add($target)));
}
$target.addclass('is-active').attr({ 'aria-hidden': false }).parent('.is-accordion-submenu-parent').attr({ 'aria-expanded': true });
//foundation.move(this.options.slidespeed, $target, function() {
$target.slidedown(_this.options.slidespeed, function () {
/**
* fires when the menu is done opening.
* @event accordionmenu#down
*/
_this.$element.trigger('down.zf.accordionmenu', [$target]);
});
//});
}
/**
* closes the sub-menu defined by `$target`. all sub-menus inside the target will be closed as well.
* @param {jquery} $target - sub-menu to close.
* @fires accordionmenu#up
*/
}, {
key: 'up',
value: function up($target) {
var _this = this;
//foundation.move(this.options.slidespeed, $target, function(){
$target.slideup(_this.options.slidespeed, function () {
/**
* fires when the menu is done collapsing up.
* @event accordionmenu#up
*/
_this.$element.trigger('up.zf.accordionmenu', [$target]);
});
//});
var $menus = $target.find('[data-submenu]').slideup(0).addback().attr('aria-hidden', true);
$menus.parent('.is-accordion-submenu-parent').attr('aria-expanded', false);
}
/**
* destroys an instance of accordion menu.
* @fires accordionmenu#destroyed
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.find('[data-submenu]').slidedown(0).css('display', '');
this.$element.find('a').off('click.zf.accordionmenu');
foundation.nest.burn(this.$element, 'accordion');
foundation.unregisterplugin(this);
}
}]);
return accordionmenu;
}();
accordionmenu.defaults = {
/**
* amount of time to animate the opening of a submenu in ms.
* @option
* @example 250
*/
slidespeed: 250,
/**
* allow the menu to have multiple open panes.
* @option
* @example true
*/
multiopen: true
};
// window exports
foundation.plugin(accordionmenu, 'accordionmenu');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* drilldown module.
* @module foundation.drilldown
* @requires foundation.util.keyboard
* @requires foundation.util.motion
* @requires foundation.util.nest
*/
var drilldown = function () {
/**
* creates a new instance of a drilldown menu.
* @class
* @param {jquery} element - jquery object to make into an accordion menu.
* @param {object} options - overrides to the default plugin settings.
*/
function drilldown(element, options) {
_classcallcheck(this, drilldown);
this.$element = element;
this.options = $.extend({}, drilldown.defaults, this.$element.data(), options);
foundation.nest.feather(this.$element, 'drilldown');
this._init();
foundation.registerplugin(this, 'drilldown');
foundation.keyboard.register('drilldown', {
'enter': 'open',
'space': 'open',
'arrow_right': 'next',
'arrow_up': 'up',
'arrow_down': 'down',
'arrow_left': 'previous',
'escape': 'close',
'tab': 'down',
'shift_tab': 'up'
});
}
/**
* initializes the drilldown by creating jquery collections of elements
* @private
*/
_createclass(drilldown, [{
key: '_init',
value: function _init() {
this.$submenuanchors = this.$element.find('li.is-drilldown-submenu-parent').children('a');
this.$submenus = this.$submenuanchors.parent('li').children('[data-submenu]');
this.$menuitems = this.$element.find('li').not('.js-drilldown-back').attr('role', 'menuitem').find('a');
this._preparemenu();
this._keyboardevents();
}
/**
* prepares drilldown menu by setting attributes to links and elements
* sets a min height to prevent content jumping
* wraps the element if not already wrapped
* @private
* @function
*/
}, {
key: '_preparemenu',
value: function _preparemenu() {
var _this = this;
// if(!this.options.holdopen){
// this._menulinkevents();
// }
this.$submenuanchors.each(function () {
var $link = $(this);
var $sub = $link.parent();
if (_this.options.parentlink) {
$link.clone().prependto($sub.children('[data-submenu]')).wrap('');
}
$link.data('savedhref', $link.attr('href')).removeattr('href');
$link.children('[data-submenu]').attr({
'aria-hidden': true,
'tabindex': 0,
'role': 'menu'
});
_this._events($link);
});
this.$submenus.each(function () {
var $menu = $(this),
$back = $menu.find('.js-drilldown-back');
if (!$back.length) {
$menu.prepend(_this.options.backbutton);
}
_this._back($menu);
});
if (!this.$element.parent().hasclass('is-drilldown')) {
this.$wrapper = $(this.options.wrapper).addclass('is-drilldown');
this.$wrapper = this.$element.wrap(this.$wrapper).parent().css(this._getmaxdims());
}
}
/**
* adds event handlers to elements in the menu.
* @function
* @private
* @param {jquery} $elem - the current menu item to add handlers to.
*/
}, {
key: '_events',
value: function _events($elem) {
var _this = this;
$elem.off('click.zf.drilldown').on('click.zf.drilldown', function (e) {
if ($(e.target).parentsuntil('ul', 'li').hasclass('is-drilldown-submenu-parent')) {
e.stopimmediatepropagation();
e.preventdefault();
}
// if(e.target !== e.currenttarget.firstelementchild){
// return false;
// }
_this._show($elem.parent('li'));
if (_this.options.closeonclick) {
var $body = $('body');
$body.off('.zf.drilldown').on('click.zf.drilldown', function (e) {
if (e.target === _this.$element[0] || $.contains(_this.$element[0], e.target)) {
return;
}
e.preventdefault();
_this._hideall();
$body.off('.zf.drilldown');
});
}
});
}
/**
* adds keydown event listener to `li`'s in the menu.
* @private
*/
}, {
key: '_keyboardevents',
value: function _keyboardevents() {
var _this = this;
this.$menuitems.add(this.$element.find('.js-drilldown-back > a')).on('keydown.zf.drilldown', function (e) {
var $element = $(this),
$elements = $element.parent('li').parent('ul').children('li').children('a'),
$prevelement,
$nextelement;
$elements.each(function (i) {
if ($(this).is($element)) {
$prevelement = $elements.eq(math.max(0, i - 1));
$nextelement = $elements.eq(math.min(i + 1, $elements.length - 1));
return;
}
});
foundation.keyboard.handlekey(e, 'drilldown', {
next: function () {
if ($element.is(_this.$submenuanchors)) {
_this._show($element.parent('li'));
$element.parent('li').one(foundation.transitionend($element), function () {
$element.parent('li').find('ul li a').filter(_this.$menuitems).first().focus();
});
return true;
}
},
previous: function () {
_this._hide($element.parent('li').parent('ul'));
$element.parent('li').parent('ul').one(foundation.transitionend($element), function () {
settimeout(function () {
$element.parent('li').parent('ul').parent('li').children('a').first().focus();
}, 1);
});
return true;
},
up: function () {
$prevelement.focus();
return true;
},
down: function () {
$nextelement.focus();
return true;
},
close: function () {
_this._back();
//_this.$menuitems.first().focus(); // focus to first element
},
open: function () {
if (!$element.is(_this.$menuitems)) {
// not menu item means back button
_this._hide($element.parent('li').parent('ul'));
$element.parent('li').parent('ul').one(foundation.transitionend($element), function () {
settimeout(function () {
$element.parent('li').parent('ul').parent('li').children('a').first().focus();
}, 1);
});
} else if ($element.is(_this.$submenuanchors)) {
_this._show($element.parent('li'));
$element.parent('li').one(foundation.transitionend($element), function () {
$element.parent('li').find('ul li a').filter(_this.$menuitems).first().focus();
});
}
return true;
},
handled: function (preventdefault) {
if (preventdefault) {
e.preventdefault();
}
e.stopimmediatepropagation();
}
});
}); // end keyboardaccess
}
/**
* closes all open elements, and returns to root menu.
* @function
* @fires drilldown#closed
*/
}, {
key: '_hideall',
value: function _hideall() {
var $elem = this.$element.find('.is-drilldown-submenu.is-active').addclass('is-closing');
$elem.one(foundation.transitionend($elem), function (e) {
$elem.removeclass('is-active is-closing');
});
/**
* fires when the menu is fully closed.
* @event drilldown#closed
*/
this.$element.trigger('closed.zf.drilldown');
}
/**
* adds event listener for each `back` button, and closes open menus.
* @function
* @fires drilldown#back
* @param {jquery} $elem - the current sub-menu to add `back` event.
*/
}, {
key: '_back',
value: function _back($elem) {
var _this = this;
$elem.off('click.zf.drilldown');
$elem.children('.js-drilldown-back').on('click.zf.drilldown', function (e) {
e.stopimmediatepropagation();
// console.log('mouseup on back');
_this._hide($elem);
});
}
/**
* adds event listener to menu items w/o submenus to close open menus on click.
* @function
* @private
*/
}, {
key: '_menulinkevents',
value: function _menulinkevents() {
var _this = this;
this.$menuitems.not('.is-drilldown-submenu-parent').off('click.zf.drilldown').on('click.zf.drilldown', function (e) {
// e.stopimmediatepropagation();
settimeout(function () {
_this._hideall();
}, 0);
});
}
/**
* opens a submenu.
* @function
* @fires drilldown#open
* @param {jquery} $elem - the current element with a submenu to open, i.e. the `li` tag.
*/
}, {
key: '_show',
value: function _show($elem) {
$elem.children('[data-submenu]').addclass('is-active');
/**
* fires when the submenu has opened.
* @event drilldown#open
*/
this.$element.trigger('open.zf.drilldown', [$elem]);
}
}, {
key: '_hide',
/**
* hides a submenu
* @function
* @fires drilldown#hide
* @param {jquery} $elem - the current sub-menu to hide, i.e. the `ul` tag.
*/
value: function _hide($elem) {
var _this = this;
$elem.addclass('is-closing').one(foundation.transitionend($elem), function () {
$elem.removeclass('is-active is-closing');
$elem.blur();
});
/**
* fires when the submenu has closed.
* @event drilldown#hide
*/
$elem.trigger('hide.zf.drilldown', [$elem]);
}
/**
* iterates through the nested menus to calculate the min-height, and max-width for the menu.
* prevents content jumping.
* @function
* @private
*/
}, {
key: '_getmaxdims',
value: function _getmaxdims() {
var max = 0,
result = {};
this.$submenus.add(this.$element).each(function () {
var numofelems = $(this).children('li').length;
max = numofelems > max ? numofelems : max;
});
result['min-height'] = max * this.$menuitems[0].getboundingclientrect().height + 'px';
result['max-width'] = this.$element[0].getboundingclientrect().width + 'px';
return result;
}
/**
* destroys the drilldown menu
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this._hideall();
foundation.nest.burn(this.$element, 'drilldown');
this.$element.unwrap().find('.js-drilldown-back, .is-submenu-parent-item').remove().end().find('.is-active, .is-closing, .is-drilldown-submenu').removeclass('is-active is-closing is-drilldown-submenu').end().find('[data-submenu]').removeattr('aria-hidden tabindex role');
this.$submenuanchors.each(function () {
$(this).off('.zf.drilldown');
});
this.$element.find('a').each(function () {
var $link = $(this);
if ($link.data('savedhref')) {
$link.attr('href', $link.data('savedhref')).removedata('savedhref');
} else {
return;
}
});
foundation.unregisterplugin(this);
}
}]);
return drilldown;
}();
drilldown.defaults = {
/**
* markup used for js generated back button. prepended to submenu lists and deleted on `destroy` method, 'js-drilldown-back' class required. remove the backslash (`\`) if copy and pasting.
* @option
* @example '<\li><\a>back<\/a><\/li>'
*/
backbutton: 'back ',
/**
* markup used to wrap drilldown menu. use a class name for independent styling; the js applied class: `is-drilldown` is required. remove the backslash (`\`) if copy and pasting.
* @option
* @example '<\div class="is-drilldown"><\/div>'
*/
wrapper: '
',
/**
* adds the parent link to the submenu.
* @option
* @example false
*/
parentlink: false,
/**
* allow the menu to return to root list on body click.
* @option
* @example false
*/
closeonclick: false
// holdopen: false
};
// window exports
foundation.plugin(drilldown, 'drilldown');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* dropdown module.
* @module foundation.dropdown
* @requires foundation.util.keyboard
* @requires foundation.util.box
* @requires foundation.util.triggers
*/
var dropdown = function () {
/**
* creates a new instance of a dropdown.
* @class
* @param {jquery} element - jquery object to make into a dropdown.
* object should be of the dropdown panel, rather than its anchor.
* @param {object} options - overrides to the default plugin settings.
*/
function dropdown(element, options) {
_classcallcheck(this, dropdown);
this.$element = element;
this.options = $.extend({}, dropdown.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'dropdown');
foundation.keyboard.register('dropdown', {
'enter': 'open',
'space': 'open',
'escape': 'close',
'tab': 'tab_forward',
'shift_tab': 'tab_backward'
});
}
/**
* initializes the plugin by setting/checking options and attributes, adding helper variables, and saving the anchor.
* @function
* @private
*/
_createclass(dropdown, [{
key: '_init',
value: function _init() {
var $id = this.$element.attr('id');
this.$anchor = $('[data-toggle="' + $id + '"]') || $('[data-open="' + $id + '"]');
this.$anchor.attr({
'aria-controls': $id,
'data-is-focus': false,
'data-yeti-box': $id,
'aria-haspopup': true,
'aria-expanded': false
});
this.options.positionclass = this.getpositionclass();
this.counter = 4;
this.usedpositions = [];
this.$element.attr({
'aria-hidden': 'true',
'data-yeti-box': $id,
'data-resize': $id,
'aria-labelledby': this.$anchor[0].id || foundation.getyodigits(6, 'dd-anchor')
});
this._events();
}
/**
* helper function to determine current orientation of dropdown pane.
* @function
* @returns {string} position - string value of a position class.
*/
}, {
key: 'getpositionclass',
value: function getpositionclass() {
var verticalposition = this.$element[0].classname.match(/(top|left|right|bottom)/g);
verticalposition = verticalposition ? verticalposition[0] : '';
var horizontalposition = /float-(\s+)\s/.exec(this.$anchor[0].classname);
horizontalposition = horizontalposition ? horizontalposition[1] : '';
var position = horizontalposition ? horizontalposition + ' ' + verticalposition : verticalposition;
return position;
}
/**
* adjusts the dropdown panes orientation by adding/removing positioning classes.
* @function
* @private
* @param {string} position - position class to remove.
*/
}, {
key: '_reposition',
value: function _reposition(position) {
this.usedpositions.push(position ? position : 'bottom');
//default, try switching to opposite side
if (!position && this.usedpositions.indexof('top') < 0) {
this.$element.addclass('top');
} else if (position === 'top' && this.usedpositions.indexof('bottom') < 0) {
this.$element.removeclass(position);
} else if (position === 'left' && this.usedpositions.indexof('right') < 0) {
this.$element.removeclass(position).addclass('right');
} else if (position === 'right' && this.usedpositions.indexof('left') < 0) {
this.$element.removeclass(position).addclass('left');
}
//if default change didn't work, try bottom or left first
else if (!position && this.usedpositions.indexof('top') > -1 && this.usedpositions.indexof('left') < 0) {
this.$element.addclass('left');
} else if (position === 'top' && this.usedpositions.indexof('bottom') > -1 && this.usedpositions.indexof('left') < 0) {
this.$element.removeclass(position).addclass('left');
} else if (position === 'left' && this.usedpositions.indexof('right') > -1 && this.usedpositions.indexof('bottom') < 0) {
this.$element.removeclass(position);
} else if (position === 'right' && this.usedpositions.indexof('left') > -1 && this.usedpositions.indexof('bottom') < 0) {
this.$element.removeclass(position);
}
//if nothing cleared, set to bottom
else {
this.$element.removeclass(position);
}
this.classchanged = true;
this.counter--;
}
/**
* sets the position and orientation of the dropdown pane, checks for collisions.
* recursively calls itself if a collision is detected, with a new position class.
* @function
* @private
*/
}, {
key: '_setposition',
value: function _setposition() {
if (this.$anchor.attr('aria-expanded') === 'false') {
return false;
}
var position = this.getpositionclass(),
$eledims = foundation.box.getdimensions(this.$element),
$anchordims = foundation.box.getdimensions(this.$anchor),
_this = this,
direction = position === 'left' ? 'left' : position === 'right' ? 'left' : 'top',
param = direction === 'top' ? 'height' : 'width',
offset = param === 'height' ? this.options.voffset : this.options.hoffset;
if ($eledims.width >= $eledims.windowdims.width || !this.counter && !foundation.box.imnottouchingyou(this.$element)) {
this.$element.offset(foundation.box.getoffsets(this.$element, this.$anchor, 'center bottom', this.options.voffset, this.options.hoffset, true)).css({
'width': $eledims.windowdims.width - this.options.hoffset * 2,
'height': 'auto'
});
this.classchanged = true;
return false;
}
this.$element.offset(foundation.box.getoffsets(this.$element, this.$anchor, position, this.options.voffset, this.options.hoffset));
while (!foundation.box.imnottouchingyou(this.$element, false, true) && this.counter) {
this._reposition(position);
this._setposition();
}
}
/**
* adds event listeners to the element utilizing the triggers utility library.
* @function
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
this.$element.on({
'open.zf.trigger': this.open.bind(this),
'close.zf.trigger': this.close.bind(this),
'toggle.zf.trigger': this.toggle.bind(this),
'resizeme.zf.trigger': this._setposition.bind(this)
});
if (this.options.hover) {
this.$anchor.off('mouseenter.zf.dropdown mouseleave.zf.dropdown').on('mouseenter.zf.dropdown', function () {
cleartimeout(_this.timeout);
_this.timeout = settimeout(function () {
_this.open();
_this.$anchor.data('hover', true);
}, _this.options.hoverdelay);
}).on('mouseleave.zf.dropdown', function () {
cleartimeout(_this.timeout);
_this.timeout = settimeout(function () {
_this.close();
_this.$anchor.data('hover', false);
}, _this.options.hoverdelay);
});
if (this.options.hoverpane) {
this.$element.off('mouseenter.zf.dropdown mouseleave.zf.dropdown').on('mouseenter.zf.dropdown', function () {
cleartimeout(_this.timeout);
}).on('mouseleave.zf.dropdown', function () {
cleartimeout(_this.timeout);
_this.timeout = settimeout(function () {
_this.close();
_this.$anchor.data('hover', false);
}, _this.options.hoverdelay);
});
}
}
this.$anchor.add(this.$element).on('keydown.zf.dropdown', function (e) {
var $target = $(this),
visiblefocusableelements = foundation.keyboard.findfocusable(_this.$element);
foundation.keyboard.handlekey(e, 'dropdown', {
tab_forward: function () {
if (_this.$element.find(':focus').is(visiblefocusableelements.eq(-1))) {
// left modal downwards, setting focus to first element
if (_this.options.trapfocus) {
// if focus shall be trapped
visiblefocusableelements.eq(0).focus();
e.preventdefault();
} else {
// if focus is not trapped, close dropdown on focus out
_this.close();
}
}
},
tab_backward: function () {
if (_this.$element.find(':focus').is(visiblefocusableelements.eq(0)) || _this.$element.is(':focus')) {
// left modal upwards, setting focus to last element
if (_this.options.trapfocus) {
// if focus shall be trapped
visiblefocusableelements.eq(-1).focus();
e.preventdefault();
} else {
// if focus is not trapped, close dropdown on focus out
_this.close();
}
}
},
open: function () {
if ($target.is(_this.$anchor)) {
_this.open();
_this.$element.attr('tabindex', -1).focus();
e.preventdefault();
}
},
close: function () {
_this.close();
_this.$anchor.focus();
}
});
});
}
/**
* adds an event handler to the body to close any dropdowns on a click.
* @function
* @private
*/
}, {
key: '_addbodyhandler',
value: function _addbodyhandler() {
var $body = $(document.body).not(this.$element),
_this = this;
$body.off('click.zf.dropdown').on('click.zf.dropdown', function (e) {
if (_this.$anchor.is(e.target) || _this.$anchor.find(e.target).length) {
return;
}
if (_this.$element.find(e.target).length) {
return;
}
_this.close();
$body.off('click.zf.dropdown');
});
}
/**
* opens the dropdown pane, and fires a bubbling event to close other dropdowns.
* @function
* @fires dropdown#closeme
* @fires dropdown#show
*/
}, {
key: 'open',
value: function open() {
// var _this = this;
/**
* fires to close other open dropdowns
* @event dropdown#closeme
*/
this.$element.trigger('closeme.zf.dropdown', this.$element.attr('id'));
this.$anchor.addclass('hover').attr({ 'aria-expanded': true });
// this.$element/*.show()*/;
this._setposition();
this.$element.addclass('is-open').attr({ 'aria-hidden': false });
if (this.options.autofocus) {
var $focusable = foundation.keyboard.findfocusable(this.$element);
if ($focusable.length) {
$focusable.eq(0).focus();
}
}
if (this.options.closeonclick) {
this._addbodyhandler();
}
/**
* fires once the dropdown is visible.
* @event dropdown#show
*/
this.$element.trigger('show.zf.dropdown', [this.$element]);
}
/**
* closes the open dropdown pane.
* @function
* @fires dropdown#hide
*/
}, {
key: 'close',
value: function close() {
if (!this.$element.hasclass('is-open')) {
return false;
}
this.$element.removeclass('is-open').attr({ 'aria-hidden': true });
this.$anchor.removeclass('hover').attr('aria-expanded', false);
if (this.classchanged) {
var curpositionclass = this.getpositionclass();
if (curpositionclass) {
this.$element.removeclass(curpositionclass);
}
this.$element.addclass(this.options.positionclass)
/*.hide()*/.css({ height: '', width: '' });
this.classchanged = false;
this.counter = 4;
this.usedpositions.length = 0;
}
this.$element.trigger('hide.zf.dropdown', [this.$element]);
}
/**
* toggles the dropdown pane's visibility.
* @function
*/
}, {
key: 'toggle',
value: function toggle() {
if (this.$element.hasclass('is-open')) {
if (this.$anchor.data('hover')) return;
this.close();
} else {
this.open();
}
}
/**
* destroys the dropdown.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.off('.zf.trigger').hide();
this.$anchor.off('.zf.dropdown');
foundation.unregisterplugin(this);
}
}]);
return dropdown;
}();
dropdown.defaults = {
/**
* amount of time to delay opening a submenu on hover event.
* @option
* @example 250
*/
hoverdelay: 250,
/**
* allow submenus to open on hover events
* @option
* @example false
*/
hover: false,
/**
* don't close dropdown when hovering over dropdown pane
* @option
* @example true
*/
hoverpane: false,
/**
* number of pixels between the dropdown pane and the triggering element on open.
* @option
* @example 1
*/
voffset: 1,
/**
* number of pixels between the dropdown pane and the triggering element on open.
* @option
* @example 1
*/
hoffset: 1,
/**
* class applied to adjust open position. js will test and fill this in.
* @option
* @example 'top'
*/
positionclass: '',
/**
* allow the plugin to trap focus to the dropdown pane if opened with keyboard commands.
* @option
* @example false
*/
trapfocus: false,
/**
* allow the plugin to set focus to the first focusable element within the pane, regardless of method of opening.
* @option
* @example true
*/
autofocus: false,
/**
* allows a click on the body to close the dropdown.
* @option
* @example false
*/
closeonclick: false
};
// window exports
foundation.plugin(dropdown, 'dropdown');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* dropdownmenu module.
* @module foundation.dropdown-menu
* @requires foundation.util.keyboard
* @requires foundation.util.box
* @requires foundation.util.nest
*/
var dropdownmenu = function () {
/**
* creates a new instance of dropdownmenu.
* @class
* @fires dropdownmenu#init
* @param {jquery} element - jquery object to make into a dropdown menu.
* @param {object} options - overrides to the default plugin settings.
*/
function dropdownmenu(element, options) {
_classcallcheck(this, dropdownmenu);
this.$element = element;
this.options = $.extend({}, dropdownmenu.defaults, this.$element.data(), options);
foundation.nest.feather(this.$element, 'dropdown');
this._init();
foundation.registerplugin(this, 'dropdownmenu');
foundation.keyboard.register('dropdownmenu', {
'enter': 'open',
'space': 'open',
'arrow_right': 'next',
'arrow_up': 'up',
'arrow_down': 'down',
'arrow_left': 'previous',
'escape': 'close'
});
}
/**
* initializes the plugin, and calls _preparemenu
* @private
* @function
*/
_createclass(dropdownmenu, [{
key: '_init',
value: function _init() {
var subs = this.$element.find('li.is-dropdown-submenu-parent');
this.$element.children('.is-dropdown-submenu-parent').children('.is-dropdown-submenu').addclass('first-sub');
this.$menuitems = this.$element.find('[role="menuitem"]');
this.$tabs = this.$element.children('[role="menuitem"]');
this.$tabs.find('ul.is-dropdown-submenu').addclass(this.options.verticalclass);
if (this.$element.hasclass(this.options.rightclass) || this.options.alignment === 'right' || foundation.rtl() || this.$element.parents('.top-bar-right').is('*')) {
this.options.alignment = 'right';
subs.addclass('opens-left');
} else {
subs.addclass('opens-right');
}
this.changed = false;
this._events();
}
}, {
key: '_events',
/**
* adds event listeners to elements within the menu
* @private
* @function
*/
value: function _events() {
var _this = this,
hastouch = 'ontouchstart' in window || typeof window.ontouchstart !== 'undefined',
parclass = 'is-dropdown-submenu-parent';
// used for onclick and in the keyboard handlers
var handleclickfn = function (e) {
var $elem = $(e.target).parentsuntil('ul', '.' + parclass),
hassub = $elem.hasclass(parclass),
hasclicked = $elem.attr('data-is-click') === 'true',
$sub = $elem.children('.is-dropdown-submenu');
if (hassub) {
if (hasclicked) {
if (!_this.options.closeonclick || !_this.options.clickopen && !hastouch || _this.options.forcefollow && hastouch) {
return;
} else {
e.stopimmediatepropagation();
e.preventdefault();
_this._hide($elem);
}
} else {
e.preventdefault();
e.stopimmediatepropagation();
_this._show($elem.children('.is-dropdown-submenu'));
$elem.add($elem.parentsuntil(_this.$element, '.' + parclass)).attr('data-is-click', true);
}
} else {
return;
}
};
if (this.options.clickopen || hastouch) {
this.$menuitems.on('click.zf.dropdownmenu touchstart.zf.dropdownmenu', handleclickfn);
}
if (!this.options.disablehover) {
this.$menuitems.on('mouseenter.zf.dropdownmenu', function (e) {
var $elem = $(this),
hassub = $elem.hasclass(parclass);
if (hassub) {
cleartimeout(_this.delay);
_this.delay = settimeout(function () {
_this._show($elem.children('.is-dropdown-submenu'));
}, _this.options.hoverdelay);
}
}).on('mouseleave.zf.dropdownmenu', function (e) {
var $elem = $(this),
hassub = $elem.hasclass(parclass);
if (hassub && _this.options.autoclose) {
if ($elem.attr('data-is-click') === 'true' && _this.options.clickopen) {
return false;
}
cleartimeout(_this.delay);
_this.delay = settimeout(function () {
_this._hide($elem);
}, _this.options.closingtime);
}
});
}
this.$menuitems.on('keydown.zf.dropdownmenu', function (e) {
var $element = $(e.target).parentsuntil('ul', '[role="menuitem"]'),
istab = _this.$tabs.index($element) > -1,
$elements = istab ? _this.$tabs : $element.siblings('li').add($element),
$prevelement,
$nextelement;
$elements.each(function (i) {
if ($(this).is($element)) {
$prevelement = $elements.eq(i - 1);
$nextelement = $elements.eq(i + 1);
return;
}
});
var nextsibling = function () {
if (!$element.is(':last-child')) {
$nextelement.children('a:first').focus();
e.preventdefault();
}
},
prevsibling = function () {
$prevelement.children('a:first').focus();
e.preventdefault();
},
opensub = function () {
var $sub = $element.children('ul.is-dropdown-submenu');
if ($sub.length) {
_this._show($sub);
$element.find('li > a:first').focus();
e.preventdefault();
} else {
return;
}
},
closesub = function () {
//if ($element.is(':first-child')) {
var close = $element.parent('ul').parent('li');
close.children('a:first').focus();
_this._hide(close);
e.preventdefault();
//}
};
var functions = {
open: opensub,
close: function () {
_this._hide(_this.$element);
_this.$menuitems.find('a:first').focus(); // focus to first element
e.preventdefault();
},
handled: function () {
e.stopimmediatepropagation();
}
};
if (istab) {
if (_this.$element.hasclass(_this.options.verticalclass)) {
// vertical menu
if (_this.options.alignment === 'left') {
// left aligned
$.extend(functions, {
down: nextsibling,
up: prevsibling,
next: opensub,
previous: closesub
});
} else {
// right aligned
$.extend(functions, {
down: nextsibling,
up: prevsibling,
next: closesub,
previous: opensub
});
}
} else {
// horizontal menu
$.extend(functions, {
next: nextsibling,
previous: prevsibling,
down: opensub,
up: closesub
});
}
} else {
// not tabs -> one sub
if (_this.options.alignment === 'left') {
// left aligned
$.extend(functions, {
next: opensub,
previous: closesub,
down: nextsibling,
up: prevsibling
});
} else {
// right aligned
$.extend(functions, {
next: closesub,
previous: opensub,
down: nextsibling,
up: prevsibling
});
}
}
foundation.keyboard.handlekey(e, 'dropdownmenu', functions);
});
}
/**
* adds an event handler to the body to close any dropdowns on a click.
* @function
* @private
*/
}, {
key: '_addbodyhandler',
value: function _addbodyhandler() {
var $body = $(document.body),
_this = this;
$body.off('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu').on('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu', function (e) {
var $link = _this.$element.find(e.target);
if ($link.length) {
return;
}
_this._hide();
$body.off('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu');
});
}
/**
* opens a dropdown pane, and checks for collisions first.
* @param {jquery} $sub - ul element that is a submenu to show
* @function
* @private
* @fires dropdownmenu#show
*/
}, {
key: '_show',
value: function _show($sub) {
var idx = this.$tabs.index(this.$tabs.filter(function (i, el) {
return $(el).find($sub).length > 0;
}));
var $sibs = $sub.parent('li.is-dropdown-submenu-parent').siblings('li.is-dropdown-submenu-parent');
this._hide($sibs, idx);
$sub.css('visibility', 'hidden').addclass('js-dropdown-active').attr({ 'aria-hidden': false }).parent('li.is-dropdown-submenu-parent').addclass('is-active').attr({ 'aria-expanded': true });
var clear = foundation.box.imnottouchingyou($sub, null, true);
if (!clear) {
var oldclass = this.options.alignment === 'left' ? '-right' : '-left',
$parentli = $sub.parent('.is-dropdown-submenu-parent');
$parentli.removeclass('opens' + oldclass).addclass('opens-' + this.options.alignment);
clear = foundation.box.imnottouchingyou($sub, null, true);
if (!clear) {
$parentli.removeclass('opens-' + this.options.alignment).addclass('opens-inner');
}
this.changed = true;
}
$sub.css('visibility', '');
if (this.options.closeonclick) {
this._addbodyhandler();
}
/**
* fires when the new dropdown pane is visible.
* @event dropdownmenu#show
*/
this.$element.trigger('show.zf.dropdownmenu', [$sub]);
}
/**
* hides a single, currently open dropdown pane, if passed a parameter, otherwise, hides everything.
* @function
* @param {jquery} $elem - element with a submenu to hide
* @param {number} idx - index of the $tabs collection to hide
* @private
*/
}, {
key: '_hide',
value: function _hide($elem, idx) {
var $toclose;
if ($elem && $elem.length) {
$toclose = $elem;
} else if (idx !== undefined) {
$toclose = this.$tabs.not(function (i, el) {
return i === idx;
});
} else {
$toclose = this.$element;
}
var somethingtoclose = $toclose.hasclass('is-active') || $toclose.find('.is-active').length > 0;
if (somethingtoclose) {
$toclose.find('li.is-active').add($toclose).attr({
'aria-expanded': false,
'data-is-click': false
}).removeclass('is-active');
$toclose.find('ul.js-dropdown-active').attr({
'aria-hidden': true
}).removeclass('js-dropdown-active');
if (this.changed || $toclose.find('opens-inner').length) {
var oldclass = this.options.alignment === 'left' ? 'right' : 'left';
$toclose.find('li.is-dropdown-submenu-parent').add($toclose).removeclass('opens-inner opens-' + this.options.alignment).addclass('opens-' + oldclass);
this.changed = false;
}
/**
* fires when the open menus are closed.
* @event dropdownmenu#hide
*/
this.$element.trigger('hide.zf.dropdownmenu', [$toclose]);
}
}
/**
* destroys the plugin.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.$menuitems.off('.zf.dropdownmenu').removeattr('data-is-click').removeclass('is-right-arrow is-left-arrow is-down-arrow opens-right opens-left opens-inner');
$(document.body).off('.zf.dropdownmenu');
foundation.nest.burn(this.$element, 'dropdown');
foundation.unregisterplugin(this);
}
}]);
return dropdownmenu;
}();
/**
* default settings for plugin
*/
dropdownmenu.defaults = {
/**
* disallows hover events from opening submenus
* @option
* @example false
*/
disablehover: false,
/**
* allow a submenu to automatically close on a mouseleave event, if not clicked open.
* @option
* @example true
*/
autoclose: true,
/**
* amount of time to delay opening a submenu on hover event.
* @option
* @example 50
*/
hoverdelay: 50,
/**
* allow a submenu to open/remain open on parent click event. allows cursor to move away from menu.
* @option
* @example true
*/
clickopen: false,
/**
* amount of time to delay closing a submenu on a mouseleave event.
* @option
* @example 500
*/
closingtime: 500,
/**
* position of the menu relative to what direction the submenus should open. handled by js.
* @option
* @example 'left'
*/
alignment: 'left',
/**
* allow clicks on the body to close any open submenus.
* @option
* @example true
*/
closeonclick: true,
/**
* class applied to vertical oriented menus, foundation default is `vertical`. update this if using your own class.
* @option
* @example 'vertical'
*/
verticalclass: 'vertical',
/**
* class applied to right-side oriented menus, foundation default is `align-right`. update this if using your own class.
* @option
* @example 'align-right'
*/
rightclass: 'align-right',
/**
* boolean to force overide the clicking of links to perform default action, on second touch event for mobile.
* @option
* @example false
*/
forcefollow: true
};
// window exports
foundation.plugin(dropdownmenu, 'dropdownmenu');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* equalizer module.
* @module foundation.equalizer
*/
var equalizer = function () {
/**
* creates a new instance of equalizer.
* @class
* @fires equalizer#init
* @param {object} element - jquery object to add the trigger to.
* @param {object} options - overrides to the default plugin settings.
*/
function equalizer(element, options) {
_classcallcheck(this, equalizer);
this.$element = element;
this.options = $.extend({}, equalizer.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'equalizer');
}
/**
* initializes the equalizer plugin and calls functions to get equalizer functioning on load.
* @private
*/
_createclass(equalizer, [{
key: '_init',
value: function _init() {
var eqid = this.$element.attr('data-equalizer') || '';
var $watched = this.$element.find('[data-equalizer-watch="' + eqid + '"]');
this.$watched = $watched.length ? $watched : this.$element.find('[data-equalizer-watch]');
this.$element.attr('data-resize', eqid || foundation.getyodigits(6, 'eq'));
this.hasnested = this.$element.find('[data-equalizer]').length > 0;
this.isnested = this.$element.parentsuntil(document.body, '[data-equalizer]').length > 0;
this.ison = false;
this._bindhandler = {
onresizemebound: this._onresizeme.bind(this),
onpostequalizedbound: this._onpostequalized.bind(this)
};
var imgs = this.$element.find('img');
var toosmall;
if (this.options.equalizeon) {
toosmall = this._checkmq();
$(window).on('changed.zf.mediaquery', this._checkmq.bind(this));
} else {
this._events();
}
if (toosmall !== undefined && toosmall === false || toosmall === undefined) {
if (imgs.length) {
foundation.onimagesloaded(imgs, this._reflow.bind(this));
} else {
this._reflow();
}
}
}
/**
* removes event listeners if the breakpoint is too small.
* @private
*/
}, {
key: '_pauseevents',
value: function _pauseevents() {
this.ison = false;
this.$element.off({
'.zf.equalizer': this._bindhandler.onpostequalizedbound,
'resizeme.zf.trigger': this._bindhandler.onresizemebound
});
}
/**
* function to handle $elements resizeme.zf.trigger, with bound this on _bindhandler.onresizemebound
* @private
*/
}, {
key: '_onresizeme',
value: function _onresizeme(e) {
this._reflow();
}
/**
* function to handle $elements postequalized.zf.equalizer, with bound this on _bindhandler.onpostequalizedbound
* @private
*/
}, {
key: '_onpostequalized',
value: function _onpostequalized(e) {
if (e.target !== this.$element[0]) {
this._reflow();
}
}
/**
* initializes events for equalizer.
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
this._pauseevents();
if (this.hasnested) {
this.$element.on('postequalized.zf.equalizer', this._bindhandler.onpostequalizedbound);
} else {
this.$element.on('resizeme.zf.trigger', this._bindhandler.onresizemebound);
}
this.ison = true;
}
/**
* checks the current breakpoint to the minimum required size.
* @private
*/
}, {
key: '_checkmq',
value: function _checkmq() {
var toosmall = !foundation.mediaquery.atleast(this.options.equalizeon);
if (toosmall) {
if (this.ison) {
this._pauseevents();
this.$watched.css('height', 'auto');
}
} else {
if (!this.ison) {
this._events();
}
}
return toosmall;
}
/**
* a noop version for the plugin
* @private
*/
}, {
key: '_killswitch',
value: function _killswitch() {
return;
}
/**
* calls necessary functions to update equalizer upon dom change
* @private
*/
}, {
key: '_reflow',
value: function _reflow() {
if (!this.options.equalizeonstack) {
if (this._isstacked()) {
this.$watched.css('height', 'auto');
return false;
}
}
if (this.options.equalizebyrow) {
this.getheightsbyrow(this.applyheightbyrow.bind(this));
} else {
this.getheights(this.applyheight.bind(this));
}
}
/**
* manually determines if the first 2 elements are *not* stacked.
* @private
*/
}, {
key: '_isstacked',
value: function _isstacked() {
return this.$watched[0].getboundingclientrect().top !== this.$watched[1].getboundingclientrect().top;
}
/**
* finds the outer heights of children contained within an equalizer parent and returns them in an array
* @param {function} cb - a non-optional callback to return the heights array to.
* @returns {array} heights - an array of heights of children within equalizer container
*/
}, {
key: 'getheights',
value: function getheights(cb) {
var heights = [];
for (var i = 0, len = this.$watched.length; i < len; i++) {
this.$watched[i].style.height = 'auto';
heights.push(this.$watched[i].offsetheight);
}
cb(heights);
}
/**
* finds the outer heights of children contained within an equalizer parent and returns them in an array
* @param {function} cb - a non-optional callback to return the heights array to.
* @returns {array} groups - an array of heights of children within equalizer container grouped by row with element,height and max as last child
*/
}, {
key: 'getheightsbyrow',
value: function getheightsbyrow(cb) {
var lasteltopoffset = this.$watched.length ? this.$watched.first().offset().top : 0,
groups = [],
group = 0;
//group by row
groups[group] = [];
for (var i = 0, len = this.$watched.length; i < len; i++) {
this.$watched[i].style.height = 'auto';
//maybe could use this.$watched[i].offsettop
var eloffsettop = $(this.$watched[i]).offset().top;
if (eloffsettop != lasteltopoffset) {
group++;
groups[group] = [];
lasteltopoffset = eloffsettop;
}
groups[group].push([this.$watched[i], this.$watched[i].offsetheight]);
}
for (var j = 0, ln = groups.length; j < ln; j++) {
var heights = $(groups[j]).map(function () {
return this[1];
}).get();
var max = math.max.apply(null, heights);
groups[j].push(max);
}
cb(groups);
}
/**
* changes the css height property of each child in an equalizer parent to match the tallest
* @param {array} heights - an array of heights of children within equalizer container
* @fires equalizer#preequalized
* @fires equalizer#postequalized
*/
}, {
key: 'applyheight',
value: function applyheight(heights) {
var max = math.max.apply(null, heights);
/**
* fires before the heights are applied
* @event equalizer#preequalized
*/
this.$element.trigger('preequalized.zf.equalizer');
this.$watched.css('height', max);
/**
* fires when the heights have been applied
* @event equalizer#postequalized
*/
this.$element.trigger('postequalized.zf.equalizer');
}
/**
* changes the css height property of each child in an equalizer parent to match the tallest by row
* @param {array} groups - an array of heights of children within equalizer container grouped by row with element,height and max as last child
* @fires equalizer#preequalized
* @fires equalizer#preequalizedrow
* @fires equalizer#postequalizedrow
* @fires equalizer#postequalized
*/
}, {
key: 'applyheightbyrow',
value: function applyheightbyrow(groups) {
/**
* fires before the heights are applied
*/
this.$element.trigger('preequalized.zf.equalizer');
for (var i = 0, len = groups.length; i < len; i++) {
var groupsilength = groups[i].length,
max = groups[i][groupsilength - 1];
if (groupsilength <= 2) {
$(groups[i][0][0]).css({ 'height': 'auto' });
continue;
}
/**
* fires before the heights per row are applied
* @event equalizer#preequalizedrow
*/
this.$element.trigger('preequalizedrow.zf.equalizer');
for (var j = 0, lenj = groupsilength - 1; j < lenj; j++) {
$(groups[i][j][0]).css({ 'height': max });
}
/**
* fires when the heights per row have been applied
* @event equalizer#postequalizedrow
*/
this.$element.trigger('postequalizedrow.zf.equalizer');
}
/**
* fires when the heights have been applied
*/
this.$element.trigger('postequalized.zf.equalizer');
}
/**
* destroys an instance of equalizer.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this._pauseevents();
this.$watched.css('height', 'auto');
foundation.unregisterplugin(this);
}
}]);
return equalizer;
}();
/**
* default settings for plugin
*/
equalizer.defaults = {
/**
* enable height equalization when stacked on smaller screens.
* @option
* @example true
*/
equalizeonstack: true,
/**
* enable height equalization row by row.
* @option
* @example false
*/
equalizebyrow: false,
/**
* string representing the minimum breakpoint size the plugin should equalize heights on.
* @option
* @example 'medium'
*/
equalizeon: ''
};
// window exports
foundation.plugin(equalizer, 'equalizer');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* interchange module.
* @module foundation.interchange
* @requires foundation.util.mediaquery
* @requires foundation.util.timerandimageloader
*/
var interchange = function () {
/**
* creates a new instance of interchange.
* @class
* @fires interchange#init
* @param {object} element - jquery object to add the trigger to.
* @param {object} options - overrides to the default plugin settings.
*/
function interchange(element, options) {
_classcallcheck(this, interchange);
this.$element = element;
this.options = $.extend({}, interchange.defaults, options);
this.rules = [];
this.currentpath = '';
this._init();
this._events();
foundation.registerplugin(this, 'interchange');
}
/**
* initializes the interchange plugin and calls functions to get interchange functioning on load.
* @function
* @private
*/
_createclass(interchange, [{
key: '_init',
value: function _init() {
this._addbreakpoints();
this._generaterules();
this._reflow();
}
/**
* initializes events for interchange.
* @function
* @private
*/
}, {
key: '_events',
value: function _events() {
$(window).on('resize.zf.interchange', foundation.util.throttle(this._reflow.bind(this), 50));
}
/**
* calls necessary functions to update interchange upon dom change
* @function
* @private
*/
}, {
key: '_reflow',
value: function _reflow() {
var match;
// iterate through each rule, but only save the last match
for (var i in this.rules) {
if (this.rules.hasownproperty(i)) {
var rule = this.rules[i];
if (window.matchmedia(rule.query).matches) {
match = rule;
}
}
}
if (match) {
this.replace(match.path);
}
}
/**
* gets the foundation breakpoints and adds them to the interchange.special_queries object.
* @function
* @private
*/
}, {
key: '_addbreakpoints',
value: function _addbreakpoints() {
for (var i in foundation.mediaquery.queries) {
if (foundation.mediaquery.queries.hasownproperty(i)) {
var query = foundation.mediaquery.queries[i];
interchange.special_queries[query.name] = query.value;
}
}
}
/**
* checks the interchange element for the provided media query + content pairings
* @function
* @private
* @param {object} element - jquery object that is an interchange instance
* @returns {array} scenarios - array of objects that have 'mq' and 'path' keys with corresponding keys
*/
}, {
key: '_generaterules',
value: function _generaterules(element) {
var ruleslist = [];
var rules;
if (this.options.rules) {
rules = this.options.rules;
} else {
rules = this.$element.data('interchange').match(/\[.*?\]/g);
}
for (var i in rules) {
if (rules.hasownproperty(i)) {
var rule = rules[i].slice(1, -1).split(', ');
var path = rule.slice(0, -1).join('');
var query = rule[rule.length - 1];
if (interchange.special_queries[query]) {
query = interchange.special_queries[query];
}
ruleslist.push({
path: path,
query: query
});
}
}
this.rules = ruleslist;
}
/**
* update the `src` property of an image, or change the html of a container, to the specified path.
* @function
* @param {string} path - path to the image or html partial.
* @fires interchange#replaced
*/
}, {
key: 'replace',
value: function replace(path) {
if (this.currentpath === path) return;
var _this = this,
trigger = 'replaced.zf.interchange';
// replacing images
if (this.$element[0].nodename === 'img') {
this.$element.attr('src', path).load(function () {
_this.currentpath = path;
}).trigger(trigger);
}
// replacing background images
else if (path.match(/\.(gif|jpg|jpeg|png|svg|tiff)([?#].*)?/i)) {
this.$element.css({ 'background-image': 'url(' + path + ')' }).trigger(trigger);
}
// replacing html
else {
$.get(path, function (response) {
_this.$element.html(response).trigger(trigger);
$(response).foundation();
_this.currentpath = path;
});
}
/**
* fires when content in an interchange element is done being loaded.
* @event interchange#replaced
*/
// this.$element.trigger('replaced.zf.interchange');
}
/**
* destroys an instance of interchange.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
//todo this.
}
}]);
return interchange;
}();
/**
* default settings for plugin
*/
interchange.defaults = {
/**
* rules to be applied to interchange elements. set with the `data-interchange` array notation.
* @option
*/
rules: null
};
interchange.special_queries = {
'landscape': 'screen and (orientation: landscape)',
'portrait': 'screen and (orientation: portrait)',
'retina': 'only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx)'
};
// window exports
foundation.plugin(interchange, 'interchange');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* magellan module.
* @module foundation.magellan
*/
var magellan = function () {
/**
* creates a new instance of magellan.
* @class
* @fires magellan#init
* @param {object} element - jquery object to add the trigger to.
* @param {object} options - overrides to the default plugin settings.
*/
function magellan(element, options) {
_classcallcheck(this, magellan);
this.$element = element;
this.options = $.extend({}, magellan.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'magellan');
}
/**
* initializes the magellan plugin and calls functions to get equalizer functioning on load.
* @private
*/
_createclass(magellan, [{
key: '_init',
value: function _init() {
var id = this.$element[0].id || foundation.getyodigits(6, 'magellan');
var _this = this;
this.$targets = $('[data-magellan-target]');
this.$links = this.$element.find('a');
this.$element.attr({
'data-resize': id,
'data-scroll': id,
'id': id
});
this.$active = $();
this.scrollpos = parseint(window.pageyoffset, 10);
this._events();
}
/**
* calculates an array of pixel values that are the demarcation lines between locations on the page.
* can be invoked if new elements are added or the size of a location changes.
* @function
*/
}, {
key: 'calcpoints',
value: function calcpoints() {
var _this = this,
body = document.body,
html = document.documentelement;
this.points = [];
this.winheight = math.round(math.max(window.innerheight, html.clientheight));
this.docheight = math.round(math.max(body.scrollheight, body.offsetheight, html.clientheight, html.scrollheight, html.offsetheight));
this.$targets.each(function () {
var $tar = $(this),
pt = math.round($tar.offset().top - _this.options.threshold);
$tar.targetpoint = pt;
_this.points.push(pt);
});
}
/**
* initializes events for magellan.
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this,
$body = $('html, body'),
opts = {
duration: _this.options.animationduration,
easing: _this.options.animationeasing
};
$(window).one('load', function () {
if (_this.options.deeplinking) {
if (location.hash) {
_this.scrolltoloc(location.hash);
}
}
_this.calcpoints();
_this._updateactive();
});
this.$element.on({
'resizeme.zf.trigger': this.reflow.bind(this),
'scrollme.zf.trigger': this._updateactive.bind(this)
}).on('click.zf.magellan', 'a[href^="#"]', function (e) {
e.preventdefault();
var arrival = this.getattribute('href');
_this.scrolltoloc(arrival);
});
}
/**
* function to scroll to a given location on the page.
* @param {string} loc - a properly formatted jquery id selector. example: '#foo'
* @function
*/
}, {
key: 'scrolltoloc',
value: function scrolltoloc(loc) {
var scrollpos = math.round($(loc).offset().top - this.options.threshold / 2 - this.options.baroffset);
$('html, body').stop(true).animate({ scrolltop: scrollpos }, this.options.animationduration, this.options.animationeasing);
}
/**
* calls necessary functions to update magellan upon dom change
* @function
*/
}, {
key: 'reflow',
value: function reflow() {
this.calcpoints();
this._updateactive();
}
/**
* updates the visibility of an active location link, and updates the url hash for the page, if deeplinking enabled.
* @private
* @function
* @fires magellan#update
*/
}, {
key: '_updateactive',
value: function _updateactive() /*evt, elem, scrollpos*/{
var winpos = /*scrollpos ||*/parseint(window.pageyoffset, 10),
curidx;
if (winpos + this.winheight === this.docheight) {
curidx = this.points.length - 1;
} else if (winpos < this.points[0]) {
curidx = 0;
} else {
var isdown = this.scrollpos < winpos,
_this = this,
curvisible = this.points.filter(function (p, i) {
return isdown ? p - _this.options.baroffset <= winpos : p - _this.options.baroffset - _this.options.threshold <= winpos;
});
curidx = curvisible.length ? curvisible.length - 1 : 0;
}
this.$active.removeclass(this.options.activeclass);
this.$active = this.$links.eq(curidx).addclass(this.options.activeclass);
if (this.options.deeplinking) {
var hash = this.$active[0].getattribute('href');
if (window.history.pushstate) {
window.history.pushstate(null, null, hash);
} else {
window.location.hash = hash;
}
}
this.scrollpos = winpos;
/**
* fires when magellan is finished updating to the new active element.
* @event magellan#update
*/
this.$element.trigger('update.zf.magellan', [this.$active]);
}
/**
* destroys an instance of magellan and resets the url of the window.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.off('.zf.trigger .zf.magellan').find('.' + this.options.activeclass).removeclass(this.options.activeclass);
if (this.options.deeplinking) {
var hash = this.$active[0].getattribute('href');
window.location.hash.replace(hash, '');
}
foundation.unregisterplugin(this);
}
}]);
return magellan;
}();
/**
* default settings for plugin
*/
magellan.defaults = {
/**
* amount of time, in ms, the animated scrolling should take between locations.
* @option
* @example 500
*/
animationduration: 500,
/**
* animation style to use when scrolling between locations.
* @option
* @example 'ease-in-out'
*/
animationeasing: 'linear',
/**
* number of pixels to use as a marker for location changes.
* @option
* @example 50
*/
threshold: 50,
/**
* class applied to the active locations link on the magellan container.
* @option
* @example 'active'
*/
activeclass: 'active',
/**
* allows the script to manipulate the url of the current page, and if supported, alter the history.
* @option
* @example true
*/
deeplinking: false,
/**
* number of pixels to offset the scroll of the page on item click if using a sticky nav bar.
* @option
* @example 25
*/
baroffset: 0
};
// window exports
foundation.plugin(magellan, 'magellan');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* offcanvas module.
* @module foundation.offcanvas
* @requires foundation.util.mediaquery
* @requires foundation.util.triggers
* @requires foundation.util.motion
*/
var offcanvas = function () {
/**
* creates a new instance of an off-canvas wrapper.
* @class
* @fires offcanvas#init
* @param {object} element - jquery object to initialize.
* @param {object} options - overrides to the default plugin settings.
*/
function offcanvas(element, options) {
_classcallcheck(this, offcanvas);
this.$element = element;
this.options = $.extend({}, offcanvas.defaults, this.$element.data(), options);
this.$lasttrigger = $();
this.$triggers = $();
this._init();
this._events();
foundation.registerplugin(this, 'offcanvas');
}
/**
* initializes the off-canvas wrapper by adding the exit overlay (if needed).
* @function
* @private
*/
_createclass(offcanvas, [{
key: '_init',
value: function _init() {
var id = this.$element.attr('id');
this.$element.attr('aria-hidden', 'true');
// find triggers that affect this element and add aria-expanded to them
this.$triggers = $(document).find('[data-open="' + id + '"], [data-close="' + id + '"], [data-toggle="' + id + '"]').attr('aria-expanded', 'false').attr('aria-controls', id);
// add a close trigger over the body if necessary
if (this.options.closeonclick) {
if ($('.js-off-canvas-exit').length) {
this.$exiter = $('.js-off-canvas-exit');
} else {
var exiter = document.createelement('div');
exiter.setattribute('class', 'js-off-canvas-exit');
$('[data-off-canvas-content]').append(exiter);
this.$exiter = $(exiter);
}
}
this.options.isrevealed = this.options.isrevealed || new regexp(this.options.revealclass, 'g').test(this.$element[0].classname);
if (this.options.isrevealed) {
this.options.revealon = this.options.revealon || this.$element[0].classname.match(/(reveal-for-medium|reveal-for-large)/g)[0].split('-')[2];
this._setmqchecker();
}
if (!this.options.transitiontime) {
this.options.transitiontime = parsefloat(window.getcomputedstyle($('[data-off-canvas-wrapper]')[0]).transitionduration) * 1000;
}
}
/**
* adds event handlers to the off-canvas wrapper and the exit overlay.
* @function
* @private
*/
}, {
key: '_events',
value: function _events() {
this.$element.off('.zf.trigger .zf.offcanvas').on({
'open.zf.trigger': this.open.bind(this),
'close.zf.trigger': this.close.bind(this),
'toggle.zf.trigger': this.toggle.bind(this),
'keydown.zf.offcanvas': this._handlekeyboard.bind(this)
});
if (this.options.closeonclick && this.$exiter.length) {
this.$exiter.on({ 'click.zf.offcanvas': this.close.bind(this) });
}
}
/**
* applies event listener for elements that will reveal at certain breakpoints.
* @private
*/
}, {
key: '_setmqchecker',
value: function _setmqchecker() {
var _this = this;
$(window).on('changed.zf.mediaquery', function () {
if (foundation.mediaquery.atleast(_this.options.revealon)) {
_this.reveal(true);
} else {
_this.reveal(false);
}
}).one('load.zf.offcanvas', function () {
if (foundation.mediaquery.atleast(_this.options.revealon)) {
_this.reveal(true);
}
});
}
/**
* handles the revealing/hiding the off-canvas at breakpoints, not the same as open.
* @param {boolean} isrevealed - true if element should be revealed.
* @function
*/
}, {
key: 'reveal',
value: function reveal(isrevealed) {
var $closer = this.$element.find('[data-close]');
if (isrevealed) {
this.close();
this.isrevealed = true;
// if (!this.options.forcetop) {
// var scrollpos = parseint(window.pageyoffset);
// this.$element[0].style.transform = 'translate(0,' + scrollpos + 'px)';
// }
// if (this.options.issticky) { this._stick(); }
this.$element.off('open.zf.trigger toggle.zf.trigger');
if ($closer.length) {
$closer.hide();
}
} else {
this.isrevealed = false;
// if (this.options.issticky || !this.options.forcetop) {
// this.$element[0].style.transform = '';
// $(window).off('scroll.zf.offcanvas');
// }
this.$element.on({
'open.zf.trigger': this.open.bind(this),
'toggle.zf.trigger': this.toggle.bind(this)
});
if ($closer.length) {
$closer.show();
}
}
}
/**
* opens the off-canvas menu.
* @function
* @param {object} event - event object passed from listener.
* @param {jquery} trigger - element that triggered the off-canvas to open.
* @fires offcanvas#opened
*/
}, {
key: 'open',
value: function open(event, trigger) {
if (this.$element.hasclass('is-open') || this.isrevealed) {
return;
}
var _this = this,
$body = $(document.body);
if (this.options.forcetop) {
$('body').scrolltop(0);
}
// window.pageyoffset = 0;
// if (!this.options.forcetop) {
// var scrollpos = parseint(window.pageyoffset);
// this.$element[0].style.transform = 'translate(0,' + scrollpos + 'px)';
// if (this.$exiter.length) {
// this.$exiter[0].style.transform = 'translate(0,' + scrollpos + 'px)';
// }
// }
/**
* fires when the off-canvas menu opens.
* @event offcanvas#opened
*/
foundation.move(this.options.transitiontime, this.$element, function () {
$('[data-off-canvas-wrapper]').addclass('is-off-canvas-open is-open-' + _this.options.position);
_this.$element.addclass('is-open');
// if (_this.options.issticky) {
// _this._stick();
// }
});
this.$triggers.attr('aria-expanded', 'true');
this.$element.attr('aria-hidden', 'false').trigger('opened.zf.offcanvas');
if (this.options.closeonclick) {
this.$exiter.addclass('is-visible');
}
if (trigger) {
this.$lasttrigger = trigger;
}
if (this.options.autofocus) {
this.$element.one(foundation.transitionend(this.$element), function () {
_this.$element.find('a, button').eq(0).focus();
});
}
if (this.options.trapfocus) {
$('[data-off-canvas-content]').attr('tabindex', '-1');
this._trapfocus();
}
}
/**
* traps focus within the offcanvas on open.
* @private
*/
}, {
key: '_trapfocus',
value: function _trapfocus() {
var focusable = foundation.keyboard.findfocusable(this.$element),
first = focusable.eq(0),
last = focusable.eq(-1);
focusable.off('.zf.offcanvas').on('keydown.zf.offcanvas', function (e) {
if (e.which === 9 || e.keycode === 9) {
if (e.target === last[0] && !e.shiftkey) {
e.preventdefault();
first.focus();
}
if (e.target === first[0] && e.shiftkey) {
e.preventdefault();
last.focus();
}
}
});
}
/**
* allows the offcanvas to appear sticky utilizing translate properties.
* @private
*/
// offcanvas.prototype._stick = function() {
// var elstyle = this.$element[0].style;
//
// if (this.options.closeonclick) {
// var exitstyle = this.$exiter[0].style;
// }
//
// $(window).on('scroll.zf.offcanvas', function(e) {
// console.log(e);
// var pagey = window.pageyoffset;
// elstyle.transform = 'translate(0,' + pagey + 'px)';
// if (exitstyle !== undefined) { exitstyle.transform = 'translate(0,' + pagey + 'px)'; }
// });
// // this.$element.trigger('stuck.zf.offcanvas');
// };
/**
* closes the off-canvas menu.
* @function
* @param {function} cb - optional cb to fire after closure.
* @fires offcanvas#closed
*/
}, {
key: 'close',
value: function close(cb) {
if (!this.$element.hasclass('is-open') || this.isrevealed) {
return;
}
var _this = this;
// foundation.move(this.options.transitiontime, this.$element, function() {
$('[data-off-canvas-wrapper]').removeclass('is-off-canvas-open is-open-' + _this.options.position);
_this.$element.removeclass('is-open');
// foundation._reflow();
// });
this.$element.attr('aria-hidden', 'true')
/**
* fires when the off-canvas menu opens.
* @event offcanvas#closed
*/
.trigger('closed.zf.offcanvas');
// if (_this.options.issticky || !_this.options.forcetop) {
// settimeout(function() {
// _this.$element[0].style.transform = '';
// $(window).off('scroll.zf.offcanvas');
// }, this.options.transitiontime);
// }
if (this.options.closeonclick) {
this.$exiter.removeclass('is-visible');
}
this.$triggers.attr('aria-expanded', 'false');
if (this.options.trapfocus) {
$('[data-off-canvas-content]').removeattr('tabindex');
}
}
/**
* toggles the off-canvas menu open or closed.
* @function
* @param {object} event - event object passed from listener.
* @param {jquery} trigger - element that triggered the off-canvas to open.
*/
}, {
key: 'toggle',
value: function toggle(event, trigger) {
if (this.$element.hasclass('is-open')) {
this.close(event, trigger);
} else {
this.open(event, trigger);
}
}
/**
* handles keyboard input when detected. when the escape key is pressed, the off-canvas menu closes, and focus is restored to the element that opened the menu.
* @function
* @private
*/
}, {
key: '_handlekeyboard',
value: function _handlekeyboard(event) {
if (event.which !== 27) return;
event.stoppropagation();
event.preventdefault();
this.close();
this.$lasttrigger.focus();
}
/**
* destroys the offcanvas plugin.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.close();
this.$element.off('.zf.trigger .zf.offcanvas');
this.$exiter.off('.zf.offcanvas');
foundation.unregisterplugin(this);
}
}]);
return offcanvas;
}();
offcanvas.defaults = {
/**
* allow the user to click outside of the menu to close it.
* @option
* @example true
*/
closeonclick: true,
/**
* amount of time in ms the open and close transition requires. if none selected, pulls from body style.
* @option
* @example 500
*/
transitiontime: 0,
/**
* direction the offcanvas opens from. determines class applied to body.
* @option
* @example left
*/
position: 'left',
/**
* force the page to scroll to top on open.
* @option
* @example true
*/
forcetop: true,
/**
* allow the offcanvas to remain open for certain breakpoints.
* @option
* @example false
*/
isrevealed: false,
/**
* breakpoint at which to reveal. js will use a regexp to target standard classes, if changing classnames, pass your class with the `revealclass` option.
* @option
* @example reveal-for-large
*/
revealon: null,
/**
* force focus to the offcanvas on open. if true, will focus the opening trigger on close.
* @option
* @example true
*/
autofocus: true,
/**
* class used to force an offcanvas to remain open. foundation defaults for this are `reveal-for-large` & `reveal-for-medium`.
* @option
* todo improve the regex testing for this.
* @example reveal-for-large
*/
revealclass: 'reveal-for-',
/**
* triggers optional focus trapping when opening an offcanvas. sets tabindex of [data-off-canvas-content] to -1 for accessibility purposes.
* @option
* @example true
*/
trapfocus: false
};
// window exports
foundation.plugin(offcanvas, 'offcanvas');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* orbit module.
* @module foundation.orbit
* @requires foundation.util.keyboard
* @requires foundation.util.motion
* @requires foundation.util.timerandimageloader
* @requires foundation.util.touch
*/
var orbit = function () {
/**
* creates a new instance of an orbit carousel.
* @class
* @param {jquery} element - jquery object to make into an orbit carousel.
* @param {object} options - overrides to the default plugin settings.
*/
function orbit(element, options) {
_classcallcheck(this, orbit);
this.$element = element;
this.options = $.extend({}, orbit.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'orbit');
foundation.keyboard.register('orbit', {
'ltr': {
'arrow_right': 'next',
'arrow_left': 'previous'
},
'rtl': {
'arrow_left': 'next',
'arrow_right': 'previous'
}
});
}
/**
* initializes the plugin by creating jquery collections, setting attributes, and starting the animation.
* @function
* @private
*/
_createclass(orbit, [{
key: '_init',
value: function _init() {
this.$wrapper = this.$element.find('.' + this.options.containerclass);
this.$slides = this.$element.find('.' + this.options.slideclass);
var $images = this.$element.find('img'),
initactive = this.$slides.filter('.is-active');
if (!initactive.length) {
this.$slides.eq(0).addclass('is-active');
}
if (!this.options.usemui) {
this.$slides.addclass('no-motionui');
}
if ($images.length) {
foundation.onimagesloaded($images, this._preparefororbit.bind(this));
} else {
this._preparefororbit(); //hehe
}
if (this.options.bullets) {
this._loadbullets();
}
this._events();
if (this.options.autoplay && this.$slides.length > 1) {
this.geosync();
}
if (this.options.accessible) {
// allow wrapper to be focusable to enable arrow navigation
this.$wrapper.attr('tabindex', 0);
}
}
/**
* creates a jquery collection of bullets, if they are being used.
* @function
* @private
*/
}, {
key: '_loadbullets',
value: function _loadbullets() {
this.$bullets = this.$element.find('.' + this.options.boxofbullets).find('button');
}
/**
* sets a `timer` object on the orbit, and starts the counter for the next slide.
* @function
*/
}, {
key: 'geosync',
value: function geosync() {
var _this = this;
this.timer = new foundation.timer(this.$element, {
duration: this.options.timerdelay,
infinite: false
}, function () {
_this.changeslide(true);
});
this.timer.start();
}
/**
* sets wrapper and slide heights for the orbit.
* @function
* @private
*/
}, {
key: '_preparefororbit',
value: function _preparefororbit() {
var _this = this;
this._setwrapperheight(function (max) {
_this._setslideheight(max);
});
}
/**
* calulates the height of each slide in the collection, and uses the tallest one for the wrapper height.
* @function
* @private
* @param {function} cb - a callback function to fire when complete.
*/
}, {
key: '_setwrapperheight',
value: function _setwrapperheight(cb) {
//rewrite this to `for` loop
var max = 0,
temp,
counter = 0;
this.$slides.each(function () {
temp = this.getboundingclientrect().height;
$(this).attr('data-slide', counter);
if (counter) {
//if not the first slide, set css position and display property
$(this).css({ 'position': 'relative', 'display': 'none' });
}
max = temp > max ? temp : max;
counter++;
});
if (counter === this.$slides.length) {
this.$wrapper.css({ 'height': max }); //only change the wrapper height property once.
cb(max); //fire callback with max height dimension.
}
}
/**
* sets the max-height of each slide.
* @function
* @private
*/
}, {
key: '_setslideheight',
value: function _setslideheight(height) {
this.$slides.each(function () {
$(this).css('max-height', height);
});
}
/**
* adds event listeners to basically everything within the element.
* @function
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
//***************************************
//**now using custom event - thanks to:**
//** yohai ararat of toronto **
//***************************************
if (this.$slides.length > 1) {
if (this.options.swipe) {
this.$slides.off('swipeleft.zf.orbit swiperight.zf.orbit').on('swipeleft.zf.orbit', function (e) {
e.preventdefault();
_this.changeslide(true);
}).on('swiperight.zf.orbit', function (e) {
e.preventdefault();
_this.changeslide(false);
});
}
//***************************************
if (this.options.autoplay) {
this.$slides.on('click.zf.orbit', function () {
_this.$element.data('clickedon', _this.$element.data('clickedon') ? false : true);
_this.timer[_this.$element.data('clickedon') ? 'pause' : 'start']();
});
if (this.options.pauseonhover) {
this.$element.on('mouseenter.zf.orbit', function () {
_this.timer.pause();
}).on('mouseleave.zf.orbit', function () {
if (!_this.$element.data('clickedon')) {
_this.timer.start();
}
});
}
}
if (this.options.navbuttons) {
var $controls = this.$element.find('.' + this.options.nextclass + ', .' + this.options.prevclass);
$controls.attr('tabindex', 0)
//also need to handle enter/return and spacebar key presses
.on('click.zf.orbit touchend.zf.orbit', function (e) {
e.preventdefault();
_this.changeslide($(this).hasclass(_this.options.nextclass));
});
}
if (this.options.bullets) {
this.$bullets.on('click.zf.orbit touchend.zf.orbit', function () {
if (/is-active/g.test(this.classname)) {
return false;
} //if this is active, kick out of function.
var idx = $(this).data('slide'),
ltr = idx > _this.$slides.filter('.is-active').data('slide'),
$slide = _this.$slides.eq(idx);
_this.changeslide(ltr, $slide, idx);
});
}
this.$wrapper.add(this.$bullets).on('keydown.zf.orbit', function (e) {
// handle keyboard event with keyboard util
foundation.keyboard.handlekey(e, 'orbit', {
next: function () {
_this.changeslide(true);
},
previous: function () {
_this.changeslide(false);
},
handled: function () {
// if bullet is focused, make sure focus moves
if ($(e.target).is(_this.$bullets)) {
_this.$bullets.filter('.is-active').focus();
}
}
});
});
}
}
/**
* changes the current slide to a new one.
* @function
* @param {boolean} isltr - flag if the slide should move left to right.
* @param {jquery} chosenslide - the jquery element of the slide to show next, if one is selected.
* @param {number} idx - the index of the new slide in its collection, if one chosen.
* @fires orbit#slidechange
*/
}, {
key: 'changeslide',
value: function changeslide(isltr, chosenslide, idx) {
var $curslide = this.$slides.filter('.is-active').eq(0);
if (/mui/g.test($curslide[0].classname)) {
return false;
} //if the slide is currently animating, kick out of the function
var $firstslide = this.$slides.first(),
$lastslide = this.$slides.last(),
dirin = isltr ? 'right' : 'left',
dirout = isltr ? 'left' : 'right',
_this = this,
$newslide;
if (!chosenslide) {
//most of the time, this will be auto played or clicked from the navbuttons.
$newslide = isltr ? //if wrapping enabled, check to see if there is a `next` or `prev` sibling, if not, select the first or last slide to fill in. if wrapping not enabled, attempt to select `next` or `prev`, if there's nothing there, the function will kick out on next step. crazy nested ternaries!!!!!
this.options.infinitewrap ? $curslide.next('.' + this.options.slideclass).length ? $curslide.next('.' + this.options.slideclass) : $firstslide : $curslide.next('.' + this.options.slideclass) : //pick next slide if moving left to right
this.options.infinitewrap ? $curslide.prev('.' + this.options.slideclass).length ? $curslide.prev('.' + this.options.slideclass) : $lastslide : $curslide.prev('.' + this.options.slideclass); //pick prev slide if moving right to left
} else {
$newslide = chosenslide;
}
if ($newslide.length) {
if (this.options.bullets) {
idx = idx || this.$slides.index($newslide); //grab index to update bullets
this._updatebullets(idx);
}
if (this.options.usemui) {
foundation.motion.animatein($newslide.addclass('is-active').css({ 'position': 'absolute', 'top': 0 }), this.options['animinfrom' + dirin], function () {
$newslide.css({ 'position': 'relative', 'display': 'block' }).attr('aria-live', 'polite');
});
foundation.motion.animateout($curslide.removeclass('is-active'), this.options['animoutto' + dirout], function () {
$curslide.removeattr('aria-live');
if (_this.options.autoplay && !_this.timer.ispaused) {
_this.timer.restart();
}
//do stuff?
});
} else {
$curslide.removeclass('is-active is-in').removeattr('aria-live').hide();
$newslide.addclass('is-active is-in').attr('aria-live', 'polite').show();
if (this.options.autoplay && !this.timer.ispaused) {
this.timer.restart();
}
}
/**
* triggers when the slide has finished animating in.
* @event orbit#slidechange
*/
this.$element.trigger('slidechange.zf.orbit', [$newslide]);
}
}
/**
* updates the active state of the bullets, if displayed.
* @function
* @private
* @param {number} idx - the index of the current slide.
*/
}, {
key: '_updatebullets',
value: function _updatebullets(idx) {
var $oldbullet = this.$element.find('.' + this.options.boxofbullets).find('.is-active').removeclass('is-active').blur(),
span = $oldbullet.find('span:last').detach(),
$newbullet = this.$bullets.eq(idx).addclass('is-active').append(span);
}
/**
* destroys the carousel and hides the element.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.off('.zf.orbit').find('*').off('.zf.orbit').end().hide();
foundation.unregisterplugin(this);
}
}]);
return orbit;
}();
orbit.defaults = {
/**
* tells the js to look for and loadbullets.
* @option
* @example true
*/
bullets: true,
/**
* tells the js to apply event listeners to nav buttons
* @option
* @example true
*/
navbuttons: true,
/**
* motion-ui animation class to apply
* @option
* @example 'slide-in-right'
*/
animinfromright: 'slide-in-right',
/**
* motion-ui animation class to apply
* @option
* @example 'slide-out-right'
*/
animouttoright: 'slide-out-right',
/**
* motion-ui animation class to apply
* @option
* @example 'slide-in-left'
*
*/
animinfromleft: 'slide-in-left',
/**
* motion-ui animation class to apply
* @option
* @example 'slide-out-left'
*/
animouttoleft: 'slide-out-left',
/**
* allows orbit to automatically animate on page load.
* @option
* @example true
*/
autoplay: true,
/**
* amount of time, in ms, between slide transitions
* @option
* @example 5000
*/
timerdelay: 5000,
/**
* allows orbit to infinitely loop through the slides
* @option
* @example true
*/
infinitewrap: true,
/**
* allows the orbit slides to bind to swipe events for mobile, requires an additional util library
* @option
* @example true
*/
swipe: true,
/**
* allows the timing function to pause animation on hover.
* @option
* @example true
*/
pauseonhover: true,
/**
* allows orbit to bind keyboard events to the slider, to animate frames with arrow keys
* @option
* @example true
*/
accessible: true,
/**
* class applied to the container of orbit
* @option
* @example 'orbit-container'
*/
containerclass: 'orbit-container',
/**
* class applied to individual slides.
* @option
* @example 'orbit-slide'
*/
slideclass: 'orbit-slide',
/**
* class applied to the bullet container. you're welcome.
* @option
* @example 'orbit-bullets'
*/
boxofbullets: 'orbit-bullets',
/**
* class applied to the `next` navigation button.
* @option
* @example 'orbit-next'
*/
nextclass: 'orbit-next',
/**
* class applied to the `previous` navigation button.
* @option
* @example 'orbit-previous'
*/
prevclass: 'orbit-previous',
/**
* boolean to flag the js to use motion ui classes or not. default to true for backwards compatability.
* @option
* @example true
*/
usemui: true
};
// window exports
foundation.plugin(orbit, 'orbit');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* responsivemenu module.
* @module foundation.responsivemenu
* @requires foundation.util.triggers
* @requires foundation.util.mediaquery
* @requires foundation.util.accordionmenu
* @requires foundation.util.drilldown
* @requires foundation.util.dropdown-menu
*/
var responsivemenu = function () {
/**
* creates a new instance of a responsive menu.
* @class
* @fires responsivemenu#init
* @param {jquery} element - jquery object to make into a dropdown menu.
* @param {object} options - overrides to the default plugin settings.
*/
function responsivemenu(element, options) {
_classcallcheck(this, responsivemenu);
this.$element = $(element);
this.rules = this.$element.data('responsive-menu');
this.currentmq = null;
this.currentplugin = null;
this._init();
this._events();
foundation.registerplugin(this, 'responsivemenu');
}
/**
* initializes the menu by parsing the classes from the 'data-responsivemenu' attribute on the element.
* @function
* @private
*/
_createclass(responsivemenu, [{
key: '_init',
value: function _init() {
// the first time an interchange plugin is initialized, this.rules is converted from a string of "classes" to an object of rules
if (typeof this.rules === 'string') {
var rulestree = {};
// parse rules from "classes" pulled from data attribute
var rules = this.rules.split(' ');
// iterate through every rule found
for (var i = 0; i < rules.length; i++) {
var rule = rules[i].split('-');
var rulesize = rule.length > 1 ? rule[0] : 'small';
var ruleplugin = rule.length > 1 ? rule[1] : rule[0];
if (menuplugins[ruleplugin] !== null) {
rulestree[rulesize] = menuplugins[ruleplugin];
}
}
this.rules = rulestree;
}
if (!$.isemptyobject(this.rules)) {
this._checkmediaqueries();
}
}
/**
* initializes events for the menu.
* @function
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
$(window).on('changed.zf.mediaquery', function () {
_this._checkmediaqueries();
});
// $(window).on('resize.zf.responsivemenu', function() {
// _this._checkmediaqueries();
// });
}
/**
* checks the current screen width against available media queries. if the media query has changed, and the plugin needed has changed, the plugins will swap out.
* @function
* @private
*/
}, {
key: '_checkmediaqueries',
value: function _checkmediaqueries() {
var matchedmq,
_this = this;
// iterate through each rule and find the last matching rule
$.each(this.rules, function (key) {
if (foundation.mediaquery.atleast(key)) {
matchedmq = key;
}
});
// no match? no dice
if (!matchedmq) return;
// plugin already initialized? we good
if (this.currentplugin instanceof this.rules[matchedmq].plugin) return;
// remove existing plugin-specific css classes
$.each(menuplugins, function (key, value) {
_this.$element.removeclass(value.cssclass);
});
// add the css class for the new plugin
this.$element.addclass(this.rules[matchedmq].cssclass);
// create an instance of the new plugin
if (this.currentplugin) this.currentplugin.destroy();
this.currentplugin = new this.rules[matchedmq].plugin(this.$element, {});
}
/**
* destroys the instance of the current plugin on this element, as well as the window resize handler that switches the plugins out.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.currentplugin.destroy();
$(window).off('.zf.responsivemenu');
foundation.unregisterplugin(this);
}
}]);
return responsivemenu;
}();
responsivemenu.defaults = {};
// the plugin matches the plugin classes with these plugin instances.
var menuplugins = {
dropdown: {
cssclass: 'dropdown',
plugin: foundation._plugins['dropdown-menu'] || null
},
drilldown: {
cssclass: 'drilldown',
plugin: foundation._plugins['drilldown'] || null
},
accordion: {
cssclass: 'accordion-menu',
plugin: foundation._plugins['accordion-menu'] || null
}
};
// window exports
foundation.plugin(responsivemenu, 'responsivemenu');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* responsivetoggle module.
* @module foundation.responsivetoggle
* @requires foundation.util.mediaquery
*/
var responsivetoggle = function () {
/**
* creates a new instance of tab bar.
* @class
* @fires responsivetoggle#init
* @param {jquery} element - jquery object to attach tab bar functionality to.
* @param {object} options - overrides to the default plugin settings.
*/
function responsivetoggle(element, options) {
_classcallcheck(this, responsivetoggle);
this.$element = $(element);
this.options = $.extend({}, responsivetoggle.defaults, this.$element.data(), options);
this._init();
this._events();
foundation.registerplugin(this, 'responsivetoggle');
}
/**
* initializes the tab bar by finding the target element, toggling element, and running update().
* @function
* @private
*/
_createclass(responsivetoggle, [{
key: '_init',
value: function _init() {
var targetid = this.$element.data('responsive-toggle');
if (!targetid) {
console.error('your tab bar needs an id of a menu as the value of data-tab-bar.');
}
this.$targetmenu = $('#' + targetid);
this.$toggler = this.$element.find('[data-toggle]');
this._update();
}
/**
* adds necessary event handlers for the tab bar to work.
* @function
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
this._updatemqhandler = this._update.bind(this);
$(window).on('changed.zf.mediaquery', this._updatemqhandler);
this.$toggler.on('click.zf.responsivetoggle', this.togglemenu.bind(this));
}
/**
* checks the current media query to determine if the tab bar should be visible or hidden.
* @function
* @private
*/
}, {
key: '_update',
value: function _update() {
// mobile
if (!foundation.mediaquery.atleast(this.options.hidefor)) {
this.$element.show();
this.$targetmenu.hide();
}
// desktop
else {
this.$element.hide();
this.$targetmenu.show();
}
}
/**
* toggles the element attached to the tab bar. the toggle only happens if the screen is small enough to allow it.
* @function
* @fires responsivetoggle#toggled
*/
}, {
key: 'togglemenu',
value: function togglemenu() {
if (!foundation.mediaquery.atleast(this.options.hidefor)) {
this.$targetmenu.toggle(0);
/**
* fires when the element attached to the tab bar toggles.
* @event responsivetoggle#toggled
*/
this.$element.trigger('toggled.zf.responsivetoggle');
}
}
}, {
key: 'destroy',
value: function destroy() {
this.$element.off('.zf.responsivetoggle');
this.$toggler.off('.zf.responsivetoggle');
$(window).off('changed.zf.mediaquery', this._updatemqhandler);
foundation.unregisterplugin(this);
}
}]);
return responsivetoggle;
}();
responsivetoggle.defaults = {
/**
* the breakpoint after which the menu is always shown, and the tab bar is hidden.
* @option
* @example 'medium'
*/
hidefor: 'medium'
};
// window exports
foundation.plugin(responsivetoggle, 'responsivetoggle');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* reveal module.
* @module foundation.reveal
* @requires foundation.util.keyboard
* @requires foundation.util.box
* @requires foundation.util.triggers
* @requires foundation.util.mediaquery
* @requires foundation.util.motion if using animations
*/
var reveal = function () {
/**
* creates a new instance of reveal.
* @class
* @param {jquery} element - jquery object to use for the modal.
* @param {object} options - optional parameters.
*/
function reveal(element, options) {
_classcallcheck(this, reveal);
this.$element = element;
this.options = $.extend({}, reveal.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'reveal');
foundation.keyboard.register('reveal', {
'enter': 'open',
'space': 'open',
'escape': 'close',
'tab': 'tab_forward',
'shift_tab': 'tab_backward'
});
}
/**
* initializes the modal by adding the overlay and close buttons, (if selected).
* @private
*/
_createclass(reveal, [{
key: '_init',
value: function _init() {
this.id = this.$element.attr('id');
this.isactive = false;
this.cached = { mq: foundation.mediaquery.current };
this.ismobile = mobilesniff();
this.$anchor = $('[data-open="' + this.id + '"]').length ? $('[data-open="' + this.id + '"]') : $('[data-toggle="' + this.id + '"]');
this.$anchor.attr({
'aria-controls': this.id,
'aria-haspopup': true,
'tabindex': 0
});
if (this.options.fullscreen || this.$element.hasclass('full')) {
this.options.fullscreen = true;
this.options.overlay = false;
}
if (this.options.overlay && !this.$overlay) {
this.$overlay = this._makeoverlay(this.id);
}
this.$element.attr({
'role': 'dialog',
'aria-hidden': true,
'data-yeti-box': this.id,
'data-resize': this.id
});
if (this.$overlay) {
this.$element.detach().appendto(this.$overlay);
} else {
this.$element.detach().appendto($('body'));
this.$element.addclass('without-overlay');
}
this._events();
if (this.options.deeplink && window.location.hash === '#' + this.id) {
$(window).one('load.zf.reveal', this.open.bind(this));
}
}
/**
* creates an overlay div to display behind the modal.
* @private
*/
}, {
key: '_makeoverlay',
value: function _makeoverlay(id) {
var $overlay = $('
').addclass('reveal-overlay').appendto('body');
return $overlay;
}
/**
* updates position of modal
* todo: figure out if we actually need to cache these values or if it doesn't matter
* @private
*/
}, {
key: '_updateposition',
value: function _updateposition() {
var width = this.$element.outerwidth();
var outerwidth = $(window).width();
var height = this.$element.outerheight();
var outerheight = $(window).height();
var left, top;
if (this.options.hoffset === 'auto') {
left = parseint((outerwidth - width) / 2, 10);
} else {
left = parseint(this.options.hoffset, 10);
}
if (this.options.voffset === 'auto') {
if (height > outerheight) {
top = parseint(math.min(100, outerheight / 10), 10);
} else {
top = parseint((outerheight - height) / 4, 10);
}
} else {
top = parseint(this.options.voffset, 10);
}
this.$element.css({ top: top + 'px' });
// only worry about left if we don't have an overlay or we havea horizontal offset,
// otherwise we're perfectly in the middle
if (!this.$overlay || this.options.hoffset !== 'auto') {
this.$element.css({ left: left + 'px' });
this.$element.css({ margin: '0px' });
}
}
/**
* adds event handlers for the modal.
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this2 = this;
var _this = this;
this.$element.on({
'open.zf.trigger': this.open.bind(this),
'close.zf.trigger': function (event, $element) {
if (event.target === _this.$element[0] || $(event.target).parents('[data-closable]')[0] === $element) {
// only close reveal when it's explicitly called
return _this2.close.apply(_this2);
}
},
'toggle.zf.trigger': this.toggle.bind(this),
'resizeme.zf.trigger': function () {
_this._updateposition();
}
});
if (this.$anchor.length) {
this.$anchor.on('keydown.zf.reveal', function (e) {
if (e.which === 13 || e.which === 32) {
e.stoppropagation();
e.preventdefault();
_this.open();
}
});
}
if (this.options.closeonclick && this.options.overlay) {
this.$overlay.off('.zf.reveal').on('click.zf.reveal', function (e) {
if (e.target === _this.$element[0] || $.contains(_this.$element[0], e.target)) {
return;
}
_this.close();
});
}
if (this.options.deeplink) {
$(window).on('popstate.zf.reveal:' + this.id, this._handlestate.bind(this));
}
}
/**
* handles modal methods on back/forward button clicks or any other event that triggers popstate.
* @private
*/
}, {
key: '_handlestate',
value: function _handlestate(e) {
if (window.location.hash === '#' + this.id && !this.isactive) {
this.open();
} else {
this.close();
}
}
/**
* opens the modal controlled by `this.$anchor`, and closes all others by default.
* @function
* @fires reveal#closeme
* @fires reveal#open
*/
}, {
key: 'open',
value: function open() {
var _this3 = this;
if (this.options.deeplink) {
var hash = '#' + this.id;
if (window.history.pushstate) {
window.history.pushstate(null, null, hash);
} else {
window.location.hash = hash;
}
}
this.isactive = true;
// make elements invisible, but remove display: none so we can get size and positioning
this.$element.css({ 'visibility': 'hidden' }).show().scrolltop(0);
if (this.options.overlay) {
this.$overlay.css({ 'visibility': 'hidden' }).show();
}
this._updateposition();
this.$element.hide().css({ 'visibility': '' });
if (this.$overlay) {
this.$overlay.css({ 'visibility': '' }).hide();
if (this.$element.hasclass('fast')) {
this.$overlay.addclass('fast');
} else if (this.$element.hasclass('slow')) {
this.$overlay.addclass('slow');
}
}
if (!this.options.multipleopened) {
/**
* fires immediately before the modal opens.
* closes any other modals that are currently open
* @event reveal#closeme
*/
this.$element.trigger('closeme.zf.reveal', this.id);
}
// motion ui method of reveal
if (this.options.animationin) {
var _this;
(function () {
var afteranimationfocus = function () {
_this.$element.attr({
'aria-hidden': false,
'tabindex': -1
}).focus();
console.log('focus');
};
_this = _this3;
if (_this3.options.overlay) {
foundation.motion.animatein(_this3.$overlay, 'fade-in');
}
foundation.motion.animatein(_this3.$element, _this3.options.animationin, function () {
_this3.focusableelements = foundation.keyboard.findfocusable(_this3.$element);
afteranimationfocus();
});
})();
}
// jquery method of reveal
else {
if (this.options.overlay) {
this.$overlay.show(0);
}
this.$element.show(this.options.showdelay);
}
// handle accessibility
this.$element.attr({
'aria-hidden': false,
'tabindex': -1
}).focus();
/**
* fires when the modal has successfully opened.
* @event reveal#open
*/
this.$element.trigger('open.zf.reveal');
if (this.ismobile) {
this.originalscrollpos = window.pageyoffset;
$('html, body').addclass('is-reveal-open');
} else {
$('body').addclass('is-reveal-open');
}
settimeout(function () {
_this3._extrahandlers();
}, 0);
}
/**
* adds extra event handlers for the body and window if necessary.
* @private
*/
}, {
key: '_extrahandlers',
value: function _extrahandlers() {
var _this = this;
this.focusableelements = foundation.keyboard.findfocusable(this.$element);
if (!this.options.overlay && this.options.closeonclick && !this.options.fullscreen) {
$('body').on('click.zf.reveal', function (e) {
if (e.target === _this.$element[0] || $.contains(_this.$element[0], e.target)) {
return;
}
_this.close();
});
}
if (this.options.closeonesc) {
$(window).on('keydown.zf.reveal', function (e) {
foundation.keyboard.handlekey(e, 'reveal', {
close: function () {
if (_this.options.closeonesc) {
_this.close();
_this.$anchor.focus();
}
}
});
});
}
// lock focus within modal while tabbing
this.$element.on('keydown.zf.reveal', function (e) {
var $target = $(this);
// handle keyboard event with keyboard util
foundation.keyboard.handlekey(e, 'reveal', {
tab_forward: function () {
if (_this.$element.find(':focus').is(_this.focusableelements.eq(-1))) {
// left modal downwards, setting focus to first element
_this.focusableelements.eq(0).focus();
return true;
}
if (_this.focusableelements.length === 0) {
// no focusable elements inside the modal at all, prevent tabbing in general
return true;
}
},
tab_backward: function () {
if (_this.$element.find(':focus').is(_this.focusableelements.eq(0)) || _this.$element.is(':focus')) {
// left modal upwards, setting focus to last element
_this.focusableelements.eq(-1).focus();
return true;
}
if (_this.focusableelements.length === 0) {
// no focusable elements inside the modal at all, prevent tabbing in general
return true;
}
},
open: function () {
if (_this.$element.find(':focus').is(_this.$element.find('[data-close]'))) {
settimeout(function () {
// set focus back to anchor if close button has been activated
_this.$anchor.focus();
}, 1);
} else if ($target.is(_this.focusableelements)) {
// dont't trigger if acual element has focus (i.e. inputs, links, ...)
_this.open();
}
},
close: function () {
if (_this.options.closeonesc) {
_this.close();
_this.$anchor.focus();
}
},
handled: function (preventdefault) {
if (preventdefault) {
e.preventdefault();
}
}
});
});
}
/**
* closes the modal.
* @function
* @fires reveal#closed
*/
}, {
key: 'close',
value: function close() {
if (!this.isactive || !this.$element.is(':visible')) {
return false;
}
var _this = this;
// motion ui method of hiding
if (this.options.animationout) {
if (this.options.overlay) {
foundation.motion.animateout(this.$overlay, 'fade-out', finishup);
} else {
finishup();
}
foundation.motion.animateout(this.$element, this.options.animationout);
}
// jquery method of hiding
else {
if (this.options.overlay) {
this.$overlay.hide(0, finishup);
} else {
finishup();
}
this.$element.hide(this.options.hidedelay);
}
// conditionals to remove extra event listeners added on open
if (this.options.closeonesc) {
$(window).off('keydown.zf.reveal');
}
if (!this.options.overlay && this.options.closeonclick) {
$('body').off('click.zf.reveal');
}
this.$element.off('keydown.zf.reveal');
function finishup() {
if (_this.ismobile) {
$('html, body').removeclass('is-reveal-open');
if (_this.originalscrollpos) {
$('body').scrolltop(_this.originalscrollpos);
_this.originalscrollpos = null;
}
} else {
$('body').removeclass('is-reveal-open');
}
_this.$element.attr('aria-hidden', true);
/**
* fires when the modal is done closing.
* @event reveal#closed
*/
_this.$element.trigger('closed.zf.reveal');
}
/**
* resets the modal content
* this prevents a running video to keep going in the background
*/
if (this.options.resetonclose) {
this.$element.html(this.$element.html());
}
this.isactive = false;
if (_this.options.deeplink) {
if (window.history.replacestate) {
window.history.replacestate("", document.title, window.location.pathname);
} else {
window.location.hash = '';
}
}
}
/**
* toggles the open/closed state of a modal.
* @function
*/
}, {
key: 'toggle',
value: function toggle() {
if (this.isactive) {
this.close();
} else {
this.open();
}
}
}, {
key: 'destroy',
/**
* destroys an instance of a modal.
* @function
*/
value: function destroy() {
if (this.options.overlay) {
this.$element.appendto($('body')); // move $element outside of $overlay to prevent error unregisterplugin()
this.$overlay.hide().off().remove();
}
this.$element.hide().off();
this.$anchor.off('.zf');
$(window).off('.zf.reveal:' + this.id);
foundation.unregisterplugin(this);
}
}]);
return reveal;
}();
reveal.defaults = {
/**
* motion-ui class to use for animated elements. if none used, defaults to simple show/hide.
* @option
* @example 'slide-in-left'
*/
animationin: '',
/**
* motion-ui class to use for animated elements. if none used, defaults to simple show/hide.
* @option
* @example 'slide-out-right'
*/
animationout: '',
/**
* time, in ms, to delay the opening of a modal after a click if no animation used.
* @option
* @example 10
*/
showdelay: 0,
/**
* time, in ms, to delay the closing of a modal after a click if no animation used.
* @option
* @example 10
*/
hidedelay: 0,
/**
* allows a click on the body/overlay to close the modal.
* @option
* @example true
*/
closeonclick: true,
/**
* allows the modal to close if the user presses the `escape` key.
* @option
* @example true
*/
closeonesc: true,
/**
* if true, allows multiple modals to be displayed at once.
* @option
* @example false
*/
multipleopened: false,
/**
* distance, in pixels, the modal should push down from the top of the screen.
* @option
* @example auto
*/
voffset: 'auto',
/**
* distance, in pixels, the modal should push in from the side of the screen.
* @option
* @example auto
*/
hoffset: 'auto',
/**
* allows the modal to be fullscreen, completely blocking out the rest of the view. js checks for this as well.
* @option
* @example false
*/
fullscreen: false,
/**
* percentage of screen height the modal should push up from the bottom of the view.
* @option
* @example 10
*/
btmoffsetpct: 10,
/**
* allows the modal to generate an overlay div, which will cover the view when modal opens.
* @option
* @example true
*/
overlay: true,
/**
* allows the modal to remove and reinject markup on close. should be true if using video elements w/o using provider's api, otherwise, videos will continue to play in the background.
* @option
* @example false
*/
resetonclose: false,
/**
* allows the modal to alter the url on open/close, and allows the use of the `back` button to close modals. also, allows a modal to auto-maniacally open on page load if the hash === the modal's user-set id.
* @option
* @example false
*/
deeplink: false
};
// window exports
foundation.plugin(reveal, 'reveal');
function iphonesniff() {
return (/ip(ad|hone|od).*os/.test(window.navigator.useragent)
);
}
function androidsniff() {
return (/android/.test(window.navigator.useragent)
);
}
function mobilesniff() {
return iphonesniff() || androidsniff();
}
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* slider module.
* @module foundation.slider
* @requires foundation.util.motion
* @requires foundation.util.triggers
* @requires foundation.util.keyboard
* @requires foundation.util.touch
*/
var slider = function () {
/**
* creates a new instance of a drilldown menu.
* @class
* @param {jquery} element - jquery object to make into an accordion menu.
* @param {object} options - overrides to the default plugin settings.
*/
function slider(element, options) {
_classcallcheck(this, slider);
this.$element = element;
this.options = $.extend({}, slider.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'slider');
foundation.keyboard.register('slider', {
'ltr': {
'arrow_right': 'increase',
'arrow_up': 'increase',
'arrow_down': 'decrease',
'arrow_left': 'decrease',
'shift_arrow_right': 'increase_fast',
'shift_arrow_up': 'increase_fast',
'shift_arrow_down': 'decrease_fast',
'shift_arrow_left': 'decrease_fast'
},
'rtl': {
'arrow_left': 'increase',
'arrow_right': 'decrease',
'shift_arrow_left': 'increase_fast',
'shift_arrow_right': 'decrease_fast'
}
});
}
/**
* initilizes the plugin by reading/setting attributes, creating collections and setting the initial position of the handle(s).
* @function
* @private
*/
_createclass(slider, [{
key: '_init',
value: function _init() {
this.inputs = this.$element.find('input');
this.handles = this.$element.find('[data-slider-handle]');
this.$handle = this.handles.eq(0);
this.$input = this.inputs.length ? this.inputs.eq(0) : $('#' + this.$handle.attr('aria-controls'));
this.$fill = this.$element.find('[data-slider-fill]').css(this.options.vertical ? 'height' : 'width', 0);
var isdbl = false,
_this = this;
if (this.options.disabled || this.$element.hasclass(this.options.disabledclass)) {
this.options.disabled = true;
this.$element.addclass(this.options.disabledclass);
}
if (!this.inputs.length) {
this.inputs = $().add(this.$input);
this.options.binding = true;
}
this._setinitattr(0);
this._events(this.$handle);
if (this.handles[1]) {
this.options.doublesided = true;
this.$handle2 = this.handles.eq(1);
this.$input2 = this.inputs.length > 1 ? this.inputs.eq(1) : $('#' + this.$handle2.attr('aria-controls'));
if (!this.inputs[1]) {
this.inputs = this.inputs.add(this.$input2);
}
isdbl = true;
this._sethandlepos(this.$handle, this.options.initialstart, true, function () {
_this._sethandlepos(_this.$handle2, _this.options.initialend, true);
});
// this.$handle.triggerhandler('click.zf.slider');
this._setinitattr(1);
this._events(this.$handle2);
}
if (!isdbl) {
this._sethandlepos(this.$handle, this.options.initialstart, true);
}
}
/**
* sets the position of the selected handle and fill bar.
* @function
* @private
* @param {jquery} $hndl - the selected handle to move.
* @param {number} location - floating point between the start and end values of the slider bar.
* @param {function} cb - callback function to fire on completion.
* @fires slider#moved
* @fires slider#changed
*/
}, {
key: '_sethandlepos',
value: function _sethandlepos($hndl, location, noinvert, cb) {
// don't move if the slider has been disabled since its initialization
if (this.$element.hasclass(this.options.disabledclass)) {
return;
}
//might need to alter that slightly for bars that will have odd number selections.
location = parsefloat(location); //on input change events, convert string to number...grumble.
// prevent slider from running out of bounds, if value exceeds the limits set through options, override the value to min/max
if (location < this.options.start) {
location = this.options.start;
} else if (location > this.options.end) {
location = this.options.end;
}
var isdbl = this.options.doublesided;
if (isdbl) {
//this block is to prevent 2 handles from crossing eachother. could/should be improved.
if (this.handles.index($hndl) === 0) {
var h2val = parsefloat(this.$handle2.attr('aria-valuenow'));
location = location >= h2val ? h2val - this.options.step : location;
} else {
var h1val = parsefloat(this.$handle.attr('aria-valuenow'));
location = location <= h1val ? h1val + this.options.step : location;
}
}
//this is for single-handled vertical sliders, it adjusts the value to account for the slider being "upside-down"
//for click and drag events, it's weird due to the scale(-1, 1) css property
if (this.options.vertical && !noinvert) {
location = this.options.end - location;
}
var _this = this,
vert = this.options.vertical,
horw = vert ? 'height' : 'width',
lort = vert ? 'top' : 'left',
handledim = $hndl[0].getboundingclientrect()[horw],
elemdim = this.$element[0].getboundingclientrect()[horw],
//percentage of bar min/max value based on click or drag point
pctofbar = percent(location - this.options.start, this.options.end - this.options.start).tofixed(2),
//number of actual pixels to shift the handle, based on the percentage obtained above
pxtomove = (elemdim - handledim) * pctofbar,
//percentage of bar to shift the handle
movement = (percent(pxtomove, elemdim) * 100).tofixed(this.options.decimal);
//fixing the decimal value for the location number, is passed to other methods as a fixed floating-point value
location = parsefloat(location.tofixed(this.options.decimal));
// declare empty object for css adjustments, only used with 2 handled-sliders
var css = {};
this._setvalues($hndl, location);
// todo update to calculate based on values set to respective inputs??
if (isdbl) {
var islefthndl = this.handles.index($hndl) === 0,
//empty variable, will be used for min-height/width for fill bar
dim,
//percentage w/h of the handle compared to the slider bar
handlepct = ~ ~(percent(handledim, elemdim) * 100);
//if left handle, the math is slightly different than if it's the right handle, and the left/top property needs to be changed for the fill bar
if (islefthndl) {
//left or top percentage value to apply to the fill bar.
css[lort] = movement + '%';
//calculate the new min-height/width for the fill bar.
dim = parsefloat(this.$handle2[0].style[lort]) - movement + handlepct;
//this callback is necessary to prevent errors and allow the proper placement and initialization of a 2-handled slider
//plus, it means we don't care if 'dim' isnan on init, it won't be in the future.
if (cb && typeof cb === 'function') {
cb();
} //this is only needed for the initialization of 2 handled sliders
} else {
//just caching the value of the left/bottom handle's left/top property
var handlepos = parsefloat(this.$handle[0].style[lort]);
//calculate the new min-height/width for the fill bar. use isnan to prevent false positives for numbers <= 0
//based on the percentage of movement of the handle being manipulated, less the opposing handle's left/top position, plus the percentage w/h of the handle itself
dim = movement - (isnan(handlepos) ? this.options.initialstart / ((this.options.end - this.options.start) / 100) : handlepos) + handlepct;
}
// assign the min-height/width to our css object
css['min-' + horw] = dim + '%';
}
this.$element.one('finished.zf.animate', function () {
/**
* fires when the handle is done moving.
* @event slider#moved
*/
_this.$element.trigger('moved.zf.slider', [$hndl]);
});
//because we don't know exactly how the handle will be moved, check the amount of time it should take to move.
var movetime = this.$element.data('dragging') ? 1000 / 60 : this.options.movetime;
foundation.move(movetime, $hndl, function () {
//adjusting the left/top property of the handle, based on the percentage calculated above
$hndl.css(lort, movement + '%');
if (!_this.options.doublesided) {
//if single-handled, a simple method to expand the fill bar
_this.$fill.css(horw, pctofbar * 100 + '%');
} else {
//otherwise, use the css object we created above
_this.$fill.css(css);
}
});
/**
* fires when the value has not been change for a given time.
* @event slider#changed
*/
cleartimeout(_this.timeout);
_this.timeout = settimeout(function () {
_this.$element.trigger('changed.zf.slider', [$hndl]);
}, _this.options.changeddelay);
}
/**
* sets the initial attribute for the slider element.
* @function
* @private
* @param {number} idx - index of the current handle/input to use.
*/
}, {
key: '_setinitattr',
value: function _setinitattr(idx) {
var id = this.inputs.eq(idx).attr('id') || foundation.getyodigits(6, 'slider');
this.inputs.eq(idx).attr({
'id': id,
'max': this.options.end,
'min': this.options.start,
'step': this.options.step
});
this.handles.eq(idx).attr({
'role': 'slider',
'aria-controls': id,
'aria-valuemax': this.options.end,
'aria-valuemin': this.options.start,
'aria-valuenow': idx === 0 ? this.options.initialstart : this.options.initialend,
'aria-orientation': this.options.vertical ? 'vertical' : 'horizontal',
'tabindex': 0
});
}
/**
* sets the input and `aria-valuenow` values for the slider element.
* @function
* @private
* @param {jquery} $handle - the currently selected handle.
* @param {number} val - floating point of the new value.
*/
}, {
key: '_setvalues',
value: function _setvalues($handle, val) {
var idx = this.options.doublesided ? this.handles.index($handle) : 0;
this.inputs.eq(idx).val(val);
$handle.attr('aria-valuenow', val);
}
/**
* handles events on the slider element.
* calculates the new location of the current handle.
* if there are two handles and the bar was clicked, it determines which handle to move.
* @function
* @private
* @param {object} e - the `event` object passed from the listener.
* @param {jquery} $handle - the current handle to calculate for, if selected.
* @param {number} val - floating point number for the new value of the slider.
* todo clean this up, there's a lot of repeated code between this and the _sethandlepos fn.
*/
}, {
key: '_handleevent',
value: function _handleevent(e, $handle, val) {
var value, hasval;
if (!val) {
//click or drag events
e.preventdefault();
var _this = this,
vertical = this.options.vertical,
param = vertical ? 'height' : 'width',
direction = vertical ? 'top' : 'left',
eventoffset = vertical ? e.pagey : e.pagex,
halfofhandle = this.$handle[0].getboundingclientrect()[param] / 2,
bardim = this.$element[0].getboundingclientrect()[param],
windowscroll = vertical ? $(window).scrolltop() : $(window).scrollleft();
var elemoffset = this.$element.offset()[direction];
// touch events emulated by the touch util give position relative to screen, add window.scroll to event coordinates...
// best way to guess this is simulated is if clienty == pagey
if (e.clienty === e.pagey) {
eventoffset = eventoffset + windowscroll;
}
var eventfrombar = eventoffset - elemoffset;
var barxy;
if (eventfrombar < 0) {
barxy = 0;
} else if (eventfrombar > bardim) {
barxy = bardim;
} else {
barxy = eventfrombar;
}
var offsetpct = percent(barxy, bardim);
value = (this.options.end - this.options.start) * offsetpct + this.options.start;
// turn everything around for rtl, yay math!
if (foundation.rtl() && !this.options.vertical) {
value = this.options.end - value;
}
value = _this._adjustvalue(null, value);
//boolean flag for the sethandlepos fn, specifically for vertical sliders
hasval = false;
if (!$handle) {
//figure out which handle it is, pass it to the next function.
var firsthndlpos = absposition(this.$handle, direction, barxy, param),
secndhndlpos = absposition(this.$handle2, direction, barxy, param);
$handle = firsthndlpos <= secndhndlpos ? this.$handle : this.$handle2;
}
} else {
//change event on input
value = this._adjustvalue(null, val);
hasval = true;
}
this._sethandlepos($handle, value, hasval);
}
/**
* adjustes value for handle in regard to step value. returns adjusted value
* @function
* @private
* @param {jquery} $handle - the selected handle.
* @param {number} value - value to adjust. used if $handle is falsy
*/
}, {
key: '_adjustvalue',
value: function _adjustvalue($handle, value) {
var val,
step = this.options.step,
div = parsefloat(step / 2),
left,
prev_val,
next_val;
if (!!$handle) {
val = parsefloat($handle.attr('aria-valuenow'));
} else {
val = value;
}
left = val % step;
prev_val = val - left;
next_val = prev_val + step;
if (left === 0) {
return val;
}
val = val >= prev_val + div ? next_val : prev_val;
return val;
}
/**
* adds event listeners to the slider elements.
* @function
* @private
* @param {jquery} $handle - the current handle to apply listeners to.
*/
}, {
key: '_events',
value: function _events($handle) {
var _this = this,
curhandle,
timer;
this.inputs.off('change.zf.slider').on('change.zf.slider', function (e) {
var idx = _this.inputs.index($(this));
_this._handleevent(e, _this.handles.eq(idx), $(this).val());
});
if (this.options.clickselect) {
this.$element.off('click.zf.slider').on('click.zf.slider', function (e) {
if (_this.$element.data('dragging')) {
return false;
}
if (!$(e.target).is('[data-slider-handle]')) {
if (_this.options.doublesided) {
_this._handleevent(e);
} else {
_this._handleevent(e, _this.$handle);
}
}
});
}
if (this.options.draggable) {
this.handles.addtouch();
var $body = $('body');
$handle.off('mousedown.zf.slider').on('mousedown.zf.slider', function (e) {
$handle.addclass('is-dragging');
_this.$fill.addclass('is-dragging'); //
_this.$element.data('dragging', true);
curhandle = $(e.currenttarget);
$body.on('mousemove.zf.slider', function (e) {
e.preventdefault();
_this._handleevent(e, curhandle);
}).on('mouseup.zf.slider', function (e) {
_this._handleevent(e, curhandle);
$handle.removeclass('is-dragging');
_this.$fill.removeclass('is-dragging');
_this.$element.data('dragging', false);
$body.off('mousemove.zf.slider mouseup.zf.slider');
});
})
// prevent events triggered by touch
.on('selectstart.zf.slider touchmove.zf.slider', function (e) {
e.preventdefault();
});
}
$handle.off('keydown.zf.slider').on('keydown.zf.slider', function (e) {
var _$handle = $(this),
idx = _this.options.doublesided ? _this.handles.index(_$handle) : 0,
oldvalue = parsefloat(_this.inputs.eq(idx).val()),
newvalue;
// handle keyboard event with keyboard util
foundation.keyboard.handlekey(e, 'slider', {
decrease: function () {
newvalue = oldvalue - _this.options.step;
},
increase: function () {
newvalue = oldvalue + _this.options.step;
},
decrease_fast: function () {
newvalue = oldvalue - _this.options.step * 10;
},
increase_fast: function () {
newvalue = oldvalue + _this.options.step * 10;
},
handled: function () {
// only set handle pos when event was handled specially
e.preventdefault();
_this._sethandlepos(_$handle, newvalue, true);
}
});
/*if (newvalue) { // if pressed key has special function, update value
e.preventdefault();
_this._sethandlepos(_$handle, newvalue);
}*/
});
}
/**
* destroys the slider plugin.
*/
}, {
key: 'destroy',
value: function destroy() {
this.handles.off('.zf.slider');
this.inputs.off('.zf.slider');
this.$element.off('.zf.slider');
foundation.unregisterplugin(this);
}
}]);
return slider;
}();
slider.defaults = {
/**
* minimum value for the slider scale.
* @option
* @example 0
*/
start: 0,
/**
* maximum value for the slider scale.
* @option
* @example 100
*/
end: 100,
/**
* minimum value change per change event.
* @option
* @example 1
*/
step: 1,
/**
* value at which the handle/input *(left handle/first input)* should be set to on initialization.
* @option
* @example 0
*/
initialstart: 0,
/**
* value at which the right handle/second input should be set to on initialization.
* @option
* @example 100
*/
initialend: 100,
/**
* allows the input to be located outside the container and visible. set to by the js
* @option
* @example false
*/
binding: false,
/**
* allows the user to click/tap on the slider bar to select a value.
* @option
* @example true
*/
clickselect: true,
/**
* set to true and use the `vertical` class to change alignment to vertical.
* @option
* @example false
*/
vertical: false,
/**
* allows the user to drag the slider handle(s) to select a value.
* @option
* @example true
*/
draggable: true,
/**
* disables the slider and prevents event listeners from being applied. double checked by js with `disabledclass`.
* @option
* @example false
*/
disabled: false,
/**
* allows the use of two handles. double checked by the js. changes some logic handling.
* @option
* @example false
*/
doublesided: false,
/**
* potential future feature.
*/
// steps: 100,
/**
* number of decimal places the plugin should go to for floating point precision.
* @option
* @example 2
*/
decimal: 2,
/**
* time delay for dragged elements.
*/
// dragdelay: 0,
/**
* time, in ms, to animate the movement of a slider handle if user clicks/taps on the bar. needs to be manually set if updating the transition time in the sass settings.
* @option
* @example 200
*/
movetime: 200, //update this if changing the transition time in the sass
/**
* class applied to disabled sliders.
* @option
* @example 'disabled'
*/
disabledclass: 'disabled',
/**
* will invert the default layout for a vertical slider.
* @option
* @example false
*/
invertvertical: false,
/**
* milliseconds before the `changed.zf-slider` event is triggered after value change.
* @option
* @example 500
*/
changeddelay: 500
};
function percent(frac, num) {
return frac / num;
}
function absposition($handle, dir, clickpos, param) {
return math.abs($handle.position()[dir] + $handle[param]() / 2 - clickpos);
}
// window exports
foundation.plugin(slider, 'slider');
}(jquery);
//*********this is in case we go to static, absolute positions instead of dynamic positioning********
// this.setsteps(function() {
// _this._events();
// var initstart = _this.options.positions[_this.options.initialstart - 1] || null;
// var initend = _this.options.initialend ? _this.options.position[_this.options.initialend - 1] : null;
// if (initstart || initend) {
// _this._handleevent(initstart, initend);
// }
// });
//***********the other part of absolute positions*************
// slider.prototype.setsteps = function(cb) {
// var poschange = this.$element.outerwidth() / this.options.steps;
// var counter = 0
// while(counter < this.options.steps) {
// if (counter) {
// this.options.positions.push(this.options.positions[counter - 1] + poschange);
// } else {
// this.options.positions.push(poschange);
// }
// counter++;
// }
// cb();
// };
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* sticky module.
* @module foundation.sticky
* @requires foundation.util.triggers
* @requires foundation.util.mediaquery
*/
var sticky = function () {
/**
* creates a new instance of a sticky thing.
* @class
* @param {jquery} element - jquery object to make sticky.
* @param {object} options - options object passed when creating the element programmatically.
*/
function sticky(element, options) {
_classcallcheck(this, sticky);
this.$element = element;
this.options = $.extend({}, sticky.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'sticky');
}
/**
* initializes the sticky element by adding classes, getting/setting dimensions, breakpoints and attributes
* @function
* @private
*/
_createclass(sticky, [{
key: '_init',
value: function _init() {
var $parent = this.$element.parent('[data-sticky-container]'),
id = this.$element[0].id || foundation.getyodigits(6, 'sticky'),
_this = this;
if (!$parent.length) {
this.waswrapped = true;
}
this.$container = $parent.length ? $parent : $(this.options.container).wrapinner(this.$element);
this.$container.addclass(this.options.containerclass);
this.$element.addclass(this.options.stickyclass).attr({ 'data-resize': id });
this.scrollcount = this.options.checkevery;
this.isstuck = false;
$(window).one('load.zf.sticky', function () {
if (_this.options.anchor !== '') {
_this.$anchor = $('#' + _this.options.anchor);
} else {
_this._parsepoints();
}
_this._setsizes(function () {
_this._calc(false);
});
_this._events(id.split('-').reverse().join('-'));
});
}
/**
* if using multiple elements as anchors, calculates the top and bottom pixel values the sticky thing should stick and unstick on.
* @function
* @private
*/
}, {
key: '_parsepoints',
value: function _parsepoints() {
var top = this.options.topanchor == "" ? 1 : this.options.topanchor,
btm = this.options.btmanchor == "" ? document.documentelement.scrollheight : this.options.btmanchor,
pts = [top, btm],
breaks = {};
for (var i = 0, len = pts.length; i < len && pts[i]; i++) {
var pt;
if (typeof pts[i] === 'number') {
pt = pts[i];
} else {
var place = pts[i].split(':'),
anchor = $('#' + place[0]);
pt = anchor.offset().top;
if (place[1] && place[1].tolowercase() === 'bottom') {
pt += anchor[0].getboundingclientrect().height;
}
}
breaks[i] = pt;
}
this.points = breaks;
return;
}
/**
* adds event handlers for the scrolling element.
* @private
* @param {string} id - psuedo-random id for unique scroll event listener.
*/
}, {
key: '_events',
value: function _events(id) {
var _this = this,
scrolllistener = this.scrolllistener = 'scroll.zf.' + id;
if (this.ison) {
return;
}
if (this.canstick) {
this.ison = true;
$(window).off(scrolllistener).on(scrolllistener, function (e) {
if (_this.scrollcount === 0) {
_this.scrollcount = _this.options.checkevery;
_this._setsizes(function () {
_this._calc(false, window.pageyoffset);
});
} else {
_this.scrollcount--;
_this._calc(false, window.pageyoffset);
}
});
}
this.$element.off('resizeme.zf.trigger').on('resizeme.zf.trigger', function (e, el) {
_this._setsizes(function () {
_this._calc(false);
if (_this.canstick) {
if (!_this.ison) {
_this._events(id);
}
} else if (_this.ison) {
_this._pauselisteners(scrolllistener);
}
});
});
}
/**
* removes event handlers for scroll and change events on anchor.
* @fires sticky#pause
* @param {string} scrolllistener - unique, namespaced scroll listener attached to `window`
*/
}, {
key: '_pauselisteners',
value: function _pauselisteners(scrolllistener) {
this.ison = false;
$(window).off(scrolllistener);
/**
* fires when the plugin is paused due to resize event shrinking the view.
* @event sticky#pause
* @private
*/
this.$element.trigger('pause.zf.sticky');
}
/**
* called on every `scroll` event and on `_init`
* fires functions based on booleans and cached values
* @param {boolean} checksizes - true if plugin should recalculate sizes and breakpoints.
* @param {number} scroll - current scroll position passed from scroll event cb function. if not passed, defaults to `window.pageyoffset`.
*/
}, {
key: '_calc',
value: function _calc(checksizes, scroll) {
if (checksizes) {
this._setsizes();
}
if (!this.canstick) {
if (this.isstuck) {
this._removesticky(true);
}
return false;
}
if (!scroll) {
scroll = window.pageyoffset;
}
if (scroll >= this.toppoint) {
if (scroll <= this.bottompoint) {
if (!this.isstuck) {
this._setsticky();
}
} else {
if (this.isstuck) {
this._removesticky(false);
}
}
} else {
if (this.isstuck) {
this._removesticky(true);
}
}
}
/**
* causes the $element to become stuck.
* adds `position: fixed;`, and helper classes.
* @fires sticky#stuckto
* @function
* @private
*/
}, {
key: '_setsticky',
value: function _setsticky() {
var _this = this,
stickto = this.options.stickto,
mrgn = stickto === 'top' ? 'margintop' : 'marginbottom',
notstuckto = stickto === 'top' ? 'bottom' : 'top',
css = {};
css[mrgn] = this.options[mrgn] + 'em';
css[stickto] = 0;
css[notstuckto] = 'auto';
css['left'] = this.$container.offset().left + parseint(window.getcomputedstyle(this.$container[0])["padding-left"], 10);
this.isstuck = true;
this.$element.removeclass('is-anchored is-at-' + notstuckto).addclass('is-stuck is-at-' + stickto).css(css)
/**
* fires when the $element has become `position: fixed;`
* namespaced to `top` or `bottom`, e.g. `sticky.zf.stuckto:top`
* @event sticky#stuckto
*/
.trigger('sticky.zf.stuckto:' + stickto);
this.$element.on("transitionend webkittransitionend otransitionend otransitionend mstransitionend", function () {
_this._setsizes();
});
}
/**
* causes the $element to become unstuck.
* removes `position: fixed;`, and helper classes.
* adds other helper classes.
* @param {boolean} istop - tells the function if the $element should anchor to the top or bottom of its $anchor element.
* @fires sticky#unstuckfrom
* @private
*/
}, {
key: '_removesticky',
value: function _removesticky(istop) {
var stickto = this.options.stickto,
sticktotop = stickto === 'top',
css = {},
anchorpt = (this.points ? this.points[1] - this.points[0] : this.anchorheight) - this.elemheight,
mrgn = sticktotop ? 'margintop' : 'marginbottom',
notstuckto = sticktotop ? 'bottom' : 'top',
toporbottom = istop ? 'top' : 'bottom';
css[mrgn] = 0;
css['bottom'] = 'auto';
if (istop) {
css['top'] = 0;
} else {
css['top'] = anchorpt;
}
css['left'] = '';
this.isstuck = false;
this.$element.removeclass('is-stuck is-at-' + stickto).addclass('is-anchored is-at-' + toporbottom).css(css)
/**
* fires when the $element has become anchored.
* namespaced to `top` or `bottom`, e.g. `sticky.zf.unstuckfrom:bottom`
* @event sticky#unstuckfrom
*/
.trigger('sticky.zf.unstuckfrom:' + toporbottom);
}
/**
* sets the $element and $container sizes for plugin.
* calls `_setbreakpoints`.
* @param {function} cb - optional callback function to fire on completion of `_setbreakpoints`.
* @private
*/
}, {
key: '_setsizes',
value: function _setsizes(cb) {
this.canstick = foundation.mediaquery.atleast(this.options.stickyon);
if (!this.canstick) {
cb();
}
var _this = this,
newelemwidth = this.$container[0].getboundingclientrect().width,
comp = window.getcomputedstyle(this.$container[0]),
pdng = parseint(comp['padding-right'], 10);
if (this.$anchor && this.$anchor.length) {
this.anchorheight = this.$anchor[0].getboundingclientrect().height;
} else {
this._parsepoints();
}
this.$element.css({
'max-width': newelemwidth - pdng + 'px'
});
var newcontainerheight = this.$element[0].getboundingclientrect().height || this.containerheight;
if (this.$element.css("display") == "none") {
newcontainerheight = 0;
}
this.containerheight = newcontainerheight;
this.$container.css({
height: newcontainerheight
});
this.elemheight = newcontainerheight;
if (this.isstuck) {
this.$element.css({ "left": this.$container.offset().left + parseint(comp['padding-left'], 10) });
}
this._setbreakpoints(newcontainerheight, function () {
if (cb) {
cb();
}
});
}
/**
* sets the upper and lower breakpoints for the element to become sticky/unsticky.
* @param {number} elemheight - px value for sticky.$element height, calculated by `_setsizes`.
* @param {function} cb - optional callback function to be called on completion.
* @private
*/
}, {
key: '_setbreakpoints',
value: function _setbreakpoints(elemheight, cb) {
if (!this.canstick) {
if (cb) {
cb();
} else {
return false;
}
}
var mtop = emcalc(this.options.margintop),
mbtm = emcalc(this.options.marginbottom),
toppoint = this.points ? this.points[0] : this.$anchor.offset().top,
bottompoint = this.points ? this.points[1] : toppoint + this.anchorheight,
// toppoint = this.$anchor.offset().top || this.points[0],
// bottompoint = toppoint + this.anchorheight || this.points[1],
winheight = window.innerheight;
if (this.options.stickto === 'top') {
toppoint -= mtop;
bottompoint -= elemheight + mtop;
} else if (this.options.stickto === 'bottom') {
toppoint -= winheight - (elemheight + mbtm);
bottompoint -= winheight - mbtm;
} else {
//this would be the stickto: both option... tricky
}
this.toppoint = toppoint;
this.bottompoint = bottompoint;
if (cb) {
cb();
}
}
/**
* destroys the current sticky element.
* resets the element to the top position first.
* removes event listeners, js-added css properties and classes, and unwraps the $element if the js added the $container.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this._removesticky(true);
this.$element.removeclass(this.options.stickyclass + ' is-anchored is-at-top').css({
height: '',
top: '',
bottom: '',
'max-width': ''
}).off('resizeme.zf.trigger');
if (this.$anchor && this.$anchor.length) {
this.$anchor.off('change.zf.sticky');
}
$(window).off(this.scrolllistener);
if (this.waswrapped) {
this.$element.unwrap();
} else {
this.$container.removeclass(this.options.containerclass).css({
height: ''
});
}
foundation.unregisterplugin(this);
}
}]);
return sticky;
}();
sticky.defaults = {
/**
* customizable container template. add your own classes for styling and sizing.
* @option
* @example '<div data-sticky-container class="small-6 columns"></div>'
*/
container: '
',
/**
* location in the view the element sticks to.
* @option
* @example 'top'
*/
stickto: 'top',
/**
* if anchored to a single element, the id of that element.
* @option
* @example 'exampleid'
*/
anchor: '',
/**
* if using more than one element as anchor points, the id of the top anchor.
* @option
* @example 'exampleid:top'
*/
topanchor: '',
/**
* if using more than one element as anchor points, the id of the bottom anchor.
* @option
* @example 'exampleid:bottom'
*/
btmanchor: '',
/**
* margin, in `em`'s to apply to the top of the element when it becomes sticky.
* @option
* @example 1
*/
margintop: 1,
/**
* margin, in `em`'s to apply to the bottom of the element when it becomes sticky.
* @option
* @example 1
*/
marginbottom: 1,
/**
* breakpoint string that is the minimum screen size an element should become sticky.
* @option
* @example 'medium'
*/
stickyon: 'medium',
/**
* class applied to sticky element, and removed on destruction. foundation defaults to `sticky`.
* @option
* @example 'sticky'
*/
stickyclass: 'sticky',
/**
* class applied to sticky container. foundation defaults to `sticky-container`.
* @option
* @example 'sticky-container'
*/
containerclass: 'sticky-container',
/**
* number of scroll events between the plugin's recalculating sticky points. setting it to `0` will cause it to recalc every scroll event, setting it to `-1` will prevent recalc on scroll.
* @option
* @example 50
*/
checkevery: -1
};
/**
* helper function to calculate em values
* @param number {em} - number of em's to calculate into pixels
*/
function emcalc(em) {
return parseint(window.getcomputedstyle(document.body, null).fontsize, 10) * em;
}
// window exports
foundation.plugin(sticky, 'sticky');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* tabs module.
* @module foundation.tabs
* @requires foundation.util.keyboard
* @requires foundation.util.timerandimageloader if tabs contain images
*/
var tabs = function () {
/**
* creates a new instance of tabs.
* @class
* @fires tabs#init
* @param {jquery} element - jquery object to make into tabs.
* @param {object} options - overrides to the default plugin settings.
*/
function tabs(element, options) {
_classcallcheck(this, tabs);
this.$element = element;
this.options = $.extend({}, tabs.defaults, this.$element.data(), options);
this._init();
foundation.registerplugin(this, 'tabs');
foundation.keyboard.register('tabs', {
'enter': 'open',
'space': 'open',
'arrow_right': 'next',
'arrow_up': 'previous',
'arrow_down': 'next',
'arrow_left': 'previous'
// 'tab': 'next',
// 'shift_tab': 'previous'
});
}
/**
* initializes the tabs by showing and focusing (if autofocus=true) the preset active tab.
* @private
*/
_createclass(tabs, [{
key: '_init',
value: function _init() {
var _this = this;
this.$tabtitles = this.$element.find('.' + this.options.linkclass);
this.$tabcontent = $('[data-tabs-content="' + this.$element[0].id + '"]');
this.$tabtitles.each(function () {
var $elem = $(this),
$link = $elem.find('a'),
isactive = $elem.hasclass('is-active'),
hash = $link[0].hash.slice(1),
linkid = $link[0].id ? $link[0].id : hash + '-label',
$tabcontent = $('#' + hash);
$elem.attr({ 'role': 'presentation' });
$link.attr({
'role': 'tab',
'aria-controls': hash,
'aria-selected': isactive,
'id': linkid
});
$tabcontent.attr({
'role': 'tabpanel',
'aria-hidden': !isactive,
'aria-labelledby': linkid
});
if (isactive && _this.options.autofocus) {
$link.focus();
}
});
if (this.options.matchheight) {
var $images = this.$tabcontent.find('img');
if ($images.length) {
foundation.onimagesloaded($images, this._setheight.bind(this));
} else {
this._setheight();
}
}
this._events();
}
/**
* adds event handlers for items within the tabs.
* @private
*/
}, {
key: '_events',
value: function _events() {
this._addkeyhandler();
this._addclickhandler();
this._setheightmqhandler = null;
if (this.options.matchheight) {
this._setheightmqhandler = this._setheight.bind(this);
$(window).on('changed.zf.mediaquery', this._setheightmqhandler);
}
}
/**
* adds click handlers for items within the tabs.
* @private
*/
}, {
key: '_addclickhandler',
value: function _addclickhandler() {
var _this = this;
this.$element.off('click.zf.tabs').on('click.zf.tabs', '.' + this.options.linkclass, function (e) {
e.preventdefault();
e.stoppropagation();
if ($(this).hasclass('is-active')) {
return;
}
_this._handletabchange($(this));
});
}
/**
* adds keyboard event handlers for items within the tabs.
* @private
*/
}, {
key: '_addkeyhandler',
value: function _addkeyhandler() {
var _this = this;
var $firsttab = _this.$element.find('li:first-of-type');
var $lasttab = _this.$element.find('li:last-of-type');
this.$tabtitles.off('keydown.zf.tabs').on('keydown.zf.tabs', function (e) {
if (e.which === 9) return;
var $element = $(this),
$elements = $element.parent('ul').children('li'),
$prevelement,
$nextelement;
$elements.each(function (i) {
if ($(this).is($element)) {
if (_this.options.wraponkeys) {
$prevelement = i === 0 ? $elements.last() : $elements.eq(i - 1);
$nextelement = i === $elements.length - 1 ? $elements.first() : $elements.eq(i + 1);
} else {
$prevelement = $elements.eq(math.max(0, i - 1));
$nextelement = $elements.eq(math.min(i + 1, $elements.length - 1));
}
return;
}
});
// handle keyboard event with keyboard util
foundation.keyboard.handlekey(e, 'tabs', {
open: function () {
$element.find('[role="tab"]').focus();
_this._handletabchange($element);
},
previous: function () {
$prevelement.find('[role="tab"]').focus();
_this._handletabchange($prevelement);
},
next: function () {
$nextelement.find('[role="tab"]').focus();
_this._handletabchange($nextelement);
},
handled: function () {
e.stoppropagation();
e.preventdefault();
}
});
});
}
/**
* opens the tab `$targetcontent` defined by `$target`.
* @param {jquery} $target - tab to open.
* @fires tabs#change
* @function
*/
}, {
key: '_handletabchange',
value: function _handletabchange($target) {
var $tablink = $target.find('[role="tab"]'),
hash = $tablink[0].hash,
$targetcontent = this.$tabcontent.find(hash),
$oldtab = this.$element.find('.' + this.options.linkclass + '.is-active').removeclass('is-active').find('[role="tab"]').attr({ 'aria-selected': 'false' });
$('#' + $oldtab.attr('aria-controls')).removeclass('is-active').attr({ 'aria-hidden': 'true' });
$target.addclass('is-active');
$tablink.attr({ 'aria-selected': 'true' });
$targetcontent.addclass('is-active').attr({ 'aria-hidden': 'false' });
/**
* fires when the plugin has successfully changed tabs.
* @event tabs#change
*/
this.$element.trigger('change.zf.tabs', [$target]);
}
/**
* public method for selecting a content pane to display.
* @param {jquery | string} elem - jquery object or string of the id of the pane to display.
* @function
*/
}, {
key: 'selecttab',
value: function selecttab(elem) {
var idstr;
if (typeof elem === 'object') {
idstr = elem[0].id;
} else {
idstr = elem;
}
if (idstr.indexof('#') < 0) {
idstr = '#' + idstr;
}
var $target = this.$tabtitles.find('[href="' + idstr + '"]').parent('.' + this.options.linkclass);
this._handletabchange($target);
}
}, {
key: '_setheight',
/**
* sets the height of each panel to the height of the tallest panel.
* if enabled in options, gets called on media query change.
* if loading content via external source, can be called directly or with _reflow.
* @function
* @private
*/
value: function _setheight() {
var max = 0;
this.$tabcontent.find('.' + this.options.panelclass).css('height', '').each(function () {
var panel = $(this),
isactive = panel.hasclass('is-active');
if (!isactive) {
panel.css({ 'visibility': 'hidden', 'display': 'block' });
}
var temp = this.getboundingclientrect().height;
if (!isactive) {
panel.css({
'visibility': '',
'display': ''
});
}
max = temp > max ? temp : max;
}).css('height', max + 'px');
}
/**
* destroys an instance of an tabs.
* @fires tabs#destroyed
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.find('.' + this.options.linkclass).off('.zf.tabs').hide().end().find('.' + this.options.panelclass).hide();
if (this.options.matchheight) {
if (this._setheightmqhandler != null) {
$(window).off('changed.zf.mediaquery', this._setheightmqhandler);
}
}
foundation.unregisterplugin(this);
}
}]);
return tabs;
}();
tabs.defaults = {
/**
* allows the window to scroll to content of active pane on load if set to true.
* @option
* @example false
*/
autofocus: false,
/**
* allows keyboard input to 'wrap' around the tab links.
* @option
* @example true
*/
wraponkeys: true,
/**
* allows the tab content panes to match heights if set to true.
* @option
* @example false
*/
matchheight: false,
/**
* class applied to `li`'s in tab link list.
* @option
* @example 'tabs-title'
*/
linkclass: 'tabs-title',
/**
* class applied to the content containers.
* @option
* @example 'tabs-panel'
*/
panelclass: 'tabs-panel'
};
function checkclass($elem) {
return $elem.hasclass('is-active');
}
// window exports
foundation.plugin(tabs, 'tabs');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* toggler module.
* @module foundation.toggler
* @requires foundation.util.motion
* @requires foundation.util.triggers
*/
var toggler = function () {
/**
* creates a new instance of toggler.
* @class
* @fires toggler#init
* @param {object} element - jquery object to add the trigger to.
* @param {object} options - overrides to the default plugin settings.
*/
function toggler(element, options) {
_classcallcheck(this, toggler);
this.$element = element;
this.options = $.extend({}, toggler.defaults, element.data(), options);
this.classname = '';
this._init();
this._events();
foundation.registerplugin(this, 'toggler');
}
/**
* initializes the toggler plugin by parsing the toggle class from data-toggler, or animation classes from data-animate.
* @function
* @private
*/
_createclass(toggler, [{
key: '_init',
value: function _init() {
var input;
// parse animation classes if they were set
if (this.options.animate) {
input = this.options.animate.split(' ');
this.animationin = input[0];
this.animationout = input[1] || null;
}
// otherwise, parse toggle class
else {
input = this.$element.data('toggler');
// allow for a . at the beginning of the string
this.classname = input[0] === '.' ? input.slice(1) : input;
}
// add aria attributes to triggers
var id = this.$element[0].id;
$('[data-open="' + id + '"], [data-close="' + id + '"], [data-toggle="' + id + '"]').attr('aria-controls', id);
// if the target is hidden, add aria-hidden
this.$element.attr('aria-expanded', this.$element.is(':hidden') ? false : true);
}
/**
* initializes events for the toggle trigger.
* @function
* @private
*/
}, {
key: '_events',
value: function _events() {
this.$element.off('toggle.zf.trigger').on('toggle.zf.trigger', this.toggle.bind(this));
}
/**
* toggles the target class on the target element. an event is fired from the original trigger depending on if the resultant state was "on" or "off".
* @function
* @fires toggler#on
* @fires toggler#off
*/
}, {
key: 'toggle',
value: function toggle() {
this[this.options.animate ? '_toggleanimate' : '_toggleclass']();
}
}, {
key: '_toggleclass',
value: function _toggleclass() {
this.$element.toggleclass(this.classname);
var ison = this.$element.hasclass(this.classname);
if (ison) {
/**
* fires if the target element has the class after a toggle.
* @event toggler#on
*/
this.$element.trigger('on.zf.toggler');
} else {
/**
* fires if the target element does not have the class after a toggle.
* @event toggler#off
*/
this.$element.trigger('off.zf.toggler');
}
this._updatearia(ison);
}
}, {
key: '_toggleanimate',
value: function _toggleanimate() {
var _this = this;
if (this.$element.is(':hidden')) {
foundation.motion.animatein(this.$element, this.animationin, function () {
_this._updatearia(true);
this.trigger('on.zf.toggler');
});
} else {
foundation.motion.animateout(this.$element, this.animationout, function () {
_this._updatearia(false);
this.trigger('off.zf.toggler');
});
}
}
}, {
key: '_updatearia',
value: function _updatearia(ison) {
this.$element.attr('aria-expanded', ison ? true : false);
}
/**
* destroys the instance of toggler on the element.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.off('.zf.toggler');
foundation.unregisterplugin(this);
}
}]);
return toggler;
}();
toggler.defaults = {
/**
* tells the plugin if the element should animated when toggled.
* @option
* @example false
*/
animate: false
};
// window exports
foundation.plugin(toggler, 'toggler');
}(jquery);
'use strict';
var _createclass = function () { function defineproperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; object.defineproperty(target, descriptor.key, descriptor); } } return function (constructor, protoprops, staticprops) { if (protoprops) defineproperties(constructor.prototype, protoprops); if (staticprops) defineproperties(constructor, staticprops); return constructor; }; }();
function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } }
!function ($) {
/**
* tooltip module.
* @module foundation.tooltip
* @requires foundation.util.box
* @requires foundation.util.triggers
*/
var tooltip = function () {
/**
* creates a new instance of a tooltip.
* @class
* @fires tooltip#init
* @param {jquery} element - jquery object to attach a tooltip to.
* @param {object} options - object to extend the default configuration.
*/
function tooltip(element, options) {
_classcallcheck(this, tooltip);
this.$element = element;
this.options = $.extend({}, tooltip.defaults, this.$element.data(), options);
this.isactive = false;
this.isclick = false;
this._init();
foundation.registerplugin(this, 'tooltip');
}
/**
* initializes the tooltip by setting the creating the tip element, adding it's text, setting private variables and setting attributes on the anchor.
* @private
*/
_createclass(tooltip, [{
key: '_init',
value: function _init() {
var elemid = this.$element.attr('aria-describedby') || foundation.getyodigits(6, 'tooltip');
this.options.positionclass = this.options.positionclass || this._getpositionclass(this.$element);
this.options.tiptext = this.options.tiptext || this.$element.attr('title');
this.template = this.options.template ? $(this.options.template) : this._buildtemplate(elemid);
this.template.appendto(document.body).text(this.options.tiptext).hide();
this.$element.attr({
'title': '',
'aria-describedby': elemid,
'data-yeti-box': elemid,
'data-toggle': elemid,
'data-resize': elemid
}).addclass(this.triggerclass);
//helper variables to track movement on collisions
this.usedpositions = [];
this.counter = 4;
this.classchanged = false;
this._events();
}
/**
* grabs the current positioning class, if present, and returns the value or an empty string.
* @private
*/
}, {
key: '_getpositionclass',
value: function _getpositionclass(element) {
if (!element) {
return '';
}
// var position = element.attr('class').match(/top|left|right/g);
var position = element[0].classname.match(/\b(top|left|right)\b/g);
position = position ? position[0] : '';
return position;
}
}, {
key: '_buildtemplate',
/**
* builds the tooltip element, adds attributes, and returns the template.
* @private
*/
value: function _buildtemplate(id) {
var templateclasses = (this.options.tooltipclass + ' ' + this.options.positionclass + ' ' + this.options.templateclasses).trim();
var $template = $('
').addclass(templateclasses).attr({
'role': 'tooltip',
'aria-hidden': true,
'data-is-active': false,
'data-is-focus': false,
'id': id
});
return $template;
}
/**
* function that gets called if a collision event is detected.
* @param {string} position - positioning class to try
* @private
*/
}, {
key: '_reposition',
value: function _reposition(position) {
this.usedpositions.push(position ? position : 'bottom');
//default, try switching to opposite side
if (!position && this.usedpositions.indexof('top') < 0) {
this.template.addclass('top');
} else if (position === 'top' && this.usedpositions.indexof('bottom') < 0) {
this.template.removeclass(position);
} else if (position === 'left' && this.usedpositions.indexof('right') < 0) {
this.template.removeclass(position).addclass('right');
} else if (position === 'right' && this.usedpositions.indexof('left') < 0) {
this.template.removeclass(position).addclass('left');
}
//if default change didn't work, try bottom or left first
else if (!position && this.usedpositions.indexof('top') > -1 && this.usedpositions.indexof('left') < 0) {
this.template.addclass('left');
} else if (position === 'top' && this.usedpositions.indexof('bottom') > -1 && this.usedpositions.indexof('left') < 0) {
this.template.removeclass(position).addclass('left');
} else if (position === 'left' && this.usedpositions.indexof('right') > -1 && this.usedpositions.indexof('bottom') < 0) {
this.template.removeclass(position);
} else if (position === 'right' && this.usedpositions.indexof('left') > -1 && this.usedpositions.indexof('bottom') < 0) {
this.template.removeclass(position);
}
//if nothing cleared, set to bottom
else {
this.template.removeclass(position);
}
this.classchanged = true;
this.counter--;
}
/**
* sets the position class of an element and recursively calls itself until there are no more possible positions to attempt, or the tooltip element is no longer colliding.
* if the tooltip is larger than the screen width, default to full width - any user selected margin
* @private
*/
}, {
key: '_setposition',
value: function _setposition() {
var position = this._getpositionclass(this.template),
$tipdims = foundation.box.getdimensions(this.template),
$anchordims = foundation.box.getdimensions(this.$element),
direction = position === 'left' ? 'left' : position === 'right' ? 'left' : 'top',
param = direction === 'top' ? 'height' : 'width',
offset = param === 'height' ? this.options.voffset : this.options.hoffset,
_this = this;
if ($tipdims.width >= $tipdims.windowdims.width || !this.counter && !foundation.box.imnottouchingyou(this.template)) {
this.template.offset(foundation.box.getoffsets(this.template, this.$element, 'center bottom', this.options.voffset, this.options.hoffset, true)).css({
// this.$element.offset(foundation.getoffsets(this.template, this.$element, 'center bottom', this.options.voffset, this.options.hoffset, true)).css({
'width': $anchordims.windowdims.width - this.options.hoffset * 2,
'height': 'auto'
});
return false;
}
this.template.offset(foundation.box.getoffsets(this.template, this.$element, 'center ' + (position || 'bottom'), this.options.voffset, this.options.hoffset));
while (!foundation.box.imnottouchingyou(this.template) && this.counter) {
this._reposition(position);
this._setposition();
}
}
/**
* reveals the tooltip, and fires an event to close any other open tooltips on the page
* @fires tooltip#closeme
* @fires tooltip#show
* @function
*/
}, {
key: 'show',
value: function show() {
if (this.options.showon !== 'all' && !foundation.mediaquery.atleast(this.options.showon)) {
// console.error('the screen is too small to display this tooltip');
return false;
}
var _this = this;
this.template.css('visibility', 'hidden').show();
this._setposition();
/**
* fires to close all other open tooltips on the page
* @event closeme#tooltip
*/
this.$element.trigger('closeme.zf.tooltip', this.template.attr('id'));
this.template.attr({
'data-is-active': true,
'aria-hidden': false
});
_this.isactive = true;
// console.log(this.template);
this.template.stop().hide().css('visibility', '').fadein(this.options.fadeinduration, function () {
//maybe do stuff?
});
/**
* fires when the tooltip is shown
* @event tooltip#show
*/
this.$element.trigger('show.zf.tooltip');
}
/**
* hides the current tooltip, and resets the positioning class if it was changed due to collision
* @fires tooltip#hide
* @function
*/
}, {
key: 'hide',
value: function hide() {
// console.log('hiding', this.$element.data('yeti-box'));
var _this = this;
this.template.stop().attr({
'aria-hidden': true,
'data-is-active': false
}).fadeout(this.options.fadeoutduration, function () {
_this.isactive = false;
_this.isclick = false;
if (_this.classchanged) {
_this.template.removeclass(_this._getpositionclass(_this.template)).addclass(_this.options.positionclass);
_this.usedpositions = [];
_this.counter = 4;
_this.classchanged = false;
}
});
/**
* fires when the tooltip is hidden
* @event tooltip#hide
*/
this.$element.trigger('hide.zf.tooltip');
}
/**
* adds event listeners for the tooltip and its anchor
* todo combine some of the listeners like focus and mouseenter, etc.
* @private
*/
}, {
key: '_events',
value: function _events() {
var _this = this;
var $template = this.template;
var isfocus = false;
if (!this.options.disablehover) {
this.$element.on('mouseenter.zf.tooltip', function (e) {
if (!_this.isactive) {
_this.timeout = settimeout(function () {
_this.show();
}, _this.options.hoverdelay);
}
}).on('mouseleave.zf.tooltip', function (e) {
cleartimeout(_this.timeout);
if (!isfocus || _this.isclick && !_this.options.clickopen) {
_this.hide();
}
});
}
if (this.options.clickopen) {
this.$element.on('mousedown.zf.tooltip', function (e) {
e.stopimmediatepropagation();
if (_this.isclick) {
//_this.hide();
// _this.isclick = false;
} else {
_this.isclick = true;
if ((_this.options.disablehover || !_this.$element.attr('tabindex')) && !_this.isactive) {
_this.show();
}
}
});
} else {
this.$element.on('mousedown.zf.tooltip', function (e) {
e.stopimmediatepropagation();
_this.isclick = true;
});
}
if (!this.options.disablefortouch) {
this.$element.on('tap.zf.tooltip touchend.zf.tooltip', function (e) {
_this.isactive ? _this.hide() : _this.show();
});
}
this.$element.on({
// 'toggle.zf.trigger': this.toggle.bind(this),
// 'close.zf.trigger': this.hide.bind(this)
'close.zf.trigger': this.hide.bind(this)
});
this.$element.on('focus.zf.tooltip', function (e) {
isfocus = true;
if (_this.isclick) {
// if we're not showing open on clicks, we need to pretend a click-launched focus isn't
// a real focus, otherwise on hover and come back we get bad behavior
if (!_this.options.clickopen) {
isfocus = false;
}
return false;
} else {
_this.show();
}
}).on('focusout.zf.tooltip', function (e) {
isfocus = false;
_this.isclick = false;
_this.hide();
}).on('resizeme.zf.trigger', function () {
if (_this.isactive) {
_this._setposition();
}
});
}
/**
* adds a toggle method, in addition to the static show() & hide() functions
* @function
*/
}, {
key: 'toggle',
value: function toggle() {
if (this.isactive) {
this.hide();
} else {
this.show();
}
}
/**
* destroys an instance of tooltip, removes template element from the view.
* @function
*/
}, {
key: 'destroy',
value: function destroy() {
this.$element.attr('title', this.template.text()).off('.zf.trigger .zf.tootip')
// .removeclass('has-tip')
.removeattr('aria-describedby').removeattr('data-yeti-box').removeattr('data-toggle').removeattr('data-resize');
this.template.remove();
foundation.unregisterplugin(this);
}
}]);
return tooltip;
}();
tooltip.defaults = {
disablefortouch: false,
/**
* time, in ms, before a tooltip should open on hover.
* @option
* @example 200
*/
hoverdelay: 200,
/**
* time, in ms, a tooltip should take to fade into view.
* @option
* @example 150
*/
fadeinduration: 150,
/**
* time, in ms, a tooltip should take to fade out of view.
* @option
* @example 150
*/
fadeoutduration: 150,
/**
* disables hover events from opening the tooltip if set to true
* @option
* @example false
*/
disablehover: false,
/**
* optional addtional classes to apply to the tooltip template on init.
* @option
* @example 'my-cool-tip-class'
*/
templateclasses: '',
/**
* non-optional class added to tooltip templates. foundation default is 'tooltip'.
* @option
* @example 'tooltip'
*/
tooltipclass: 'tooltip',
/**
* class applied to the tooltip anchor element.
* @option
* @example 'has-tip'
*/
triggerclass: 'has-tip',
/**
* minimum breakpoint size at which to open the tooltip.
* @option
* @example 'small'
*/
showon: 'small',
/**
* custom template to be used to generate markup for tooltip.
* @option
* @example '<div class="tooltip"></div>'
*/
template: '',
/**
* text displayed in the tooltip template on open.
* @option
* @example 'some cool space fact here.'
*/
tiptext: '',
touchclosetext: 'tap to close.',
/**
* allows the tooltip to remain open if triggered with a click or touch event.
* @option
* @example true
*/
clickopen: true,
/**
* additional positioning classes, set by the js
* @option
* @example 'top'
*/
positionclass: '',
/**
* distance, in pixels, the template should push away from the anchor on the y axis.
* @option
* @example 10
*/
voffset: 10,
/**
* distance, in pixels, the template should push away from the anchor on the x axis, if aligned to a side.
* @option
* @example 12
*/
hoffset: 12
};
/**
* todo utilize resize event trigger
*/
// window exports
foundation.plugin(tooltip, 'tooltip');
}(jquery);
$(document).foundation();