foundation.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. /*
  2. * Foundation Responsive Library
  3. * http://foundation.zurb.com
  4. * Copyright 2015, ZURB
  5. * Free to use under the MIT license.
  6. * http://www.opensource.org/licenses/mit-license.php
  7. */
  8. (function ($, window, document, undefined) {
  9. 'use strict';
  10. var header_helpers = function (class_array) {
  11. var head = $('head');
  12. head.prepend($.map(class_array, function (class_name) {
  13. if (head.has('.' + class_name).length === 0) {
  14. return '<meta class="' + class_name + '" />';
  15. }
  16. }));
  17. };
  18. header_helpers([
  19. 'foundation-mq-small',
  20. 'foundation-mq-small-only',
  21. 'foundation-mq-medium',
  22. 'foundation-mq-medium-only',
  23. 'foundation-mq-large',
  24. 'foundation-mq-large-only',
  25. 'foundation-mq-xlarge',
  26. 'foundation-mq-xlarge-only',
  27. 'foundation-mq-xxlarge',
  28. 'foundation-data-attribute-namespace']);
  29. // Enable FastClick if present
  30. $(function () {
  31. if (typeof FastClick !== 'undefined') {
  32. // Don't attach to body if undefined
  33. if (typeof document.body !== 'undefined') {
  34. FastClick.attach(document.body);
  35. }
  36. }
  37. });
  38. // private Fast Selector wrapper,
  39. // returns jQuery object. Only use where
  40. // getElementById is not available.
  41. var S = function (selector, context) {
  42. if (typeof selector === 'string') {
  43. if (context) {
  44. var cont;
  45. if (context.jquery) {
  46. cont = context[0];
  47. if (!cont) {
  48. return context;
  49. }
  50. } else {
  51. cont = context;
  52. }
  53. return $(cont.querySelectorAll(selector));
  54. }
  55. return $(document.querySelectorAll(selector));
  56. }
  57. return $(selector, context);
  58. };
  59. // Namespace functions.
  60. var attr_name = function (init) {
  61. var arr = [];
  62. if (!init) {
  63. arr.push('data');
  64. }
  65. if (this.namespace.length > 0) {
  66. arr.push(this.namespace);
  67. }
  68. arr.push(this.name);
  69. return arr.join('-');
  70. };
  71. var add_namespace = function (str) {
  72. var parts = str.split('-'),
  73. i = parts.length,
  74. arr = [];
  75. while (i--) {
  76. if (i !== 0) {
  77. arr.push(parts[i]);
  78. } else {
  79. if (this.namespace.length > 0) {
  80. arr.push(this.namespace, parts[i]);
  81. } else {
  82. arr.push(parts[i]);
  83. }
  84. }
  85. }
  86. return arr.reverse().join('-');
  87. };
  88. // Event binding and data-options updating.
  89. var bindings = function (method, options) {
  90. var self = this,
  91. bind = function(){
  92. var $this = S(this),
  93. should_bind_events = !$this.data(self.attr_name(true) + '-init');
  94. $this.data(self.attr_name(true) + '-init', $.extend({}, self.settings, (options || method), self.data_options($this)));
  95. if (should_bind_events) {
  96. self.events(this);
  97. }
  98. };
  99. if (S(this.scope).is('[' + this.attr_name() +']')) {
  100. bind.call(this.scope);
  101. } else {
  102. S('[' + this.attr_name() +']', this.scope).each(bind);
  103. }
  104. // # 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.
  105. if (typeof method === 'string') {
  106. return this[method].call(this, options);
  107. }
  108. };
  109. var single_image_loaded = function (image, callback) {
  110. function loaded () {
  111. callback(image[0]);
  112. }
  113. function bindLoad () {
  114. this.one('load', loaded);
  115. if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
  116. var src = this.attr( 'src' ),
  117. param = src.match( /\?/ ) ? '&' : '?';
  118. param += 'random=' + (new Date()).getTime();
  119. this.attr('src', src + param);
  120. }
  121. }
  122. if (!image.attr('src')) {
  123. loaded();
  124. return;
  125. }
  126. if (image[0].complete || image[0].readyState === 4) {
  127. loaded();
  128. } else {
  129. bindLoad.call(image);
  130. }
  131. };
  132. /*! 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 */
  133. window.matchMedia || (window.matchMedia = function() {
  134. "use strict";
  135. // For browsers that support matchMedium api such as IE 9 and webkit
  136. var styleMedia = (window.styleMedia || window.media);
  137. // For those that don't support matchMedium
  138. if (!styleMedia) {
  139. var style = document.createElement('style'),
  140. script = document.getElementsByTagName('script')[0],
  141. info = null;
  142. style.type = 'text/css';
  143. style.id = 'matchmediajs-test';
  144. script.parentNode.insertBefore(style, script);
  145. // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
  146. info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle;
  147. styleMedia = {
  148. matchMedium: function(media) {
  149. var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
  150. // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
  151. if (style.styleSheet) {
  152. style.styleSheet.cssText = text;
  153. } else {
  154. style.textContent = text;
  155. }
  156. // Test if media query is true or false
  157. return info.width === '1px';
  158. }
  159. };
  160. }
  161. return function(media) {
  162. return {
  163. matches: styleMedia.matchMedium(media || 'all'),
  164. media: media || 'all'
  165. };
  166. };
  167. }());
  168. /*
  169. * jquery.requestAnimationFrame
  170. * https://github.com/gnarf37/jquery-requestAnimationFrame
  171. * Requires jQuery 1.8+
  172. *
  173. * Copyright (c) 2012 Corey Frang
  174. * Licensed under the MIT license.
  175. */
  176. (function(jQuery) {
  177. // requestAnimationFrame polyfill adapted from Erik Möller
  178. // fixes from Paul Irish and Tino Zijdel
  179. // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  180. // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
  181. var animating,
  182. lastTime = 0,
  183. vendors = ['webkit', 'moz'],
  184. requestAnimationFrame = window.requestAnimationFrame,
  185. cancelAnimationFrame = window.cancelAnimationFrame,
  186. jqueryFxAvailable = 'undefined' !== typeof jQuery.fx;
  187. for (; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
  188. requestAnimationFrame = window[ vendors[lastTime] + 'RequestAnimationFrame' ];
  189. cancelAnimationFrame = cancelAnimationFrame ||
  190. window[ vendors[lastTime] + 'CancelAnimationFrame' ] ||
  191. window[ vendors[lastTime] + 'CancelRequestAnimationFrame' ];
  192. }
  193. function raf() {
  194. if (animating) {
  195. requestAnimationFrame(raf);
  196. if (jqueryFxAvailable) {
  197. jQuery.fx.tick();
  198. }
  199. }
  200. }
  201. if (requestAnimationFrame) {
  202. // use rAF
  203. window.requestAnimationFrame = requestAnimationFrame;
  204. window.cancelAnimationFrame = cancelAnimationFrame;
  205. if (jqueryFxAvailable) {
  206. jQuery.fx.timer = function (timer) {
  207. if (timer() && jQuery.timers.push(timer) && !animating) {
  208. animating = true;
  209. raf();
  210. }
  211. };
  212. jQuery.fx.stop = function () {
  213. animating = false;
  214. };
  215. }
  216. } else {
  217. // polyfill
  218. window.requestAnimationFrame = function (callback) {
  219. var currTime = new Date().getTime(),
  220. timeToCall = Math.max(0, 16 - (currTime - lastTime)),
  221. id = window.setTimeout(function () {
  222. callback(currTime + timeToCall);
  223. }, timeToCall);
  224. lastTime = currTime + timeToCall;
  225. return id;
  226. };
  227. window.cancelAnimationFrame = function (id) {
  228. clearTimeout(id);
  229. };
  230. }
  231. }( $ ));
  232. function removeQuotes (string) {
  233. if (typeof string === 'string' || string instanceof String) {
  234. string = string.replace(/^['\\/"]+|(;\s?})+|['\\/"]+$/g, '');
  235. }
  236. return string;
  237. }
  238. function MediaQuery(selector) {
  239. this.selector = selector;
  240. this.query = '';
  241. }
  242. MediaQuery.prototype.toString = function () {
  243. return this.query || (this.query = S(this.selector).css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''));
  244. };
  245. window.Foundation = {
  246. name : 'Foundation',
  247. version : '5.5.3',
  248. media_queries : {
  249. 'small' : new MediaQuery('.foundation-mq-small'),
  250. 'small-only' : new MediaQuery('.foundation-mq-small-only'),
  251. 'medium' : new MediaQuery('.foundation-mq-medium'),
  252. 'medium-only' : new MediaQuery('.foundation-mq-medium-only'),
  253. 'large' : new MediaQuery('.foundation-mq-large'),
  254. 'large-only' : new MediaQuery('.foundation-mq-large-only'),
  255. 'xlarge' : new MediaQuery('.foundation-mq-xlarge'),
  256. 'xlarge-only' : new MediaQuery('.foundation-mq-xlarge-only'),
  257. 'xxlarge' : new MediaQuery('.foundation-mq-xxlarge')
  258. },
  259. stylesheet : $('<style></style>').appendTo('head')[0].sheet,
  260. global : {
  261. namespace : undefined
  262. },
  263. init : function (scope, libraries, method, options, response) {
  264. var args = [scope, method, options, response],
  265. responses = [];
  266. // check RTL
  267. this.rtl = /rtl/i.test(S('html').attr('dir'));
  268. // set foundation global scope
  269. this.scope = scope || this.scope;
  270. this.set_namespace();
  271. if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) {
  272. if (this.libs.hasOwnProperty(libraries)) {
  273. responses.push(this.init_lib(libraries, args));
  274. }
  275. } else {
  276. for (var lib in this.libs) {
  277. responses.push(this.init_lib(lib, libraries));
  278. }
  279. }
  280. S(window).load(function () {
  281. S(window)
  282. .trigger('resize.fndtn.clearing')
  283. .trigger('resize.fndtn.dropdown')
  284. .trigger('resize.fndtn.equalizer')
  285. .trigger('resize.fndtn.interchange')
  286. .trigger('resize.fndtn.joyride')
  287. .trigger('resize.fndtn.magellan')
  288. .trigger('resize.fndtn.topbar')
  289. .trigger('resize.fndtn.slider');
  290. });
  291. return scope;
  292. },
  293. init_lib : function (lib, args) {
  294. if (this.libs.hasOwnProperty(lib)) {
  295. this.patch(this.libs[lib]);
  296. if (args && args.hasOwnProperty(lib)) {
  297. if (typeof this.libs[lib].settings !== 'undefined') {
  298. $.extend(true, this.libs[lib].settings, args[lib]);
  299. } else if (typeof this.libs[lib].defaults !== 'undefined') {
  300. $.extend(true, this.libs[lib].defaults, args[lib]);
  301. }
  302. return this.libs[lib].init.apply(this.libs[lib], [this.scope, args[lib]]);
  303. }
  304. args = args instanceof Array ? args : new Array(args);
  305. return this.libs[lib].init.apply(this.libs[lib], args);
  306. }
  307. return function () {};
  308. },
  309. patch : function (lib) {
  310. lib.scope = this.scope;
  311. lib.namespace = this.global.namespace;
  312. lib.rtl = this.rtl;
  313. lib['data_options'] = this.utils.data_options;
  314. lib['attr_name'] = attr_name;
  315. lib['add_namespace'] = add_namespace;
  316. lib['bindings'] = bindings;
  317. lib['S'] = this.utils.S;
  318. },
  319. inherit : function (scope, methods) {
  320. var methods_arr = methods.split(' '),
  321. i = methods_arr.length;
  322. while (i--) {
  323. if (this.utils.hasOwnProperty(methods_arr[i])) {
  324. scope[methods_arr[i]] = this.utils[methods_arr[i]];
  325. }
  326. }
  327. },
  328. set_namespace : function () {
  329. // Description:
  330. // Don't bother reading the namespace out of the meta tag
  331. // if the namespace has been set globally in javascript
  332. //
  333. // Example:
  334. // Foundation.global.namespace = 'my-namespace';
  335. // or make it an empty string:
  336. // Foundation.global.namespace = '';
  337. //
  338. //
  339. // If the namespace has not been set (is undefined), try to read it out of the meta element.
  340. // Otherwise use the globally defined namespace, even if it's empty ('')
  341. var namespace = ( this.global.namespace === undefined ) ? $('.foundation-data-attribute-namespace').css('font-family') : this.global.namespace;
  342. // Finally, if the namsepace is either undefined or false, set it to an empty string.
  343. // Otherwise use the namespace value.
  344. this.global.namespace = ( namespace === undefined || /false/i.test(namespace) ) ? '' : namespace;
  345. },
  346. libs : {},
  347. // methods that can be inherited in libraries
  348. utils : {
  349. // Description:
  350. // Fast Selector wrapper returns jQuery object. Only use where getElementById
  351. // is not available.
  352. //
  353. // Arguments:
  354. // Selector (String): CSS selector describing the element(s) to be
  355. // returned as a jQuery object.
  356. //
  357. // Scope (String): CSS selector describing the area to be searched. Default
  358. // is document.
  359. //
  360. // Returns:
  361. // Element (jQuery Object): jQuery object containing elements matching the
  362. // selector within the scope.
  363. S : S,
  364. // Description:
  365. // Executes a function a max of once every n milliseconds
  366. //
  367. // Arguments:
  368. // Func (Function): Function to be throttled.
  369. //
  370. // Delay (Integer): Function execution threshold in milliseconds.
  371. //
  372. // Returns:
  373. // Lazy_function (Function): Function with throttling applied.
  374. throttle : function (func, delay) {
  375. var timer = null;
  376. return function () {
  377. var context = this, args = arguments;
  378. if (timer == null) {
  379. timer = setTimeout(function () {
  380. func.apply(context, args);
  381. timer = null;
  382. }, delay);
  383. }
  384. };
  385. },
  386. // Description:
  387. // Executes a function when it stops being invoked for n seconds
  388. // Modified version of _.debounce() http://underscorejs.org
  389. //
  390. // Arguments:
  391. // Func (Function): Function to be debounced.
  392. //
  393. // Delay (Integer): Function execution threshold in milliseconds.
  394. //
  395. // Immediate (Bool): Whether the function should be called at the beginning
  396. // of the delay instead of the end. Default is false.
  397. //
  398. // Returns:
  399. // Lazy_function (Function): Function with debouncing applied.
  400. debounce : function (func, delay, immediate) {
  401. var timeout, result;
  402. return function () {
  403. var context = this, args = arguments;
  404. var later = function () {
  405. timeout = null;
  406. if (!immediate) {
  407. result = func.apply(context, args);
  408. }
  409. };
  410. var callNow = immediate && !timeout;
  411. clearTimeout(timeout);
  412. timeout = setTimeout(later, delay);
  413. if (callNow) {
  414. result = func.apply(context, args);
  415. }
  416. return result;
  417. };
  418. },
  419. // Description:
  420. // Parses data-options attribute
  421. //
  422. // Arguments:
  423. // El (jQuery Object): Element to be parsed.
  424. //
  425. // Returns:
  426. // Options (Javascript Object): Contents of the element's data-options
  427. // attribute.
  428. data_options : function (el, data_attr_name) {
  429. data_attr_name = data_attr_name || 'options';
  430. var opts = {}, ii, p, opts_arr,
  431. data_options = function (el) {
  432. var namespace = Foundation.global.namespace;
  433. if (namespace.length > 0) {
  434. return el.data(namespace + '-' + data_attr_name);
  435. }
  436. return el.data(data_attr_name);
  437. };
  438. var cached_options = data_options(el);
  439. if (typeof cached_options === 'object') {
  440. return cached_options;
  441. }
  442. opts_arr = (cached_options || ':').split(';');
  443. ii = opts_arr.length;
  444. function isNumber (o) {
  445. return !isNaN (o - 0) && o !== null && o !== '' && o !== false && o !== true;
  446. }
  447. function trim (str) {
  448. if (typeof str === 'string') {
  449. return $.trim(str);
  450. }
  451. return str;
  452. }
  453. while (ii--) {
  454. p = opts_arr[ii].split(':');
  455. p = [p[0], p.slice(1).join(':')];
  456. if (/true/i.test(p[1])) {
  457. p[1] = true;
  458. }
  459. if (/false/i.test(p[1])) {
  460. p[1] = false;
  461. }
  462. if (isNumber(p[1])) {
  463. if (p[1].indexOf('.') === -1) {
  464. p[1] = parseInt(p[1], 10);
  465. } else {
  466. p[1] = parseFloat(p[1]);
  467. }
  468. }
  469. if (p.length === 2 && p[0].length > 0) {
  470. opts[trim(p[0])] = trim(p[1]);
  471. }
  472. }
  473. return opts;
  474. },
  475. // Description:
  476. // Adds JS-recognizable media queries
  477. //
  478. // Arguments:
  479. // Media (String): Key string for the media query to be stored as in
  480. // Foundation.media_queries
  481. //
  482. // Class (String): Class name for the generated <meta> tag
  483. register_media : function (media, media_class) {
  484. if (Foundation.media_queries[media] === undefined) {
  485. $('head').append('<meta class="' + media_class + '"/>');
  486. Foundation.media_queries[media] = removeQuotes($('.' + media_class).css('font-family'));
  487. }
  488. },
  489. // Description:
  490. // Add custom CSS within a JS-defined media query
  491. //
  492. // Arguments:
  493. // Rule (String): CSS rule to be appended to the document.
  494. //
  495. // Media (String): Optional media query string for the CSS rule to be
  496. // nested under.
  497. add_custom_rule : function (rule, media) {
  498. if (media === undefined && Foundation.stylesheet) {
  499. Foundation.stylesheet.insertRule(rule, Foundation.stylesheet.cssRules.length);
  500. } else {
  501. var query = Foundation.media_queries[media];
  502. if (query !== undefined) {
  503. Foundation.stylesheet.insertRule('@media ' +
  504. Foundation.media_queries[media] + '{ ' + rule + ' }', Foundation.stylesheet.cssRules.length);
  505. }
  506. }
  507. },
  508. // Description:
  509. // Performs a callback function when an image is fully loaded
  510. //
  511. // Arguments:
  512. // Image (jQuery Object): Image(s) to check if loaded.
  513. //
  514. // Callback (Function): Function to execute when image is fully loaded.
  515. image_loaded : function (images, callback) {
  516. var self = this,
  517. unloaded = images.length;
  518. function pictures_has_height(images) {
  519. var pictures_number = images.length;
  520. for (var i = pictures_number - 1; i >= 0; i--) {
  521. if(images.attr('height') === undefined) {
  522. return false;
  523. };
  524. };
  525. return true;
  526. }
  527. if (unloaded === 0 || pictures_has_height(images)) {
  528. callback(images);
  529. }
  530. images.each(function () {
  531. single_image_loaded(self.S(this), function () {
  532. unloaded -= 1;
  533. if (unloaded === 0) {
  534. callback(images);
  535. }
  536. });
  537. });
  538. },
  539. // Description:
  540. // Returns a random, alphanumeric string
  541. //
  542. // Arguments:
  543. // Length (Integer): Length of string to be generated. Defaults to random
  544. // integer.
  545. //
  546. // Returns:
  547. // Rand (String): Pseudo-random, alphanumeric string.
  548. random_str : function () {
  549. if (!this.fidx) {
  550. this.fidx = 0;
  551. }
  552. this.prefix = this.prefix || [(this.name || 'F'), (+new Date).toString(36)].join('-');
  553. return this.prefix + (this.fidx++).toString(36);
  554. },
  555. // Description:
  556. // Helper for window.matchMedia
  557. //
  558. // Arguments:
  559. // mq (String): Media query
  560. //
  561. // Returns:
  562. // (Boolean): Whether the media query passes or not
  563. match : function (mq) {
  564. return window.matchMedia(mq).matches;
  565. },
  566. // Description:
  567. // Helpers for checking Foundation default media queries with JS
  568. //
  569. // Returns:
  570. // (Boolean): Whether the media query passes or not
  571. is_small_up : function () {
  572. return this.match(Foundation.media_queries.small);
  573. },
  574. is_medium_up : function () {
  575. return this.match(Foundation.media_queries.medium);
  576. },
  577. is_large_up : function () {
  578. return this.match(Foundation.media_queries.large);
  579. },
  580. is_xlarge_up : function () {
  581. return this.match(Foundation.media_queries.xlarge);
  582. },
  583. is_xxlarge_up : function () {
  584. return this.match(Foundation.media_queries.xxlarge);
  585. },
  586. is_small_only : function () {
  587. return !this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
  588. },
  589. is_medium_only : function () {
  590. return this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
  591. },
  592. is_large_only : function () {
  593. return this.is_medium_up() && this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
  594. },
  595. is_xlarge_only : function () {
  596. return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && !this.is_xxlarge_up();
  597. },
  598. is_xxlarge_only : function () {
  599. return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && this.is_xxlarge_up();
  600. }
  601. }
  602. };
  603. $.fn.foundation = function () {
  604. var args = Array.prototype.slice.call(arguments, 0);
  605. return this.each(function () {
  606. Foundation.init.apply(Foundation, [this].concat(args));
  607. return this;
  608. });
  609. };
  610. }(jQuery, window, window.document));