foundation.js 16 KB

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