2 * Foundation Responsive Library
3 * http://foundation.zurb.com
5 * Free to use under the MIT license.
6 * http://www.opensource.org/licenses/mit-license.php
9 (function ($, window, document, undefined) {
12 var header_helpers = function (class_array) {
13 var i = class_array.length;
17 if (head.has('.' + class_array[i]).length === 0) {
18 head.append('<meta class="' + class_array[i] + '" />');
24 'foundation-mq-small',
25 'foundation-mq-small-only',
26 'foundation-mq-medium',
27 'foundation-mq-medium-only',
28 'foundation-mq-large',
29 'foundation-mq-large-only',
30 'foundation-mq-xlarge',
31 'foundation-mq-xlarge-only',
32 'foundation-mq-xxlarge',
33 'foundation-data-attribute-namespace']);
35 // Enable FastClick if present
38 if (typeof FastClick !== 'undefined') {
39 // Don't attach to body if undefined
40 if (typeof document.body !== 'undefined') {
41 FastClick.attach(document.body);
46 // private Fast Selector wrapper,
47 // returns jQuery object. Only use where
48 // getElementById is not available.
49 var S = function (selector, context) {
50 if (typeof selector === 'string') {
61 return $(cont.querySelectorAll(selector));
64 return $(document.querySelectorAll(selector));
67 return $(selector, context);
70 // Namespace functions.
72 var attr_name = function (init) {
77 if (this.namespace.length > 0) {
78 arr.push(this.namespace);
85 var add_namespace = function (str) {
86 var parts = str.split('-'),
94 if (this.namespace.length > 0) {
95 arr.push(this.namespace, parts[i]);
102 return arr.reverse().join('-');
105 // Event binding and data-options updating.
107 var bindings = function (method, options) {
111 should_bind_events = !$this.data(self.attr_name(true) + '-init');
112 $this.data(self.attr_name(true) + '-init', $.extend({}, self.settings, (options || method), self.data_options($this)));
114 if (should_bind_events) {
119 if (S(this.scope).is('[' + this.attr_name() +']')) {
120 bind.call(this.scope);
122 S('[' + this.attr_name() +']', this.scope).each(bind);
124 // # Patch to fix #5043 to move this *after* the if/else clause in order for Backbone and similar frameworks to have improved control over event binding and data-options updating.
125 if (typeof method === 'string') {
126 return this[method].call(this, options);
131 var single_image_loaded = function (image, callback) {
136 function bindLoad () {
137 this.one('load', loaded);
139 if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
140 var src = this.attr( 'src' ),
141 param = src.match( /\?/ ) ? '&' : '?';
143 param += 'random=' + (new Date()).getTime();
144 this.attr('src', src + param);
148 if (!image.attr('src')) {
153 if (image[0].complete || image[0].readyState === 4) {
156 bindLoad.call(image);
161 https://github.com/paulirish/matchMedia.js
164 window.matchMedia = window.matchMedia || (function ( doc ) {
169 docElem = doc.documentElement,
170 refNode = docElem.firstElementChild || docElem.firstChild,
171 // fakeBody required for <FF4 when executed in <head>
172 fakeBody = doc.createElement( 'body' ),
173 div = doc.createElement( 'div' );
175 div.id = 'mq-test-1';
176 div.style.cssText = 'position:absolute;top:-100em';
177 fakeBody.style.background = 'none';
178 fakeBody.appendChild(div);
180 return function (q) {
182 div.innerHTML = '­<style media="' + q + '"> #mq-test-1 { width: 42px; }</style>';
184 docElem.insertBefore( fakeBody, refNode );
185 bool = div.offsetWidth === 42;
186 docElem.removeChild( fakeBody );
198 * jquery.requestAnimationFrame
199 * https://github.com/gnarf37/jquery-requestAnimationFrame
200 * Requires jQuery 1.8+
202 * Copyright (c) 2012 Corey Frang
203 * Licensed under the MIT license.
209 // requestAnimationFrame polyfill adapted from Erik Möller
210 // fixes from Paul Irish and Tino Zijdel
211 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
212 // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
216 vendors = ['webkit', 'moz'],
217 requestAnimationFrame = window.requestAnimationFrame,
218 cancelAnimationFrame = window.cancelAnimationFrame,
219 jqueryFxAvailable = 'undefined' !== typeof jQuery.fx;
221 for (; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
222 requestAnimationFrame = window[ vendors[lastTime] + 'RequestAnimationFrame' ];
223 cancelAnimationFrame = cancelAnimationFrame ||
224 window[ vendors[lastTime] + 'CancelAnimationFrame' ] ||
225 window[ vendors[lastTime] + 'CancelRequestAnimationFrame' ];
230 requestAnimationFrame(raf);
232 if (jqueryFxAvailable) {
238 if (requestAnimationFrame) {
240 window.requestAnimationFrame = requestAnimationFrame;
241 window.cancelAnimationFrame = cancelAnimationFrame;
243 if (jqueryFxAvailable) {
244 jQuery.fx.timer = function (timer) {
245 if (timer() && jQuery.timers.push(timer) && !animating) {
251 jQuery.fx.stop = function () {
257 window.requestAnimationFrame = function (callback) {
258 var currTime = new Date().getTime(),
259 timeToCall = Math.max(0, 16 - (currTime - lastTime)),
260 id = window.setTimeout(function () {
261 callback(currTime + timeToCall);
263 lastTime = currTime + timeToCall;
267 window.cancelAnimationFrame = function (id) {
275 function removeQuotes (string) {
276 if (typeof string === 'string' || string instanceof String) {
277 string = string.replace(/^['\\/"]+|(;\s?})+|['\\/"]+$/g, '');
283 window.Foundation = {
289 'small' : S('.foundation-mq-small').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
290 'small-only' : S('.foundation-mq-small-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
291 'medium' : S('.foundation-mq-medium').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
292 'medium-only' : S('.foundation-mq-medium-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
293 'large' : S('.foundation-mq-large').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
294 'large-only' : S('.foundation-mq-large-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
295 'xlarge' : S('.foundation-mq-xlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
296 'xlarge-only' : S('.foundation-mq-xlarge-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
297 'xxlarge' : S('.foundation-mq-xxlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, '')
300 stylesheet : $('<style></style>').appendTo('head')[0].sheet,
303 namespace : undefined
306 init : function (scope, libraries, method, options, response) {
307 var args = [scope, method, options, response],
311 this.rtl = /rtl/i.test(S('html').attr('dir'));
313 // set foundation global scope
314 this.scope = scope || this.scope;
316 this.set_namespace();
318 if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) {
319 if (this.libs.hasOwnProperty(libraries)) {
320 responses.push(this.init_lib(libraries, args));
323 for (var lib in this.libs) {
324 responses.push(this.init_lib(lib, libraries));
328 S(window).load(function () {
330 .trigger('resize.fndtn.clearing')
331 .trigger('resize.fndtn.dropdown')
332 .trigger('resize.fndtn.equalizer')
333 .trigger('resize.fndtn.interchange')
334 .trigger('resize.fndtn.joyride')
335 .trigger('resize.fndtn.magellan')
336 .trigger('resize.fndtn.topbar')
337 .trigger('resize.fndtn.slider');
343 init_lib : function (lib, args) {
344 if (this.libs.hasOwnProperty(lib)) {
345 this.patch(this.libs[lib]);
347 if (args && args.hasOwnProperty(lib)) {
348 if (typeof this.libs[lib].settings !== 'undefined') {
349 $.extend(true, this.libs[lib].settings, args[lib]);
350 } else if (typeof this.libs[lib].defaults !== 'undefined') {
351 $.extend(true, this.libs[lib].defaults, args[lib]);
353 return this.libs[lib].init.apply(this.libs[lib], [this.scope, args[lib]]);
356 args = args instanceof Array ? args : new Array(args);
357 return this.libs[lib].init.apply(this.libs[lib], args);
360 return function () {};
363 patch : function (lib) {
364 lib.scope = this.scope;
365 lib.namespace = this.global.namespace;
367 lib['data_options'] = this.utils.data_options;
368 lib['attr_name'] = attr_name;
369 lib['add_namespace'] = add_namespace;
370 lib['bindings'] = bindings;
371 lib['S'] = this.utils.S;
374 inherit : function (scope, methods) {
375 var methods_arr = methods.split(' '),
376 i = methods_arr.length;
379 if (this.utils.hasOwnProperty(methods_arr[i])) {
380 scope[methods_arr[i]] = this.utils[methods_arr[i]];
385 set_namespace : function () {
388 // Don't bother reading the namespace out of the meta tag
389 // if the namespace has been set globally in javascript
392 // Foundation.global.namespace = 'my-namespace';
393 // or make it an empty string:
394 // Foundation.global.namespace = '';
398 // If the namespace has not been set (is undefined), try to read it out of the meta element.
399 // Otherwise use the globally defined namespace, even if it's empty ('')
400 var namespace = ( this.global.namespace === undefined ) ? $('.foundation-data-attribute-namespace').css('font-family') : this.global.namespace;
402 // Finally, if the namsepace is either undefined or false, set it to an empty string.
403 // Otherwise use the namespace value.
404 this.global.namespace = ( namespace === undefined || /false/i.test(namespace) ) ? '' : namespace;
409 // methods that can be inherited in libraries
413 // Fast Selector wrapper returns jQuery object. Only use where getElementById
417 // Selector (String): CSS selector describing the element(s) to be
418 // returned as a jQuery object.
420 // Scope (String): CSS selector describing the area to be searched. Default
424 // Element (jQuery Object): jQuery object containing elements matching the
425 // selector within the scope.
429 // Executes a function a max of once every n milliseconds
432 // Func (Function): Function to be throttled.
434 // Delay (Integer): Function execution threshold in milliseconds.
437 // Lazy_function (Function): Function with throttling applied.
438 throttle : function (func, delay) {
442 var context = this, args = arguments;
445 timer = setTimeout(function () {
446 func.apply(context, args);
454 // Executes a function when it stops being invoked for n seconds
455 // Modified version of _.debounce() http://underscorejs.org
458 // Func (Function): Function to be debounced.
460 // Delay (Integer): Function execution threshold in milliseconds.
462 // Immediate (Bool): Whether the function should be called at the beginning
463 // of the delay instead of the end. Default is false.
466 // Lazy_function (Function): Function with debouncing applied.
467 debounce : function (func, delay, immediate) {
470 var context = this, args = arguments;
471 var later = function () {
474 result = func.apply(context, args);
477 var callNow = immediate && !timeout;
478 clearTimeout(timeout);
479 timeout = setTimeout(later, delay);
481 result = func.apply(context, args);
488 // Parses data-options attribute
491 // El (jQuery Object): Element to be parsed.
494 // Options (Javascript Object): Contents of the element's data-options
496 data_options : function (el, data_attr_name) {
497 data_attr_name = data_attr_name || 'options';
498 var opts = {}, ii, p, opts_arr,
499 data_options = function (el) {
500 var namespace = Foundation.global.namespace;
502 if (namespace.length > 0) {
503 return el.data(namespace + '-' + data_attr_name);
506 return el.data(data_attr_name);
509 var cached_options = data_options(el);
511 if (typeof cached_options === 'object') {
512 return cached_options;
515 opts_arr = (cached_options || ':').split(';');
516 ii = opts_arr.length;
518 function isNumber (o) {
519 return !isNaN (o - 0) && o !== null && o !== '' && o !== false && o !== true;
522 function trim (str) {
523 if (typeof str === 'string') {
530 p = opts_arr[ii].split(':');
531 p = [p[0], p.slice(1).join(':')];
533 if (/true/i.test(p[1])) {
536 if (/false/i.test(p[1])) {
539 if (isNumber(p[1])) {
540 if (p[1].indexOf('.') === -1) {
541 p[1] = parseInt(p[1], 10);
543 p[1] = parseFloat(p[1]);
547 if (p.length === 2 && p[0].length > 0) {
548 opts[trim(p[0])] = trim(p[1]);
556 // Adds JS-recognizable media queries
559 // Media (String): Key string for the media query to be stored as in
560 // Foundation.media_queries
562 // Class (String): Class name for the generated <meta> tag
563 register_media : function (media, media_class) {
564 if (Foundation.media_queries[media] === undefined) {
565 $('head').append('<meta class="' + media_class + '"/>');
566 Foundation.media_queries[media] = removeQuotes($('.' + media_class).css('font-family'));
571 // Add custom CSS within a JS-defined media query
574 // Rule (String): CSS rule to be appended to the document.
576 // Media (String): Optional media query string for the CSS rule to be
578 add_custom_rule : function (rule, media) {
579 if (media === undefined && Foundation.stylesheet) {
580 Foundation.stylesheet.insertRule(rule, Foundation.stylesheet.cssRules.length);
582 var query = Foundation.media_queries[media];
584 if (query !== undefined) {
585 Foundation.stylesheet.insertRule('@media ' +
586 Foundation.media_queries[media] + '{ ' + rule + ' }');
592 // Performs a callback function when an image is fully loaded
595 // Image (jQuery Object): Image(s) to check if loaded.
597 // Callback (Function): Function to execute when image is fully loaded.
598 image_loaded : function (images, callback) {
600 unloaded = images.length;
602 if (unloaded === 0) {
606 images.each(function () {
607 single_image_loaded(self.S(this), function () {
609 if (unloaded === 0) {
617 // Returns a random, alphanumeric string
620 // Length (Integer): Length of string to be generated. Defaults to random
624 // Rand (String): Pseudo-random, alphanumeric string.
625 random_str : function () {
629 this.prefix = this.prefix || [(this.name || 'F'), (+new Date).toString(36)].join('-');
631 return this.prefix + (this.fidx++).toString(36);
635 // Helper for window.matchMedia
638 // mq (String): Media query
641 // (Boolean): Whether the media query passes or not
642 match : function (mq) {
643 return window.matchMedia(mq).matches;
647 // Helpers for checking Foundation default media queries with JS
650 // (Boolean): Whether the media query passes or not
652 is_small_up : function () {
653 return this.match(Foundation.media_queries.small);
656 is_medium_up : function () {
657 return this.match(Foundation.media_queries.medium);
660 is_large_up : function () {
661 return this.match(Foundation.media_queries.large);
664 is_xlarge_up : function () {
665 return this.match(Foundation.media_queries.xlarge);
668 is_xxlarge_up : function () {
669 return this.match(Foundation.media_queries.xxlarge);
672 is_small_only : function () {
673 return !this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
676 is_medium_only : function () {
677 return this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
680 is_large_only : function () {
681 return this.is_medium_up() && this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
684 is_xlarge_only : function () {
685 return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && !this.is_xxlarge_up();
688 is_xxlarge_only : function () {
689 return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && this.is_xxlarge_up();
694 $.fn.foundation = function () {
695 var args = Array.prototype.slice.call(arguments, 0);
697 return this.each(function () {
698 Foundation.init.apply(Foundation, [this].concat(args));
703 }(jQuery, window, window.document));
704 ;(function ($, window, document, undefined) {
707 Foundation.libs.equalizer = {
714 before_height_change : $.noop,
715 after_height_change : $.noop,
716 equalize_on_stack : false
719 init : function (scope, method, options) {
720 Foundation.inherit(this, 'image_loaded');
721 this.bindings(method, options);
725 events : function () {
726 this.S(window).off('.equalizer').on('resize.fndtn.equalizer', function (e) {
731 equalize : function (equalizer) {
732 var isStacked = false,
733 vals = equalizer.find('[' + this.attr_name() + '-watch]:visible'),
734 settings = equalizer.data(this.attr_name(true) + '-init');
736 if (vals.length === 0) {
739 var firstTopOffset = vals.first().offset().top;
740 settings.before_height_change();
741 equalizer.trigger('before-height-change').trigger('before-height-change.fndth.equalizer');
742 vals.height('inherit');
743 vals.each(function () {
745 if (el.offset().top !== firstTopOffset) {
750 if (settings.equalize_on_stack === false) {
756 var heights = vals.map(function () { return $(this).outerHeight(false) }).get();
758 if (settings.use_tallest) {
759 var max = Math.max.apply(null, heights);
760 vals.css('height', max);
762 var min = Math.min.apply(null, heights);
763 vals.css('height', min);
765 settings.after_height_change();
766 equalizer.trigger('after-height-change').trigger('after-height-change.fndtn.equalizer');
769 reflow : function () {
772 this.S('[' + this.attr_name() + ']', this.scope).each(function () {
773 var $eq_target = $(this);
774 self.image_loaded(self.S('img', this), function () {
775 self.equalize($eq_target)
780 })(jQuery, window, window.document);
781 ;(function ($, window, document, undefined) {
784 Foundation.libs.interchange = {
785 name : 'interchange',
791 images_loaded : false,
792 nodes_loaded : false,
795 load_attr : 'interchange',
798 'default' : 'only screen',
799 'small' : Foundation.media_queries['small'],
800 'small-only' : Foundation.media_queries['small-only'],
801 'medium' : Foundation.media_queries['medium'],
802 'medium-only' : Foundation.media_queries['medium-only'],
803 'large' : Foundation.media_queries['large'],
804 'large-only' : Foundation.media_queries['large-only'],
805 'xlarge' : Foundation.media_queries['xlarge'],
806 'xlarge-only' : Foundation.media_queries['xlarge-only'],
807 'xxlarge' : Foundation.media_queries['xxlarge'],
808 'landscape' : 'only screen and (orientation: landscape)',
809 'portrait' : 'only screen and (orientation: portrait)',
810 'retina' : 'only screen and (-webkit-min-device-pixel-ratio: 2),' +
811 'only screen and (min--moz-device-pixel-ratio: 2),' +
812 'only screen and (-o-min-device-pixel-ratio: 2/1),' +
813 'only screen and (min-device-pixel-ratio: 2),' +
814 'only screen and (min-resolution: 192dpi),' +
815 'only screen and (min-resolution: 2dppx)'
819 replace : function (el, path, trigger) {
820 // The trigger argument, if called within the directive, fires
821 // an event named after the directive on the element, passing
822 // any parameters along to the event that you pass to trigger.
824 // ex. trigger(), trigger([a, b, c]), or trigger(a, b, c)
826 // This allows you to bind a callback like so:
827 // $('#interchangeContainer').on('replace', function (e, a, b, c) {
828 // console.log($(this).html(), a, b, c);
831 if (/IMG/.test(el[0].nodeName)) {
832 var orig_path = el[0].src;
834 if (new RegExp(path, 'i').test(orig_path)) {
840 return trigger(el[0].src);
842 var last_path = el.data(this.data_attr + '-last-path'),
845 if (last_path == path) {
849 if (/\.(gif|jpg|jpeg|tiff|png)([?#].*)?/i.test(path)) {
850 $(el).css('background-image', 'url(' + path + ')');
851 el.data('interchange-last-path', path);
852 return trigger(path);
855 return $.get(path, function (response) {
857 el.data(self.data_attr + '-last-path', path);
865 init : function (scope, method, options) {
866 Foundation.inherit(this, 'throttle random_str');
868 this.data_attr = this.set_data_attr();
869 $.extend(true, this.settings, method, options);
870 this.bindings(method, options);
875 get_media_hash : function () {
877 for (var queryName in this.settings.named_queries ) {
878 mediaHash += matchMedia(this.settings.named_queries[queryName]).matches.toString();
883 events : function () {
884 var self = this, prevMediaHash;
888 .on('resize.fndtn.interchange', self.throttle(function () {
889 var currMediaHash = self.get_media_hash();
890 if (currMediaHash !== prevMediaHash) {
893 prevMediaHash = currMediaHash;
899 resize : function () {
900 var cache = this.cache;
902 if (!this.images_loaded || !this.nodes_loaded) {
903 setTimeout($.proxy(this.resize, this), 50);
907 for (var uuid in cache) {
908 if (cache.hasOwnProperty(uuid)) {
909 var passed = this.results(uuid, cache[uuid]);
912 this.settings.directives[passed
913 .scenario[1]].call(this, passed.el, passed.scenario[0], (function (passed) {
914 if (arguments[0] instanceof Array) {
915 var args = arguments[0];
917 var args = Array.prototype.slice.call(arguments, 0);
921 passed.el.trigger(passed.scenario[1], args);
930 results : function (uuid, scenarios) {
931 var count = scenarios.length;
934 var el = this.S('[' + this.add_namespace('data-uuid') + '="' + uuid + '"]');
937 var mq, rule = scenarios[count][2];
938 if (this.settings.named_queries.hasOwnProperty(rule)) {
939 mq = matchMedia(this.settings.named_queries[rule]);
941 mq = matchMedia(rule);
944 return {el : el, scenario : scenarios[count]};
952 load : function (type, force_update) {
953 if (typeof this['cached_' + type] === 'undefined' || force_update) {
954 this['update_' + type]();
957 return this['cached_' + type];
960 update_images : function () {
961 var images = this.S('img[' + this.data_attr + ']'),
962 count = images.length,
965 data_attr = this.data_attr;
968 this.cached_images = [];
969 this.images_loaded = (count === 0);
974 var str = images[i].getAttribute(data_attr) || '';
976 if (str.length > 0) {
977 this.cached_images.push(images[i]);
981 if (loaded_count === count) {
982 this.images_loaded = true;
983 this.enhance('images');
990 update_nodes : function () {
991 var nodes = this.S('[' + this.data_attr + ']').not('img'),
992 count = nodes.length,
995 data_attr = this.data_attr;
997 this.cached_nodes = [];
998 this.nodes_loaded = (count === 0);
1002 var str = nodes[i].getAttribute(data_attr) || '';
1004 if (str.length > 0) {
1005 this.cached_nodes.push(nodes[i]);
1008 if (loaded_count === count) {
1009 this.nodes_loaded = true;
1010 this.enhance('nodes');
1017 enhance : function (type) {
1018 var i = this['cached_' + type].length;
1021 this.object($(this['cached_' + type][i]));
1024 return $(window).trigger('resize').trigger('resize.fndtn.interchange');
1027 convert_directive : function (directive) {
1029 var trimmed = this.trim(directive);
1031 if (trimmed.length > 0) {
1038 parse_scenario : function (scenario) {
1039 // This logic had to be made more complex since some users were using commas in the url path
1040 // So we cannot simply just split on a comma
1041 var directive_match = scenario[0].match(/(.+),\s*(\w+)\s*$/),
1042 media_query = scenario[1];
1044 if (directive_match) {
1045 var path = directive_match[1],
1046 directive = directive_match[2];
1048 var cached_split = scenario[0].split(/,\s*$/),
1049 path = cached_split[0],
1053 return [this.trim(path), this.convert_directive(directive), this.trim(media_query)];
1056 object : function (el) {
1057 var raw_arr = this.parse_data_attr(el),
1063 var split = raw_arr[i].split(/\(([^\)]*?)(\))$/);
1065 if (split.length > 1) {
1066 var params = this.parse_scenario(split);
1067 scenarios.push(params);
1072 return this.store(el, scenarios);
1075 store : function (el, scenarios) {
1076 var uuid = this.random_str(),
1077 current_uuid = el.data(this.add_namespace('uuid', true));
1079 if (this.cache[current_uuid]) {
1080 return this.cache[current_uuid];
1083 el.attr(this.add_namespace('data-uuid'), uuid);
1085 return this.cache[uuid] = scenarios;
1088 trim : function (str) {
1090 if (typeof str === 'string') {
1097 set_data_attr : function (init) {
1099 if (this.namespace.length > 0) {
1100 return this.namespace + '-' + this.settings.load_attr;
1103 return this.settings.load_attr;
1106 if (this.namespace.length > 0) {
1107 return 'data-' + this.namespace + '-' + this.settings.load_attr;
1110 return 'data-' + this.settings.load_attr;
1113 parse_data_attr : function (el) {
1114 var raw = el.attr(this.attr_name()).split(/\[(.*?)\]/),
1119 if (raw[i].replace(/[\W\d]+/, '').length > 4) {
1120 output.push(raw[i]);
1127 reflow : function () {
1128 this.load('images', true);
1129 this.load('nodes', true);
1134 }(jQuery, window, window.document));
1135 ;(function ($, window, document, undefined) {
1138 Foundation.libs.accordion = {
1144 content_class : 'content',
1145 active_class : 'active',
1146 multi_expand : false,
1148 callback : function () {}
1151 init : function (scope, method, options) {
1152 this.bindings(method, options);
1155 events : function () {
1159 .off('.fndtn.accordion')
1160 .on('click.fndtn.accordion', '[' + this.attr_name() + '] > .accordion-navigation > a', function (e) {
1161 var accordion = S(this).closest('[' + self.attr_name() + ']'),
1162 groupSelector = self.attr_name() + '=' + accordion.attr(self.attr_name()),
1163 settings = accordion.data(self.attr_name(true) + '-init') || self.settings,
1164 target = S('#' + this.href.split('#')[1]),
1165 aunts = $('> .accordion-navigation', accordion),
1166 siblings = aunts.children('.' + settings.content_class),
1167 active_content = siblings.filter('.' + settings.active_class);
1171 if (accordion.attr(self.attr_name())) {
1172 siblings = siblings.add('[' + groupSelector + '] dd > ' + '.' + settings.content_class);
1173 aunts = aunts.add('[' + groupSelector + '] .accordion-navigation');
1176 if (settings.toggleable && target.is(active_content)) {
1177 target.parent('.accordion-navigation').toggleClass(settings.active_class, false);
1178 target.toggleClass(settings.active_class, false);
1179 settings.callback(target);
1180 target.triggerHandler('toggled', [accordion]);
1181 accordion.triggerHandler('toggled', [target]);
1185 if (!settings.multi_expand) {
1186 siblings.removeClass(settings.active_class);
1187 aunts.removeClass(settings.active_class);
1190 target.addClass(settings.active_class).parent().addClass(settings.active_class);
1191 settings.callback(target);
1192 target.triggerHandler('toggled', [accordion]);
1193 accordion.triggerHandler('toggled', [target]);
1197 off : function () {},
1199 reflow : function () {}
1201 }(jQuery, window, window.document));
1202 ;(function ($, window, document, undefined) {
1205 Foundation.libs.dropdown = {
1211 active_class : 'open',
1212 disabled_class : 'disabled',
1213 mega_class : 'mega',
1216 hover_timeout : 150,
1217 opened : function () {},
1218 closed : function () {}
1221 init : function (scope, method, options) {
1222 Foundation.inherit(this, 'throttle');
1224 $.extend(true, this.settings, method, options);
1225 this.bindings(method, options);
1228 events : function (scope) {
1234 .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) {
1235 var settings = S(this).data(self.attr_name(true) + '-init') || self.settings;
1236 if (!settings.is_hover || Modernizr.touch) {
1238 if (S(this).parent('[data-reveal-id]')) {
1239 e.stopPropagation();
1241 self.toggle($(this));
1244 .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
1245 var $this = S(this),
1249 clearTimeout(self.timeout);
1251 if ($this.data(self.data_attr())) {
1252 dropdown = S('#' + $this.data(self.data_attr()));
1256 target = S('[' + self.attr_name() + '="' + dropdown.attr('id') + '"]');
1259 var settings = target.data(self.attr_name(true) + '-init') || self.settings;
1261 if (S(e.currentTarget).data(self.data_attr()) && settings.is_hover) {
1262 self.closeall.call(self);
1265 if (settings.is_hover) {
1266 self.open.apply(self, [dropdown, target]);
1269 .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
1270 var $this = S(this);
1273 if ($this.data(self.data_attr())) {
1274 settings = $this.data(self.data_attr(true) + '-init') || self.settings;
1276 var target = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'),
1277 settings = target.data(self.attr_name(true) + '-init') || self.settings;
1280 self.timeout = setTimeout(function () {
1281 if ($this.data(self.data_attr())) {
1282 if (settings.is_hover) {
1283 self.close.call(self, S('#' + $this.data(self.data_attr())));
1286 if (settings.is_hover) {
1287 self.close.call(self, $this);
1290 }.bind(this), settings.hover_timeout);
1292 .on('click.fndtn.dropdown', function (e) {
1293 var parent = S(e.target).closest('[' + self.attr_name() + '-content]');
1294 var links = parent.find('a');
1296 if (links.length > 0 && parent.attr('aria-autoclose') !== 'false') {
1297 self.close.call(self, S('[' + self.attr_name() + '-content]'));
1300 if (e.target !== document && !$.contains(document.documentElement, e.target)) {
1304 if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) {
1308 if (!(S(e.target).data('revealId')) &&
1309 (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') ||
1310 $.contains(parent.first()[0], e.target)))) {
1311 e.stopPropagation();
1315 self.close.call(self, S('[' + self.attr_name() + '-content]'));
1317 .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
1318 self.settings.opened.call(this);
1320 .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
1321 self.settings.closed.call(this);
1326 .on('resize.fndtn.dropdown', self.throttle(function () {
1327 self.resize.call(self);
1333 close : function (dropdown) {
1335 dropdown.each(function () {
1336 var original_target = $('[' + self.attr_name() + '=' + dropdown[0].id + ']') || $('aria-controls=' + dropdown[0].id + ']');
1337 original_target.attr('aria-expanded', 'false');
1338 if (self.S(this).hasClass(self.settings.active_class)) {
1340 .css(Foundation.rtl ? 'right' : 'left', '-99999px')
1341 .attr('aria-hidden', 'true')
1342 .removeClass(self.settings.active_class)
1343 .prev('[' + self.attr_name() + ']')
1344 .removeClass(self.settings.active_class)
1345 .removeData('target');
1347 self.S(this).trigger('closed').trigger('closed.fndtn.dropdown', [dropdown]);
1350 dropdown.removeClass('f-open-' + this.attr_name(true));
1353 closeall : function () {
1355 $.each(self.S('.f-open-' + this.attr_name(true)), function () {
1356 self.close.call(self, self.S(this));
1360 open : function (dropdown, target) {
1363 .addClass(this.settings.active_class), target);
1364 dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class);
1365 dropdown.data('target', target.get(0)).trigger('opened').trigger('opened.fndtn.dropdown', [dropdown, target]);
1366 dropdown.attr('aria-hidden', 'false');
1367 target.attr('aria-expanded', 'true');
1369 dropdown.addClass('f-open-' + this.attr_name(true));
1372 data_attr : function () {
1373 if (this.namespace.length > 0) {
1374 return this.namespace + '-' + this.name;
1380 toggle : function (target) {
1381 if (target.hasClass(this.settings.disabled_class)) {
1384 var dropdown = this.S('#' + target.data(this.data_attr()));
1385 if (dropdown.length === 0) {
1386 // No dropdown found, not continuing
1390 this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown));
1392 if (dropdown.hasClass(this.settings.active_class)) {
1393 this.close.call(this, dropdown);
1394 if (dropdown.data('target') !== target.get(0)) {
1395 this.open.call(this, dropdown, target);
1398 this.open.call(this, dropdown, target);
1402 resize : function () {
1403 var dropdown = this.S('[' + this.attr_name() + '-content].open');
1404 var target = $(dropdown.data("target"));
1406 if (dropdown.length && target.length) {
1407 this.css(dropdown, target);
1411 css : function (dropdown, target) {
1412 var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8),
1413 settings = target.data(this.attr_name(true) + '-init') || this.settings;
1418 var p = this.dirs.bottom.call(dropdown, target, settings);
1420 dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({
1421 position : 'absolute',
1423 'max-width' : 'none',
1427 dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
1430 this.style(dropdown, target, settings);
1436 style : function (dropdown, target, settings) {
1437 var css = $.extend({position : 'absolute'},
1438 this.dirs[settings.align].call(dropdown, target, settings));
1440 dropdown.attr('style', '').css(css);
1443 // return CSS property object
1444 // `this` is the dropdown
1446 // Calculate target offset
1447 _base : function (t) {
1448 var o_p = this.offsetParent(),
1455 //set some flags on the p object to pass along
1456 p.missRight = false;
1459 p.leftRightFlag = false;
1461 //lets see if the panel will be off the screen
1462 //get the actual width of the page and store it
1463 var actualBodyWidth;
1464 if (document.getElementsByClassName('row')[0]) {
1465 actualBodyWidth = document.getElementsByClassName('row')[0].clientWidth;
1467 actualBodyWidth = window.outerWidth;
1470 var actualMarginWidth = (window.outerWidth - actualBodyWidth) / 2;
1471 var actualBoundary = actualBodyWidth;
1473 if (!this.hasClass('mega')) {
1475 if (t.offset().top <= this.outerHeight()) {
1477 actualBoundary = window.outerWidth - actualMarginWidth;
1478 p.leftRightFlag = true;
1482 if (t.offset().left + this.outerWidth() > t.offset().left + actualMarginWidth && t.offset().left - actualMarginWidth > this.outerWidth()) {
1488 if (t.offset().left - this.outerWidth() <= 0) {
1490 p.missRight = false;
1497 top : function (t, s) {
1498 var self = Foundation.libs.dropdown,
1499 p = self.dirs._base.call(this, t);
1501 this.addClass('drop-top');
1503 if (p.missTop == true) {
1504 p.top = p.top + t.outerHeight() + this.outerHeight();
1505 this.removeClass('drop-top');
1508 if (p.missRight == true) {
1509 p.left = p.left - this.outerWidth() + t.outerWidth();
1512 if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
1513 self.adjust_pip(this, t, s, p);
1516 if (Foundation.rtl) {
1517 return {left : p.left - this.outerWidth() + t.outerWidth(),
1518 top : p.top - this.outerHeight()};
1521 return {left : p.left, top : p.top - this.outerHeight()};
1524 bottom : function (t, s) {
1525 var self = Foundation.libs.dropdown,
1526 p = self.dirs._base.call(this, t);
1528 if (p.missRight == true) {
1529 p.left = p.left - this.outerWidth() + t.outerWidth();
1532 if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
1533 self.adjust_pip(this, t, s, p);
1537 return {left : p.left - this.outerWidth() + t.outerWidth(), top : p.top + t.outerHeight()};
1540 return {left : p.left, top : p.top + t.outerHeight()};
1543 left : function (t, s) {
1544 var p = Foundation.libs.dropdown.dirs._base.call(this, t);
1546 this.addClass('drop-left');
1548 if (p.missLeft == true) {
1549 p.left = p.left + this.outerWidth();
1550 p.top = p.top + t.outerHeight();
1551 this.removeClass('drop-left');
1554 return {left : p.left - this.outerWidth(), top : p.top};
1557 right : function (t, s) {
1558 var p = Foundation.libs.dropdown.dirs._base.call(this, t);
1560 this.addClass('drop-right');
1562 if (p.missRight == true) {
1563 p.left = p.left - this.outerWidth();
1564 p.top = p.top + t.outerHeight();
1565 this.removeClass('drop-right');
1567 p.triggeredRight = true;
1570 var self = Foundation.libs.dropdown;
1572 if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
1573 self.adjust_pip(this, t, s, p);
1576 return {left : p.left + t.outerWidth(), top : p.top};
1580 // Insert rule to style psuedo elements
1581 adjust_pip : function (dropdown, target, settings, position) {
1582 var sheet = Foundation.stylesheet,
1583 pip_offset_base = 8;
1585 if (dropdown.hasClass(settings.mega_class)) {
1586 pip_offset_base = position.left + (target.outerWidth() / 2) - 8;
1587 } else if (this.small()) {
1588 pip_offset_base += position.left - 8;
1591 this.rule_idx = sheet.cssRules.length;
1594 var sel_before = '.f-dropdown.open:before',
1595 sel_after = '.f-dropdown.open:after',
1596 css_before = 'left: ' + pip_offset_base + 'px;',
1597 css_after = 'left: ' + (pip_offset_base - 1) + 'px;';
1599 if (position.missRight == true) {
1600 pip_offset_base = dropdown.outerWidth() - 23;
1601 sel_before = '.f-dropdown.open:before',
1602 sel_after = '.f-dropdown.open:after',
1603 css_before = 'left: ' + pip_offset_base + 'px;',
1604 css_after = 'left: ' + (pip_offset_base - 1) + 'px;';
1607 //just a case where right is fired, but its not missing right
1608 if (position.triggeredRight == true) {
1609 sel_before = '.f-dropdown.open:before',
1610 sel_after = '.f-dropdown.open:after',
1611 css_before = 'left:-12px;',
1612 css_after = 'left:-14px;';
1615 if (sheet.insertRule) {
1616 sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx);
1617 sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1);
1619 sheet.addRule(sel_before, css_before, this.rule_idx);
1620 sheet.addRule(sel_after, css_after, this.rule_idx + 1);
1624 // Remove old dropdown rule index
1625 clear_idx : function () {
1626 var sheet = Foundation.stylesheet;
1628 if (typeof this.rule_idx !== 'undefined') {
1629 sheet.deleteRule(this.rule_idx);
1630 sheet.deleteRule(this.rule_idx);
1631 delete this.rule_idx;
1635 small : function () {
1636 return matchMedia(Foundation.media_queries.small).matches &&
1637 !matchMedia(Foundation.media_queries.medium).matches;
1641 this.S(this.scope).off('.fndtn.dropdown');
1642 this.S('html, body').off('.fndtn.dropdown');
1643 this.S(window).off('.fndtn.dropdown');
1644 this.S('[data-dropdown-content]').off('.fndtn.dropdown');
1647 reflow : function () {}
1649 }(jQuery, window, window.document));
1650 ;(function ($, window, document, undefined) {
1653 Foundation.libs.offcanvas = {
1659 open_method : 'move',
1660 close_on_click : false
1663 init : function (scope, method, options) {
1664 this.bindings(method, options);
1667 events : function () {
1674 if (this.settings.open_method === 'move') {
1675 move_class = 'move-';
1676 right_postfix = 'right';
1677 left_postfix = 'left';
1678 } else if (this.settings.open_method === 'overlap_single') {
1679 move_class = 'offcanvas-overlap-';
1680 right_postfix = 'right';
1681 left_postfix = 'left';
1682 } else if (this.settings.open_method === 'overlap') {
1683 move_class = 'offcanvas-overlap';
1686 S(this.scope).off('.offcanvas')
1687 .on('click.fndtn.offcanvas', '.left-off-canvas-toggle', function (e) {
1688 self.click_toggle_class(e, move_class + right_postfix);
1689 if (self.settings.open_method !== 'overlap') {
1690 S('.left-submenu').removeClass(move_class + right_postfix);
1692 $('.left-off-canvas-toggle').attr('aria-expanded', 'true');
1694 .on('click.fndtn.offcanvas', '.left-off-canvas-menu a', function (e) {
1695 var settings = self.get_settings(e);
1696 var parent = S(this).parent();
1698 if (settings.close_on_click && !parent.hasClass('has-submenu') && !parent.hasClass('back')) {
1699 self.hide.call(self, move_class + right_postfix, self.get_wrapper(e));
1700 parent.parent().removeClass(move_class + right_postfix);
1701 } else if (S(this).parent().hasClass('has-submenu')) {
1703 S(this).siblings('.left-submenu').toggleClass(move_class + right_postfix);
1704 } else if (parent.hasClass('back')) {
1706 parent.parent().removeClass(move_class + right_postfix);
1708 $('.left-off-canvas-toggle').attr('aria-expanded', 'true');
1710 .on('click.fndtn.offcanvas', '.right-off-canvas-toggle', function (e) {
1711 self.click_toggle_class(e, move_class + left_postfix);
1712 if (self.settings.open_method !== 'overlap') {
1713 S('.right-submenu').removeClass(move_class + left_postfix);
1715 $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
1717 .on('click.fndtn.offcanvas', '.right-off-canvas-menu a', function (e) {
1718 var settings = self.get_settings(e);
1719 var parent = S(this).parent();
1721 if (settings.close_on_click && !parent.hasClass('has-submenu') && !parent.hasClass('back')) {
1722 self.hide.call(self, move_class + left_postfix, self.get_wrapper(e));
1723 parent.parent().removeClass(move_class + left_postfix);
1724 } else if (S(this).parent().hasClass('has-submenu')) {
1726 S(this).siblings('.right-submenu').toggleClass(move_class + left_postfix);
1727 } else if (parent.hasClass('back')) {
1729 parent.parent().removeClass(move_class + left_postfix);
1731 $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
1733 .on('click.fndtn.offcanvas', '.exit-off-canvas', function (e) {
1734 self.click_remove_class(e, move_class + left_postfix);
1735 S('.right-submenu').removeClass(move_class + left_postfix);
1736 if (right_postfix) {
1737 self.click_remove_class(e, move_class + right_postfix);
1738 S('.left-submenu').removeClass(move_class + left_postfix);
1740 $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
1742 .on('click.fndtn.offcanvas', '.exit-off-canvas', function (e) {
1743 self.click_remove_class(e, move_class + left_postfix);
1744 $('.left-off-canvas-toggle').attr('aria-expanded', 'false');
1745 if (right_postfix) {
1746 self.click_remove_class(e, move_class + right_postfix);
1747 $('.right-off-canvas-toggle').attr('aria-expanded', 'false');
1752 toggle : function (class_name, $off_canvas) {
1753 $off_canvas = $off_canvas || this.get_wrapper();
1754 if ($off_canvas.is('.' + class_name)) {
1755 this.hide(class_name, $off_canvas);
1757 this.show(class_name, $off_canvas);
1761 show : function (class_name, $off_canvas) {
1762 $off_canvas = $off_canvas || this.get_wrapper();
1763 $off_canvas.trigger('open').trigger('open.fndtn.offcanvas');
1764 $off_canvas.addClass(class_name);
1767 hide : function (class_name, $off_canvas) {
1768 $off_canvas = $off_canvas || this.get_wrapper();
1769 $off_canvas.trigger('close').trigger('close.fndtn.offcanvas');
1770 $off_canvas.removeClass(class_name);
1773 click_toggle_class : function (e, class_name) {
1775 var $off_canvas = this.get_wrapper(e);
1776 this.toggle(class_name, $off_canvas);
1779 click_remove_class : function (e, class_name) {
1781 var $off_canvas = this.get_wrapper(e);
1782 this.hide(class_name, $off_canvas);
1785 get_settings : function (e) {
1786 var offcanvas = this.S(e.target).closest('[' + this.attr_name() + ']');
1787 return offcanvas.data(this.attr_name(true) + '-init') || this.settings;
1790 get_wrapper : function (e) {
1791 var $off_canvas = this.S(e ? e.target : this.scope).closest('.off-canvas-wrap');
1793 if ($off_canvas.length === 0) {
1794 $off_canvas = this.S('.off-canvas-wrap');
1799 reflow : function () {}
1801 }(jQuery, window, window.document));
1802 ;(function ($, window, document, undefined) {
1805 Foundation.libs.tab = {
1811 active_class : 'active',
1812 callback : function () {},
1813 deep_linking : false,
1814 scroll_to_content : true,
1818 default_tab_hashes : [],
1820 init : function (scope, method, options) {
1824 this.bindings(method, options);
1826 // store the initial href, which is used to allow correct behaviour of the
1827 // browser back button when deep linking is turned on.
1828 self.entry_location = window.location.href;
1830 this.handle_location_hash_change();
1832 // Store the default active tabs which will be referenced when the
1833 // location hash is absent, as in the case of navigating the tabs and
1834 // returning to the first viewing via the browser Back button.
1835 S('[' + this.attr_name() + '] > .active > a', this.scope).each(function () {
1836 self.default_tab_hashes.push(this.hash);
1840 events : function () {
1844 var usual_tab_behavior = function (e) {
1845 var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
1846 if (!settings.is_hover || Modernizr.touch) {
1848 e.stopPropagation();
1849 self.toggle_active_tab(S(this).parent());
1855 // Click event: tab title
1856 .on('focus.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
1857 .on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
1858 // Hover event: tab title
1859 .on('mouseenter.fndtn.tab', '[' + this.attr_name() + '] > * > a', function (e) {
1860 var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
1861 if (settings.is_hover) {
1862 self.toggle_active_tab(S(this).parent());
1866 // Location hash change event
1867 S(window).on('hashchange.fndtn.tab', function (e) {
1869 self.handle_location_hash_change();
1873 handle_location_hash_change : function () {
1878 S('[' + this.attr_name() + ']', this.scope).each(function () {
1879 var settings = S(this).data(self.attr_name(true) + '-init');
1880 if (settings.deep_linking) {
1881 // Match the location hash to a label
1883 if (settings.scroll_to_content) {
1884 hash = self.scope.location.hash;
1886 // prefix the hash to prevent anchor scrolling
1887 hash = self.scope.location.hash.replace('fndtn-', '');
1890 // Check whether the location hash references a tab content div or
1891 // another element on the page (inside or outside the tab content div)
1892 var hash_element = S(hash);
1893 if (hash_element.hasClass('content') && hash_element.parent().hasClass('tabs-content')) {
1895 self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + hash + ']').parent());
1897 // Not the tab content div. If inside the tab content, find the
1898 // containing tab and toggle it as active.
1899 var hash_tab_container_id = hash_element.closest('.content').attr('id');
1900 if (hash_tab_container_id != undefined) {
1901 self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=#' + hash_tab_container_id + ']').parent(), hash);
1905 // Reference the default tab hashes which were initialized in the init function
1906 for (var ind = 0; ind < self.default_tab_hashes.length; ind++) {
1907 self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + self.default_tab_hashes[ind] + ']').parent());
1914 toggle_active_tab : function (tab, location_hash) {
1917 tabs = tab.closest('[' + this.attr_name() + ']'),
1918 tab_link = tab.find('a'),
1919 anchor = tab.children('a').first(),
1920 target_hash = '#' + anchor.attr('href').split('#')[1],
1921 target = S(target_hash),
1922 siblings = tab.siblings(),
1923 settings = tabs.data(this.attr_name(true) + '-init'),
1924 interpret_keyup_action = function (e) {
1925 // Light modification of Heydon Pickering's Practical ARIA Examples: http://heydonworks.com/practical_aria_examples/js/a11y.js
1927 // define current, previous and next (possible) tabs
1929 var $original = $(this);
1930 var $prev = $(this).parents('li').prev().children('[role="tab"]');
1931 var $next = $(this).parents('li').next().children('[role="tab"]');
1934 // find the direction (prev or next)
1936 switch (e.keyCode) {
1948 if ($target.length) {
1951 'aria-selected' : null
1955 'aria-selected' : true
1961 $('[role="tabpanel"]')
1962 .attr('aria-hidden', 'true');
1964 // Show panel which corresponds to target
1966 $('#' + $(document.activeElement).attr('href').substring(1))
1967 .attr('aria-hidden', null);
1970 go_to_hash = function(hash) {
1971 // This function allows correct behaviour of the browser's back button when deep linking is enabled. Without it
1972 // the user would get continually redirected to the default hash.
1973 var is_entry_location = window.location.href === self.entry_location,
1974 default_hash = settings.scroll_to_content ? self.default_tab_hashes[0] : is_entry_location ? window.location.hash :'fndtn-' + self.default_tab_hashes[0].replace('#', '')
1976 if (!(is_entry_location && hash === default_hash)) {
1977 window.location.hash = hash;
1981 // allow usage of data-tab-content attribute instead of href
1982 if (S(this).data(this.data_attr('tab-content'))) {
1983 target_hash = '#' + S(this).data(this.data_attr('tab-content')).split('#')[1];
1984 target = S(target_hash);
1987 if (settings.deep_linking) {
1989 if (settings.scroll_to_content) {
1991 // retain current hash to scroll to content
1992 go_to_hash(location_hash || target_hash);
1994 if (location_hash == undefined || location_hash == target_hash) {
1995 tab.parent()[0].scrollIntoView();
1997 S(target_hash)[0].scrollIntoView();
2000 // prefix the hashes so that the browser doesn't scroll down
2001 if (location_hash != undefined) {
2002 go_to_hash('fndtn-' + location_hash.replace('#', ''));
2004 go_to_hash('fndtn-' + target_hash.replace('#', ''));
2009 // WARNING: The activation and deactivation of the tab content must
2010 // occur after the deep linking in order to properly refresh the browser
2011 // window (notably in Chrome).
2012 // Clean up multiple attr instances to done once
2013 tab.addClass(settings.active_class).triggerHandler('opened');
2014 tab_link.attr({'aria-selected' : 'true', tabindex : 0});
2015 siblings.removeClass(settings.active_class)
2016 siblings.find('a').attr({'aria-selected' : 'false', tabindex : -1});
2017 target.siblings().removeClass(settings.active_class).attr({'aria-hidden' : 'true', tabindex : -1});
2018 target.addClass(settings.active_class).attr('aria-hidden', 'false').removeAttr('tabindex');
2019 settings.callback(tab);
2020 target.triggerHandler('toggled', [tab]);
2021 tabs.triggerHandler('toggled', [target]);
2023 tab_link.off('keydown').on('keydown', interpret_keyup_action );
2026 data_attr : function (str) {
2027 if (this.namespace.length > 0) {
2028 return this.namespace + '-' + str;
2034 off : function () {},
2036 reflow : function () {}
2038 }(jQuery, window, window.document));
2039 ;(function ($, window, document, undefined) {
2042 Foundation.libs.tooltip = {
2048 additional_inheritable_classes : [],
2049 tooltip_class : '.tooltip',
2051 touch_close_text : 'Tap To Close',
2052 disable_for_touch : false,
2055 tip_template : function (selector, content) {
2056 return '<span data-selector="' + selector + '" id="' + selector + '" class="'
2057 + Foundation.libs.tooltip.settings.tooltip_class.substring(1)
2058 + '" role="tooltip">' + content + '<span class="nub"></span></span>';
2064 init : function (scope, method, options) {
2065 Foundation.inherit(this, 'random_str');
2066 this.bindings(method, options);
2069 should_show : function (target, tip) {
2070 var settings = $.extend({}, this.settings, this.data_options(target));
2072 if (settings.show_on === 'all') {
2074 } else if (this.small() && settings.show_on === 'small') {
2076 } else if (this.medium() && settings.show_on === 'medium') {
2078 } else if (this.large() && settings.show_on === 'large') {
2084 medium : function () {
2085 return matchMedia(Foundation.media_queries['medium']).matches;
2088 large : function () {
2089 return matchMedia(Foundation.media_queries['large']).matches;
2092 events : function (instance) {
2096 self.create(this.S(instance));
2100 .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip',
2101 '[' + this.attr_name() + ']', function (e) {
2102 var $this = S(this),
2103 settings = $.extend({}, self.settings, self.data_options($this)),
2106 if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type) && S(e.target).is('a')) {
2110 if (/mouse/i.test(e.type) && self.ie_touch(e)) {
2114 if ($this.hasClass('open')) {
2115 if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
2120 if (settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
2122 } else if (!settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
2124 S(settings.tooltip_class + '.open').hide();
2128 if (/enter|over/i.test(e.type)) {
2129 this.timer = setTimeout(function () {
2130 var tip = self.showTip($this);
2131 }.bind(this), self.settings.hover_delay);
2132 } else if (e.type === 'mouseout' || e.type === 'mouseleave') {
2133 clearTimeout(this.timer);
2136 self.showTip($this);
2140 .on('mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', '[' + this.attr_name() + '].open', function (e) {
2141 if (/mouse/i.test(e.type) && self.ie_touch(e)) {
2145 if ($(this).data('tooltip-open-event-type') == 'touch' && e.type == 'mouseleave') {
2147 } else if ($(this).data('tooltip-open-event-type') == 'mouse' && /MSPointerDown|touchstart/i.test(e.type)) {
2148 self.convert_to_touch($(this));
2153 .on('DOMNodeRemoved DOMAttrModified', '[' + this.attr_name() + ']:not(a)', function (e) {
2158 ie_touch : function (e) {
2159 // How do I distinguish between IE11 and Windows Phone 8?????
2163 showTip : function ($target) {
2164 var $tip = this.getTip($target);
2165 if (this.should_show($target, $tip)) {
2166 return this.show($target);
2171 getTip : function ($target) {
2172 var selector = this.selector($target),
2173 settings = $.extend({}, this.settings, this.data_options($target)),
2177 tip = this.S('span[data-selector="' + selector + '"]' + settings.tooltip_class);
2180 return (typeof tip === 'object') ? tip : false;
2183 selector : function ($target) {
2184 var id = $target.attr('id'),
2185 dataSelector = $target.attr(this.attr_name()) || $target.attr('data-selector');
2187 if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') {
2188 dataSelector = this.random_str(6);
2190 .attr('data-selector', dataSelector)
2191 .attr('aria-describedby', dataSelector);
2194 return (id && id.length > 0) ? id : dataSelector;
2197 create : function ($target) {
2199 settings = $.extend({}, this.settings, this.data_options($target)),
2200 tip_template = this.settings.tip_template;
2202 if (typeof settings.tip_template === 'string' && window.hasOwnProperty(settings.tip_template)) {
2203 tip_template = window[settings.tip_template];
2206 var $tip = $(tip_template(this.selector($target), $('<div></div>').html($target.attr('title')).html())),
2207 classes = this.inheritable_classes($target);
2209 $tip.addClass(classes).appendTo(settings.append_to);
2211 if (Modernizr.touch) {
2212 $tip.append('<span class="tap-to-close">' + settings.touch_close_text + '</span>');
2213 $tip.on('touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', function (e) {
2218 $target.removeAttr('title').attr('title', '');
2221 reposition : function (target, tip, classes) {
2222 var width, nub, nubHeight, nubWidth, column, objPos;
2224 tip.css('visibility', 'hidden').show();
2226 width = target.data('width');
2227 nub = tip.children('.nub');
2228 nubHeight = nub.outerHeight();
2229 nubWidth = nub.outerHeight();
2232 tip.css({'width' : '100%'});
2234 tip.css({'width' : (width) ? width : 'auto'});
2237 objPos = function (obj, top, right, bottom, left, width) {
2239 'top' : (top) ? top : 'auto',
2240 'bottom' : (bottom) ? bottom : 'auto',
2241 'left' : (left) ? left : 'auto',
2242 'right' : (right) ? right : 'auto'
2246 objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', target.offset().left);
2249 objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', 12.5, $(this.scope).width());
2250 tip.addClass('tip-override');
2251 objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left);
2253 var left = target.offset().left;
2254 if (Foundation.rtl) {
2255 nub.addClass('rtl');
2256 left = target.offset().left + target.outerWidth() - tip.outerWidth();
2258 objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', left);
2259 tip.removeClass('tip-override');
2260 if (classes && classes.indexOf('tip-top') > -1) {
2261 if (Foundation.rtl) {
2262 nub.addClass('rtl');
2264 objPos(tip, (target.offset().top - tip.outerHeight()), 'auto', 'auto', left)
2265 .removeClass('tip-override');
2266 } else if (classes && classes.indexOf('tip-left') > -1) {
2267 objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left - tip.outerWidth() - nubHeight))
2268 .removeClass('tip-override');
2269 nub.removeClass('rtl');
2270 } else if (classes && classes.indexOf('tip-right') > -1) {
2271 objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left + target.outerWidth() + nubHeight))
2272 .removeClass('tip-override');
2273 nub.removeClass('rtl');
2277 tip.css('visibility', 'visible').hide();
2280 small : function () {
2281 return matchMedia(Foundation.media_queries.small).matches &&
2282 !matchMedia(Foundation.media_queries.medium).matches;
2285 inheritable_classes : function ($target) {
2286 var settings = $.extend({}, this.settings, this.data_options($target)),
2287 inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'radius', 'round'].concat(settings.additional_inheritable_classes),
2288 classes = $target.attr('class'),
2289 filtered = classes ? $.map(classes.split(' '), function (el, i) {
2290 if ($.inArray(el, inheritables) !== -1) {
2295 return $.trim(filtered);
2298 convert_to_touch : function ($target) {
2300 $tip = self.getTip($target),
2301 settings = $.extend({}, self.settings, self.data_options($target));
2303 if ($tip.find('.tap-to-close').length === 0) {
2304 $tip.append('<span class="tap-to-close">' + settings.touch_close_text + '</span>');
2305 $tip.on('click.fndtn.tooltip.tapclose touchstart.fndtn.tooltip.tapclose MSPointerDown.fndtn.tooltip.tapclose', function (e) {
2310 $target.data('tooltip-open-event-type', 'touch');
2313 show : function ($target) {
2314 var $tip = this.getTip($target);
2316 if ($target.data('tooltip-open-event-type') == 'touch') {
2317 this.convert_to_touch($target);
2320 this.reposition($target, $tip, $target.attr('class'));
2321 $target.addClass('open');
2325 hide : function ($target) {
2326 var $tip = this.getTip($target);
2328 $tip.fadeOut(150, function () {
2329 $tip.find('.tap-to-close').remove();
2330 $tip.off('click.fndtn.tooltip.tapclose MSPointerDown.fndtn.tapclose');
2331 $target.removeClass('open');
2337 this.S(this.scope).off('.fndtn.tooltip');
2338 this.S(this.settings.tooltip_class).each(function (i) {
2339 $('[' + self.attr_name() + ']').eq(i).attr('title', $(this).text());
2343 reflow : function () {}
2345 }(jQuery, window, window.document));
2346 ;(function ($, window, document, undefined) {
2349 Foundation.libs.topbar = {
2356 sticky_class : 'sticky',
2357 custom_back_text : true,
2359 mobile_show_parent_link : true,
2361 scrolltop : true, // jump to top when sticky nav menu toggle is clicked
2365 init : function (section, method, options) {
2366 Foundation.inherit(this, 'add_custom_rule register_media throttle');
2369 self.register_media('topbar', 'foundation-mq-topbar');
2371 this.bindings(method, options);
2373 self.S('[' + this.attr_name() + ']', this.scope).each(function () {
2374 var topbar = $(this),
2375 settings = topbar.data(self.attr_name(true) + '-init'),
2376 section = self.S('section, .top-bar-section', this);
2377 topbar.data('index', 0);
2378 var topbarContainer = topbar.parent();
2379 if (topbarContainer.hasClass('fixed') || self.is_sticky(topbar, topbarContainer, settings) ) {
2380 self.settings.sticky_class = settings.sticky_class;
2381 self.settings.sticky_topbar = topbar;
2382 topbar.data('height', topbarContainer.outerHeight());
2383 topbar.data('stickyoffset', topbarContainer.offset().top);
2385 topbar.data('height', topbar.outerHeight());
2388 if (!settings.assembled) {
2389 self.assemble(topbar);
2392 if (settings.is_hover) {
2393 self.S('.has-dropdown', topbar).addClass('not-click');
2395 self.S('.has-dropdown', topbar).removeClass('not-click');
2398 // Pad body when sticky (scrolled) or fixed.
2399 self.add_custom_rule('.f-topbar-fixed { padding-top: ' + topbar.data('height') + 'px }');
2401 if (topbarContainer.hasClass('fixed')) {
2402 self.S('body').addClass('f-topbar-fixed');
2408 is_sticky : function (topbar, topbarContainer, settings) {
2409 var sticky = topbarContainer.hasClass(settings.sticky_class);
2410 var smallMatch = matchMedia(Foundation.media_queries.small).matches;
2411 var medMatch = matchMedia(Foundation.media_queries.medium).matches;
2412 var lrgMatch = matchMedia(Foundation.media_queries.large).matches;
2414 if (sticky && settings.sticky_on === 'all') {
2417 if (sticky && this.small() && settings.sticky_on.indexOf('small') !== -1) {
2418 if (smallMatch && !medMatch && !lrgMatch) { return true; }
2420 if (sticky && this.medium() && settings.sticky_on.indexOf('medium') !== -1) {
2421 if (smallMatch && medMatch && !lrgMatch) { return true; }
2423 if (sticky && this.large() && settings.sticky_on.indexOf('large') !== -1) {
2424 if (smallMatch && medMatch && lrgMatch) { return true; }
2427 // fix for iOS browsers
2428 if (sticky && navigator.userAgent.match(/(iPad|iPhone|iPod)/g)) {
2434 toggle : function (toggleEl) {
2439 topbar = self.S(toggleEl).closest('[' + this.attr_name() + ']');
2441 topbar = self.S('[' + this.attr_name() + ']');
2444 var settings = topbar.data(this.attr_name(true) + '-init');
2446 var section = self.S('section, .top-bar-section', topbar);
2448 if (self.breakpoint()) {
2450 section.css({left : '0%'});
2451 $('>.name', section).css({left : '100%'});
2453 section.css({right : '0%'});
2454 $('>.name', section).css({right : '100%'});
2457 self.S('li.moved', section).removeClass('moved');
2458 topbar.data('index', 0);
2461 .toggleClass('expanded')
2465 if (settings.scrolltop) {
2466 if (!topbar.hasClass('expanded')) {
2467 if (topbar.hasClass('fixed')) {
2468 topbar.parent().addClass('fixed');
2469 topbar.removeClass('fixed');
2470 self.S('body').addClass('f-topbar-fixed');
2472 } else if (topbar.parent().hasClass('fixed')) {
2473 if (settings.scrolltop) {
2474 topbar.parent().removeClass('fixed');
2475 topbar.addClass('fixed');
2476 self.S('body').removeClass('f-topbar-fixed');
2478 window.scrollTo(0, 0);
2480 topbar.parent().removeClass('expanded');
2484 if (self.is_sticky(topbar, topbar.parent(), settings)) {
2485 topbar.parent().addClass('fixed');
2488 if (topbar.parent().hasClass('fixed')) {
2489 if (!topbar.hasClass('expanded')) {
2490 topbar.removeClass('fixed');
2491 topbar.parent().removeClass('expanded');
2492 self.update_sticky_positioning();
2494 topbar.addClass('fixed');
2495 topbar.parent().addClass('expanded');
2496 self.S('body').addClass('f-topbar-fixed');
2504 events : function (bar) {
2510 .on('click.fndtn.topbar', '[' + this.attr_name() + '] .toggle-topbar', function (e) {
2514 .on('click.fndtn.topbar', '.top-bar .top-bar-section li a[href^="#"],[' + this.attr_name() + '] .top-bar-section li a[href^="#"]', function (e) {
2515 var li = $(this).closest('li');
2516 if (self.breakpoint() && !li.hasClass('back') && !li.hasClass('has-dropdown')) {
2520 .on('click.fndtn.topbar', '[' + this.attr_name() + '] li.has-dropdown', function (e) {
2522 target = S(e.target),
2523 topbar = li.closest('[' + self.attr_name() + ']'),
2524 settings = topbar.data(self.attr_name(true) + '-init');
2526 if (target.data('revealId')) {
2531 if (self.breakpoint()) {
2535 if (settings.is_hover && !Modernizr.touch) {
2539 e.stopImmediatePropagation();
2541 if (li.hasClass('hover')) {
2543 .removeClass('hover')
2545 .removeClass('hover');
2547 li.parents('li.hover')
2548 .removeClass('hover');
2550 li.addClass('hover');
2552 $(li).siblings().removeClass('hover');
2554 if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) {
2559 .on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown>a', function (e) {
2560 if (self.breakpoint()) {
2564 var $this = S(this),
2565 topbar = $this.closest('[' + self.attr_name() + ']'),
2566 section = topbar.find('section, .top-bar-section'),
2567 dropdownHeight = $this.next('.dropdown').outerHeight(),
2568 $selectedLi = $this.closest('li');
2570 topbar.data('index', topbar.data('index') + 1);
2571 $selectedLi.addClass('moved');
2574 section.css({left : -(100 * topbar.data('index')) + '%'});
2575 section.find('>.name').css({left : 100 * topbar.data('index') + '%'});
2577 section.css({right : -(100 * topbar.data('index')) + '%'});
2578 section.find('>.name').css({right : 100 * topbar.data('index') + '%'});
2581 topbar.css('height', $this.siblings('ul').outerHeight(true) + topbar.data('height'));
2585 S(window).off('.topbar').on('resize.fndtn.topbar', self.throttle(function () {
2586 self.resize.call(self);
2587 }, 50)).trigger('resize').trigger('resize.fndtn.topbar').load(function () {
2588 // Ensure that the offset is calculated after all of the pages resources have loaded
2589 S(this).trigger('resize.fndtn.topbar');
2592 S('body').off('.topbar').on('click.fndtn.topbar', function (e) {
2593 var parent = S(e.target).closest('li').closest('li.hover');
2595 if (parent.length > 0) {
2599 S('[' + self.attr_name() + '] li.hover').removeClass('hover');
2602 // Go up a level on Click
2603 S(this.scope).on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown .back', function (e) {
2606 var $this = S(this),
2607 topbar = $this.closest('[' + self.attr_name() + ']'),
2608 section = topbar.find('section, .top-bar-section'),
2609 settings = topbar.data(self.attr_name(true) + '-init'),
2610 $movedLi = $this.closest('li.moved'),
2611 $previousLevelUl = $movedLi.parent();
2613 topbar.data('index', topbar.data('index') - 1);
2616 section.css({left : -(100 * topbar.data('index')) + '%'});
2617 section.find('>.name').css({left : 100 * topbar.data('index') + '%'});
2619 section.css({right : -(100 * topbar.data('index')) + '%'});
2620 section.find('>.name').css({right : 100 * topbar.data('index') + '%'});
2623 if (topbar.data('index') === 0) {
2624 topbar.css('height', '');
2626 topbar.css('height', $previousLevelUl.outerHeight(true) + topbar.data('height'));
2629 setTimeout(function () {
2630 $movedLi.removeClass('moved');
2634 // Show dropdown menus when their items are focused
2635 S(this.scope).find('.dropdown a')
2636 .focus(function () {
2637 $(this).parents('.has-dropdown').addClass('hover');
2640 $(this).parents('.has-dropdown').removeClass('hover');
2644 resize : function () {
2646 self.S('[' + this.attr_name() + ']').each(function () {
2647 var topbar = self.S(this),
2648 settings = topbar.data(self.attr_name(true) + '-init');
2650 var stickyContainer = topbar.parent('.' + self.settings.sticky_class);
2653 if (!self.breakpoint()) {
2654 var doToggle = topbar.hasClass('expanded');
2657 .removeClass('expanded')
2659 .removeClass('hover');
2662 self.toggle(topbar);
2666 if (self.is_sticky(topbar, stickyContainer, settings)) {
2667 if (stickyContainer.hasClass('fixed')) {
2668 // Remove the fixed to allow for correct calculation of the offset.
2669 stickyContainer.removeClass('fixed');
2671 stickyOffset = stickyContainer.offset().top;
2672 if (self.S(document.body).hasClass('f-topbar-fixed')) {
2673 stickyOffset -= topbar.data('height');
2676 topbar.data('stickyoffset', stickyOffset);
2677 stickyContainer.addClass('fixed');
2679 stickyOffset = stickyContainer.offset().top;
2680 topbar.data('stickyoffset', stickyOffset);
2687 breakpoint : function () {
2688 return !matchMedia(Foundation.media_queries['topbar']).matches;
2691 small : function () {
2692 return matchMedia(Foundation.media_queries['small']).matches;
2695 medium : function () {
2696 return matchMedia(Foundation.media_queries['medium']).matches;
2699 large : function () {
2700 return matchMedia(Foundation.media_queries['large']).matches;
2703 assemble : function (topbar) {
2705 settings = topbar.data(this.attr_name(true) + '-init'),
2706 section = self.S('section, .top-bar-section', topbar);
2708 // Pull element out of the DOM for manipulation
2711 self.S('.has-dropdown>a', section).each(function () {
2712 var $link = self.S(this),
2713 $dropdown = $link.siblings('.dropdown'),
2714 url = $link.attr('href'),
2717 if (!$dropdown.find('.title.back').length) {
2719 if (settings.mobile_show_parent_link == true && url) {
2720 $titleLi = $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5></li><li class="parent-link hide-for-large-up"><a class="parent-link js-generated" href="' + url + '">' + $link.html() +'</a></li>');
2722 $titleLi = $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5>');
2725 // Copy link to subnav
2726 if (settings.custom_back_text == true) {
2727 $('h5>a', $titleLi).html(settings.back_text);
2729 $('h5>a', $titleLi).html('« ' + $link.html());
2731 $dropdown.prepend($titleLi);
2735 // Put element back in the DOM
2736 section.appendTo(topbar);
2741 this.assembled(topbar);
2744 assembled : function (topbar) {
2745 topbar.data(this.attr_name(true), $.extend({}, topbar.data(this.attr_name(true)), {assembled : true}));
2748 height : function (ul) {
2752 $('> li', ul).each(function () {
2753 total += self.S(this).outerHeight(true);
2759 sticky : function () {
2762 this.S(window).on('scroll', function () {
2763 self.update_sticky_positioning();
2767 update_sticky_positioning : function () {
2768 var klass = '.' + this.settings.sticky_class,
2769 $window = this.S(window),
2772 if (self.settings.sticky_topbar && self.is_sticky(this.settings.sticky_topbar, this.settings.sticky_topbar.parent(), this.settings)) {
2773 var distance = this.settings.sticky_topbar.data('stickyoffset');
2774 if (!self.S(klass).hasClass('expanded')) {
2775 if ($window.scrollTop() > (distance)) {
2776 if (!self.S(klass).hasClass('fixed')) {
2777 self.S(klass).addClass('fixed');
2778 self.S('body').addClass('f-topbar-fixed');
2780 } else if ($window.scrollTop() <= distance) {
2781 if (self.S(klass).hasClass('fixed')) {
2782 self.S(klass).removeClass('fixed');
2783 self.S('body').removeClass('f-topbar-fixed');
2791 this.S(this.scope).off('.fndtn.topbar');
2792 this.S(window).off('.fndtn.topbar');
2795 reflow : function () {}
2797 }(jQuery, window, window.document));