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') {
55 if (!cont
) return context
;
59 return $(cont
.querySelectorAll(selector
));
62 return $(document
.querySelectorAll(selector
));
65 return $(selector
, context
);
68 // Namespace functions.
70 var attr_name = function (init
) {
72 if (!init
) arr
.push('data');
73 if (this.namespace.length
> 0) arr
.push(this.namespace);
79 var add_namespace = function (str
) {
80 var parts
= str
.split('-'),
88 if (this.namespace.length
> 0) {
89 arr
.push(this.namespace, parts
[i
]);
96 return arr
.reverse().join('-');
99 // Event binding and data-options updating.
101 var bindings = function (method
, options
) {
103 should_bind_events
= !S(this).data(this.attr_name(true));
105 if (S(this.scope
).is('[' + this.attr_name() + ']')) {
106 S(this.scope
).data(this.attr_name(true) + '-init', $.extend({}, this.settings
, (options
|| method
), this.data_options(S(this.scope
))));
108 if (should_bind_events
) {
109 this.events(this.scope
);
113 S('[' + this.attr_name() + ']', this.scope
).each(function () {
114 var should_bind_events
= !S(this).data(self
.attr_name(true) + '-init');
115 S(this).data(self
.attr_name(true) + '-init', $.extend({}, self
.settings
, (options
|| method
), self
.data_options(S(this))));
117 if (should_bind_events
) {
122 // # 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.
123 if (typeof method
=== 'string') {
124 return this[method
].call(this, options
);
129 var single_image_loaded = function (image
, callback
) {
134 function bindLoad() {
135 this.one('load', loaded
);
137 if (/MSIE (\d+\.\d+);/.test(navigator
.userAgent
)) {
138 var src
= this.attr('src'),
139 param
= src
.match(/\?/) ? '&' : '?';
141 param
+= 'random=' + (new Date()).getTime();
142 this.attr('src', src
+ param
);
146 if (!image
.attr('src')) {
151 if (image
[0].complete
|| image
[0].readyState
=== 4) {
154 bindLoad
.call(image
);
159 https://github.com/paulirish/matchMedia.js
162 window
.matchMedia
= window
.matchMedia
|| (function (doc
) {
167 docElem
= doc
.documentElement
,
168 refNode
= docElem
.firstElementChild
|| docElem
.firstChild
,
169 // fakeBody required for <FF4 when executed in <head>
170 fakeBody
= doc
.createElement('body'),
171 div
= doc
.createElement('div');
173 div
.id
= 'mq-test-1';
174 div
.style
.cssText
= 'position:absolute;top:-100em';
175 fakeBody
.style
.background
= 'none';
176 fakeBody
.appendChild(div
);
178 return function (q
) {
180 div
.innerHTML
= '­<style media="' + q
+ '"> #mq-test-1 { width: 42px; }</style>';
182 docElem
.insertBefore(fakeBody
, refNode
);
183 bool
= div
.offsetWidth
=== 42;
184 docElem
.removeChild(fakeBody
);
196 * jquery.requestAnimationFrame
197 * https://github.com/gnarf37/jquery-requestAnimationFrame
198 * Requires jQuery 1.8+
200 * Copyright (c) 2012 Corey Frang
201 * Licensed under the MIT license.
206 // requestAnimationFrame polyfill adapted from Erik Möller
207 // fixes from Paul Irish and Tino Zijdel
208 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
209 // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
213 vendors
= ['webkit', 'moz'],
214 requestAnimationFrame
= window
.requestAnimationFrame
,
215 cancelAnimationFrame
= window
.cancelAnimationFrame
,
216 jqueryFxAvailable
= 'undefined' !== typeof jQuery
.fx
;
218 for (; lastTime
< vendors
.length
&& !requestAnimationFrame
; lastTime
++) {
219 requestAnimationFrame
= window
[vendors
[lastTime
] + 'RequestAnimationFrame'];
220 cancelAnimationFrame
= cancelAnimationFrame
||
221 window
[vendors
[lastTime
] + 'CancelAnimationFrame'] ||
222 window
[vendors
[lastTime
] + 'CancelRequestAnimationFrame'];
227 requestAnimationFrame(raf
);
229 if (jqueryFxAvailable
) {
235 if (requestAnimationFrame
) {
237 window
.requestAnimationFrame
= requestAnimationFrame
;
238 window
.cancelAnimationFrame
= cancelAnimationFrame
;
240 if (jqueryFxAvailable
) {
241 jQuery
.fx
.timer = function (timer
) {
242 if (timer() && jQuery
.timers
.push(timer
) && !animating
) {
248 jQuery
.fx
.stop = function () {
254 window
.requestAnimationFrame = function (callback
) {
255 var currTime
= new Date().getTime(),
256 timeToCall
= Math
.max(0, 16 - (currTime
- lastTime
)),
257 id
= window
.setTimeout(function () {
258 callback(currTime
+ timeToCall
);
260 lastTime
= currTime
+ timeToCall
;
264 window
.cancelAnimationFrame = function (id
) {
273 function removeQuotes(string
) {
274 if (typeof string
=== 'string' || string
instanceof String
) {
275 string
= string
.replace(/^['\\/"]+|(;\s?})+|['\\/"]+$/g, '');
281 window
.Foundation
= {
287 'small': S('.foundation-mq-small').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
288 'small-only': S('.foundation-mq-small-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
289 'medium': S('.foundation-mq-medium').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
290 'medium-only': S('.foundation-mq-medium-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
291 'large': S('.foundation-mq-large').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
292 'large-only': S('.foundation-mq-large-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
293 'xlarge': S('.foundation-mq-xlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
294 'xlarge-only': S('.foundation-mq-xlarge-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
295 'xxlarge': S('.foundation-mq-xxlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, '')
298 stylesheet
: $('<style></style>').appendTo('head')[0].sheet
,
304 init: function (scope
, libraries
, method
, options
, response
) {
305 var args
= [scope
, method
, options
, response
],
309 this.rtl
= /rtl/i.test(S('html').attr('dir'));
311 // set foundation global scope
312 this.scope
= scope
|| this.scope
;
314 this.set_namespace();
316 if (libraries
&& typeof libraries
=== 'string' && !/reflow/i.test(libraries
)) {
317 if (this.libs
.hasOwnProperty(libraries
)) {
318 responses
.push(this.init_lib(libraries
, args
));
321 for (var lib
in this.libs
) {
322 responses
.push(this.init_lib(lib
, libraries
));
326 S(window
).load(function () {
328 .trigger('resize.fndtn.clearing')
329 .trigger('resize.fndtn.dropdown')
330 .trigger('resize.fndtn.equalizer')
331 .trigger('resize.fndtn.interchange')
332 .trigger('resize.fndtn.joyride')
333 .trigger('resize.fndtn.magellan')
334 .trigger('resize.fndtn.topbar')
335 .trigger('resize.fndtn.slider');
341 init_lib: function (lib
, args
) {
342 if (this.libs
.hasOwnProperty(lib
)) {
343 this.patch(this.libs
[lib
]);
345 if (args
&& args
.hasOwnProperty(lib
)) {
346 if (typeof this.libs
[lib
].settings
!== 'undefined') {
347 $.extend(true, this.libs
[lib
].settings
, args
[lib
]);
349 else if (typeof this.libs
[lib
].defaults
!== 'undefined') {
350 $.extend(true, this.libs
[lib
].defaults
, args
[lib
]);
352 return this.libs
[lib
].init
.apply(this.libs
[lib
], [this.scope
, args
[lib
]]);
355 args
= args
instanceof Array
? args
: new Array(args
);
356 return this.libs
[lib
].init
.apply(this.libs
[lib
], args
);
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 () {
473 if (!immediate
) result
= func
.apply(context
, args
);
475 var callNow
= immediate
&& !timeout
;
476 clearTimeout(timeout
);
477 timeout
= setTimeout(later
, delay
);
478 if (callNow
) result
= func
.apply(context
, args
);
484 // Parses data-options attribute
487 // El (jQuery Object): Element to be parsed.
490 // Options (Javascript Object): Contents of the element's data-options
492 data_options: function (el
, data_attr_name
) {
493 data_attr_name
= data_attr_name
|| 'options';
494 var opts
= {}, ii
, p
, opts_arr
,
495 data_options = function (el
) {
496 var namespace = Foundation
.global
.namespace;
498 if (namespace.length
> 0) {
499 return el
.data(namespace + '-' + data_attr_name
);
502 return el
.data(data_attr_name
);
505 var cached_options
= data_options(el
);
507 if (typeof cached_options
=== 'object') {
508 return cached_options
;
511 opts_arr
= (cached_options
|| ':').split(';');
512 ii
= opts_arr
.length
;
514 function isNumber(o
) {
515 return !isNaN(o
- 0) && o
!== null && o
!== '' && o
!== false && o
!== true;
519 if (typeof str
=== 'string') return $.trim(str
);
524 p
= opts_arr
[ii
].split(':');
525 p
= [p
[0], p
.slice(1).join(':')];
527 if (/true/i.test(p
[1])) p
[1] = true;
528 if (/false/i.test(p
[1])) p
[1] = false;
529 if (isNumber(p
[1])) {
530 if (p
[1].indexOf('.') === -1) {
531 p
[1] = parseInt(p
[1], 10);
533 p
[1] = parseFloat(p
[1]);
537 if (p
.length
=== 2 && p
[0].length
> 0) {
538 opts
[trim(p
[0])] = trim(p
[1]);
546 // Adds JS-recognizable media queries
549 // Media (String): Key string for the media query to be stored as in
550 // Foundation.media_queries
552 // Class (String): Class name for the generated <meta> tag
553 register_media: function (media
, media_class
) {
554 if (Foundation
.media_queries
[media
] === undefined) {
555 $('head').append('<meta class="' + media_class
+ '"/>');
556 Foundation
.media_queries
[media
] = removeQuotes($('.' + media_class
).css('font-family'));
561 // Add custom CSS within a JS-defined media query
564 // Rule (String): CSS rule to be appended to the document.
566 // Media (String): Optional media query string for the CSS rule to be
568 add_custom_rule: function (rule
, media
) {
569 if (media
=== undefined && Foundation
.stylesheet
) {
570 Foundation
.stylesheet
.insertRule(rule
, Foundation
.stylesheet
.cssRules
.length
);
572 var query
= Foundation
.media_queries
[media
];
574 if (query
!== undefined) {
575 Foundation
.stylesheet
.insertRule('@media ' +
576 Foundation
.media_queries
[media
] + '{ ' + rule
+ ' }');
582 // Performs a callback function when an image is fully loaded
585 // Image (jQuery Object): Image(s) to check if loaded.
587 // Callback (Function): Function to execute when image is fully loaded.
588 image_loaded: function (images
, callback
) {
590 unloaded
= images
.length
;
592 if (unloaded
=== 0) {
596 images
.each(function () {
597 single_image_loaded(self
.S(this), function () {
599 if (unloaded
=== 0) {
607 // Returns a random, alphanumeric string
610 // Length (Integer): Length of string to be generated. Defaults to random
614 // Rand (String): Pseudo-random, alphanumeric string.
615 random_str: function () {
616 if (!this.fidx
) this.fidx
= 0;
617 this.prefix
= this.prefix
|| [(this.name
|| 'F'), (+new Date
).toString(36)].join('-');
619 return this.prefix
+ (this.fidx
++).toString(36);
623 // Helper for window.matchMedia
626 // mq (String): Media query
629 // (Boolean): Whether the media query passes or not
630 match: function (mq
) {
631 return window
.matchMedia(mq
).matches
;
635 // Helpers for checking Foundation default media queries with JS
638 // (Boolean): Whether the media query passes or not
640 is_small_up: function () {
641 return this.match(Foundation
.media_queries
.small
);
644 is_medium_up: function () {
645 return this.match(Foundation
.media_queries
.medium
);
648 is_large_up: function () {
649 return this.match(Foundation
.media_queries
.large
);
652 is_xlarge_up: function () {
653 return this.match(Foundation
.media_queries
.xlarge
);
656 is_xxlarge_up: function () {
657 return this.match(Foundation
.media_queries
.xxlarge
);
660 is_small_only: function () {
661 return !this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
664 is_medium_only: function () {
665 return this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
668 is_large_only: function () {
669 return this.is_medium_up() && this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
672 is_xlarge_only: function () {
673 return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && !this.is_xxlarge_up();
676 is_xxlarge_only: function () {
677 return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && this.is_xxlarge_up();
682 $.fn
.foundation = function () {
683 var args
= Array
.prototype.slice
.call(arguments
, 0);
685 return this.each(function () {
686 Foundation
.init
.apply(Foundation
, [this].concat(args
));
691 }(jQuery
, window
, window
.document
));