foundation.js 18 KB

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