In this repo i store all my websites, each in a different branch
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

1974 Zeilen
62 KiB

  1. !function ($) {
  2. "use strict";
  3. var FOUNDATION_VERSION = '6.3.1';
  4. // Global Foundation object
  5. // This is attached to the window, or used as a module for AMD/Browserify
  6. var Foundation = {
  7. version: FOUNDATION_VERSION,
  8. /**
  9. * Stores initialized plugins.
  10. */
  11. _plugins: {},
  12. /**
  13. * Stores generated unique ids for plugin instances
  14. */
  15. _uuids: [],
  16. /**
  17. * Returns a boolean for RTL support
  18. */
  19. rtl: function () {
  20. return $('html').attr('dir') === 'rtl';
  21. },
  22. /**
  23. * Defines a Foundation plugin, adding it to the `Foundation` namespace and the list of plugins to initialize when reflowing.
  24. * @param {Object} plugin - The constructor of the plugin.
  25. */
  26. plugin: function (plugin, name) {
  27. // Object key to use when adding to global Foundation object
  28. // Examples: Foundation.Reveal, Foundation.OffCanvas
  29. var className = name || functionName(plugin);
  30. // Object key to use when storing the plugin, also used to create the identifying data attribute for the plugin
  31. // Examples: data-reveal, data-off-canvas
  32. var attrName = hyphenate(className);
  33. // Add to the Foundation object and the plugins list (for reflowing)
  34. this._plugins[attrName] = this[className] = plugin;
  35. },
  36. /**
  37. * @function
  38. * Populates the _uuids array with pointers to each individual plugin instance.
  39. * Adds the `zfPlugin` data-attribute to programmatically created plugins to allow use of $(selector).foundation(method) calls.
  40. * Also fires the initialization event for each plugin, consolidating repetitive code.
  41. * @param {Object} plugin - an instance of a plugin, usually `this` in context.
  42. * @param {String} name - the name of the plugin, passed as a camelCased string.
  43. * @fires Plugin#init
  44. */
  45. registerPlugin: function (plugin, name) {
  46. var pluginName = name ? hyphenate(name) : functionName(plugin.constructor).toLowerCase();
  47. plugin.uuid = this.GetYoDigits(6, pluginName);
  48. if (!plugin.$element.attr('data-' + pluginName)) {
  49. plugin.$element.attr('data-' + pluginName, plugin.uuid);
  50. }
  51. if (!plugin.$element.data('zfPlugin')) {
  52. plugin.$element.data('zfPlugin', plugin);
  53. }
  54. /**
  55. * Fires when the plugin has initialized.
  56. * @event Plugin#init
  57. */
  58. plugin.$element.trigger('init.zf.' + pluginName);
  59. this._uuids.push(plugin.uuid);
  60. return;
  61. },
  62. /**
  63. * @function
  64. * Removes the plugins uuid from the _uuids array.
  65. * Removes the zfPlugin data attribute, as well as the data-plugin-name attribute.
  66. * Also fires the destroyed event for the plugin, consolidating repetitive code.
  67. * @param {Object} plugin - an instance of a plugin, usually `this` in context.
  68. * @fires Plugin#destroyed
  69. */
  70. unregisterPlugin: function (plugin) {
  71. var pluginName = hyphenate(functionName(plugin.$element.data('zfPlugin').constructor));
  72. this._uuids.splice(this._uuids.indexOf(plugin.uuid), 1);
  73. plugin.$element.removeAttr('data-' + pluginName).removeData('zfPlugin')
  74. /**
  75. * Fires when the plugin has been destroyed.
  76. * @event Plugin#destroyed
  77. */
  78. .trigger('destroyed.zf.' + pluginName);
  79. for (var prop in plugin) {
  80. plugin[prop] = null; //clean up script to prep for garbage collection.
  81. }
  82. return;
  83. },
  84. /**
  85. * @function
  86. * Causes one or more active plugins to re-initialize, resetting event listeners, recalculating positions, etc.
  87. * @param {String} plugins - optional string of an individual plugin key, attained by calling `$(element).data('pluginName')`, or string of a plugin class i.e. `'dropdown'`
  88. * @default If no argument is passed, reflow all currently active plugins.
  89. */
  90. reInit: function (plugins) {
  91. var isJQ = plugins instanceof $;
  92. try {
  93. if (isJQ) {
  94. plugins.each(function () {
  95. $(this).data('zfPlugin')._init();
  96. });
  97. } else {
  98. var type = typeof plugins,
  99. _this = this,
  100. fns = {
  101. 'object': function (plgs) {
  102. plgs.forEach(function (p) {
  103. p = hyphenate(p);
  104. $('[data-' + p + ']').foundation('_init');
  105. });
  106. },
  107. 'string': function () {
  108. plugins = hyphenate(plugins);
  109. $('[data-' + plugins + ']').foundation('_init');
  110. },
  111. 'undefined': function () {
  112. this['object'](Object.keys(_this._plugins));
  113. }
  114. };
  115. fns[type](plugins);
  116. }
  117. } catch (err) {
  118. console.error(err);
  119. } finally {
  120. return plugins;
  121. }
  122. },
  123. /**
  124. * returns a random base-36 uid with namespacing
  125. * @function
  126. * @param {Number} length - number of random base-36 digits desired. Increase for more random strings.
  127. * @param {String} namespace - name of plugin to be incorporated in uid, optional.
  128. * @default {String} '' - if no plugin name is provided, nothing is appended to the uid.
  129. * @returns {String} - unique id
  130. */
  131. GetYoDigits: function (length, namespace) {
  132. length = length || 6;
  133. return Math.round(Math.pow(36, length + 1) - Math.random() * Math.pow(36, length)).toString(36).slice(1) + (namespace ? '-' + namespace : '');
  134. },
  135. /**
  136. * Initialize plugins on any elements within `elem` (and `elem` itself) that aren't already initialized.
  137. * @param {Object} elem - jQuery object containing the element to check inside. Also checks the element itself, unless it's the `document` object.
  138. * @param {String|Array} plugins - A list of plugins to initialize. Leave this out to initialize everything.
  139. */
  140. reflow: function (elem, plugins) {
  141. // If plugins is undefined, just grab everything
  142. if (typeof plugins === 'undefined') {
  143. plugins = Object.keys(this._plugins);
  144. }
  145. // If plugins is a string, convert it to an array with one item
  146. else if (typeof plugins === 'string') {
  147. plugins = [plugins];
  148. }
  149. var _this = this;
  150. // Iterate through each plugin
  151. $.each(plugins, function (i, name) {
  152. // Get the current plugin
  153. var plugin = _this._plugins[name];
  154. // Localize the search to all elements inside elem, as well as elem itself, unless elem === document
  155. var $elem = $(elem).find('[data-' + name + ']').addBack('[data-' + name + ']');
  156. // For each plugin found, initialize it
  157. $elem.each(function () {
  158. var $el = $(this),
  159. opts = {};
  160. // Don't double-dip on plugins
  161. if ($el.data('zfPlugin')) {
  162. console.warn("Tried to initialize " + name + " on an element that already has a Foundation plugin.");
  163. return;
  164. }
  165. if ($el.attr('data-options')) {
  166. var thing = $el.attr('data-options').split(';').forEach(function (e, i) {
  167. var opt = e.split(':').map(function (el) {
  168. return el.trim();
  169. });
  170. if (opt[0]) opts[opt[0]] = parseValue(opt[1]);
  171. });
  172. }
  173. try {
  174. $el.data('zfPlugin', new plugin($(this), opts));
  175. } catch (er) {
  176. console.error(er);
  177. } finally {
  178. return;
  179. }
  180. });
  181. });
  182. },
  183. getFnName: functionName,
  184. transitionend: function ($elem) {
  185. var transitions = {
  186. 'transition': 'transitionend',
  187. 'WebkitTransition': 'webkitTransitionEnd',
  188. 'MozTransition': 'transitionend',
  189. 'OTransition': 'otransitionend'
  190. };
  191. var elem = document.createElement('div'),
  192. end;
  193. for (var t in transitions) {
  194. if (typeof elem.style[t] !== 'undefined') {
  195. end = transitions[t];
  196. }
  197. }
  198. if (end) {
  199. return end;
  200. } else {
  201. end = setTimeout(function () {
  202. $elem.triggerHandler('transitionend', [$elem]);
  203. }, 1);
  204. return 'transitionend';
  205. }
  206. }
  207. };
  208. Foundation.util = {
  209. /**
  210. * Function for applying a debounce effect to a function call.
  211. * @function
  212. * @param {Function} func - Function to be called at end of timeout.
  213. * @param {Number} delay - Time in ms to delay the call of `func`.
  214. * @returns function
  215. */
  216. throttle: function (func, delay) {
  217. var timer = null;
  218. return function () {
  219. var context = this,
  220. args = arguments;
  221. if (timer === null) {
  222. timer = setTimeout(function () {
  223. func.apply(context, args);
  224. timer = null;
  225. }, delay);
  226. }
  227. };
  228. }
  229. };
  230. // TODO: consider not making this a jQuery function
  231. // TODO: need way to reflow vs. re-initialize
  232. /**
  233. * The Foundation jQuery method.
  234. * @param {String|Array} method - An action to perform on the current jQuery object.
  235. */
  236. var foundation = function (method) {
  237. var type = typeof method,
  238. $meta = $('meta.foundation-mq'),
  239. $noJS = $('.no-js');
  240. if (!$meta.length) {
  241. $('<meta class="foundation-mq">').appendTo(document.head);
  242. }
  243. if ($noJS.length) {
  244. $noJS.removeClass('no-js');
  245. }
  246. if (type === 'undefined') {
  247. //needs to initialize the Foundation object, or an individual plugin.
  248. Foundation.MediaQuery._init();
  249. Foundation.reflow(this);
  250. } else if (type === 'string') {
  251. //an individual method to invoke on a plugin or group of plugins
  252. var args = Array.prototype.slice.call(arguments, 1); //collect all the arguments, if necessary
  253. var plugClass = this.data('zfPlugin'); //determine the class of plugin
  254. if (plugClass !== undefined && plugClass[method] !== undefined) {
  255. //make sure both the class and method exist
  256. if (this.length === 1) {
  257. //if there's only one, call it directly.
  258. plugClass[method].apply(plugClass, args);
  259. } else {
  260. this.each(function (i, el) {
  261. //otherwise loop through the jQuery collection and invoke the method on each
  262. plugClass[method].apply($(el).data('zfPlugin'), args);
  263. });
  264. }
  265. } else {
  266. //error for no class or no method
  267. throw new ReferenceError("We're sorry, '" + method + "' is not an available method for " + (plugClass ? functionName(plugClass) : 'this element') + '.');
  268. }
  269. } else {
  270. //error for invalid argument type
  271. throw new TypeError('We\'re sorry, ' + type + ' is not a valid parameter. You must use a string representing the method you wish to invoke.');
  272. }
  273. return this;
  274. };
  275. window.Foundation = Foundation;
  276. $.fn.foundation = foundation;
  277. // Polyfill for requestAnimationFrame
  278. (function () {
  279. if (!Date.now || !window.Date.now) window.Date.now = Date.now = function () {
  280. return new Date().getTime();
  281. };
  282. var vendors = ['webkit', 'moz'];
  283. for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
  284. var vp = vendors[i];
  285. window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];
  286. window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];
  287. }
  288. if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
  289. var lastTime = 0;
  290. window.requestAnimationFrame = function (callback) {
  291. var now = Date.now();
  292. var nextTime = Math.max(lastTime + 16, now);
  293. return setTimeout(function () {
  294. callback(lastTime = nextTime);
  295. }, nextTime - now);
  296. };
  297. window.cancelAnimationFrame = clearTimeout;
  298. }
  299. /**
  300. * Polyfill for performance.now, required by rAF
  301. */
  302. if (!window.performance || !window.performance.now) {
  303. window.performance = {
  304. start: Date.now(),
  305. now: function () {
  306. return Date.now() - this.start;
  307. }
  308. };
  309. }
  310. })();
  311. if (!Function.prototype.bind) {
  312. Function.prototype.bind = function (oThis) {
  313. if (typeof this !== 'function') {
  314. // closest thing possible to the ECMAScript 5
  315. // internal IsCallable function
  316. throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  317. }
  318. var aArgs = Array.prototype.slice.call(arguments, 1),
  319. fToBind = this,
  320. fNOP = function () {},
  321. fBound = function () {
  322. return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
  323. };
  324. if (this.prototype) {
  325. // native functions don't have a prototype
  326. fNOP.prototype = this.prototype;
  327. }
  328. fBound.prototype = new fNOP();
  329. return fBound;
  330. };
  331. }
  332. // Polyfill to get the name of a function in IE9
  333. function functionName(fn) {
  334. if (Function.prototype.name === undefined) {
  335. var funcNameRegex = /function\s([^(]{1,})\(/;
  336. var results = funcNameRegex.exec(fn.toString());
  337. return results && results.length > 1 ? results[1].trim() : "";
  338. } else if (fn.prototype === undefined) {
  339. return fn.constructor.name;
  340. } else {
  341. return fn.prototype.constructor.name;
  342. }
  343. }
  344. function parseValue(str) {
  345. if ('true' === str) return true;else if ('false' === str) return false;else if (!isNaN(str * 1)) return parseFloat(str);
  346. return str;
  347. }
  348. // Convert PascalCase to kebab-case
  349. // Thank you: http://stackoverflow.com/a/8955580
  350. function hyphenate(str) {
  351. return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
  352. }
  353. }(jQuery);
  354. 'use strict';
  355. !function ($) {
  356. // Default set of media queries
  357. var defaultQueries = {
  358. 'default': 'only screen',
  359. landscape: 'only screen and (orientation: landscape)',
  360. portrait: 'only screen and (orientation: portrait)',
  361. retina: 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 'only screen and (min--moz-device-pixel-ratio: 2),' + 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 'only screen and (min-device-pixel-ratio: 2),' + 'only screen and (min-resolution: 192dpi),' + 'only screen and (min-resolution: 2dppx)'
  362. };
  363. var MediaQuery = {
  364. queries: [],
  365. current: '',
  366. /**
  367. * Initializes the media query helper, by extracting the breakpoint list from the CSS and activating the breakpoint watcher.
  368. * @function
  369. * @private
  370. */
  371. _init: function () {
  372. var self = this;
  373. var extractedStyles = $('.foundation-mq').css('font-family');
  374. var namedQueries;
  375. namedQueries = parseStyleToObject(extractedStyles);
  376. for (var key in namedQueries) {
  377. if (namedQueries.hasOwnProperty(key)) {
  378. self.queries.push({
  379. name: key,
  380. value: 'only screen and (min-width: ' + namedQueries[key] + ')'
  381. });
  382. }
  383. }
  384. this.current = this._getCurrentSize();
  385. this._watcher();
  386. },
  387. /**
  388. * Checks if the screen is at least as wide as a breakpoint.
  389. * @function
  390. * @param {String} size - Name of the breakpoint to check.
  391. * @returns {Boolean} `true` if the breakpoint matches, `false` if it's smaller.
  392. */
  393. atLeast: function (size) {
  394. var query = this.get(size);
  395. if (query) {
  396. return window.matchMedia(query).matches;
  397. }
  398. return false;
  399. },
  400. /**
  401. * Checks if the screen matches to a breakpoint.
  402. * @function
  403. * @param {String} size - Name of the breakpoint to check, either 'small only' or 'small'. Omitting 'only' falls back to using atLeast() method.
  404. * @returns {Boolean} `true` if the breakpoint matches, `false` if it does not.
  405. */
  406. is: function (size) {
  407. size = size.trim().split(' ');
  408. if (size.length > 1 && size[1] === 'only') {
  409. if (size[0] === this._getCurrentSize()) return true;
  410. } else {
  411. return this.atLeast(size[0]);
  412. }
  413. return false;
  414. },
  415. /**
  416. * Gets the media query of a breakpoint.
  417. * @function
  418. * @param {String} size - Name of the breakpoint to get.
  419. * @returns {String|null} - The media query of the breakpoint, or `null` if the breakpoint doesn't exist.
  420. */
  421. get: function (size) {
  422. for (var i in this.queries) {
  423. if (this.queries.hasOwnProperty(i)) {
  424. var query = this.queries[i];
  425. if (size === query.name) return query.value;
  426. }
  427. }
  428. return null;
  429. },
  430. /**
  431. * Gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one).
  432. * @function
  433. * @private
  434. * @returns {String} Name of the current breakpoint.
  435. */
  436. _getCurrentSize: function () {
  437. var matched;
  438. for (var i = 0; i < this.queries.length; i++) {
  439. var query = this.queries[i];
  440. if (window.matchMedia(query.value).matches) {
  441. matched = query;
  442. }
  443. }
  444. if (typeof matched === 'object') {
  445. return matched.name;
  446. } else {
  447. return matched;
  448. }
  449. },
  450. /**
  451. * Activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes.
  452. * @function
  453. * @private
  454. */
  455. _watcher: function () {
  456. var _this = this;
  457. $(window).on('resize.zf.mediaquery', function () {
  458. var newSize = _this._getCurrentSize(),
  459. currentSize = _this.current;
  460. if (newSize !== currentSize) {
  461. // Change the current media query
  462. _this.current = newSize;
  463. // Broadcast the media query change on the window
  464. $(window).trigger('changed.zf.mediaquery', [newSize, currentSize]);
  465. }
  466. });
  467. }
  468. };
  469. Foundation.MediaQuery = MediaQuery;
  470. // matchMedia() polyfill - Test a CSS media type/query in JS.
  471. // Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license
  472. window.matchMedia || (window.matchMedia = function () {
  473. 'use strict';
  474. // For browsers that support matchMedium api such as IE 9 and webkit
  475. var styleMedia = window.styleMedia || window.media;
  476. // For those that don't support matchMedium
  477. if (!styleMedia) {
  478. var style = document.createElement('style'),
  479. script = document.getElementsByTagName('script')[0],
  480. info = null;
  481. style.type = 'text/css';
  482. style.id = 'matchmediajs-test';
  483. script && script.parentNode && script.parentNode.insertBefore(style, script);
  484. // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
  485. info = 'getComputedStyle' in window && window.getComputedStyle(style, null) || style.currentStyle;
  486. styleMedia = {
  487. matchMedium: function (media) {
  488. var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
  489. // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
  490. if (style.styleSheet) {
  491. style.styleSheet.cssText = text;
  492. } else {
  493. style.textContent = text;
  494. }
  495. // Test if media query is true or false
  496. return info.width === '1px';
  497. }
  498. };
  499. }
  500. return function (media) {
  501. return {
  502. matches: styleMedia.matchMedium(media || 'all'),
  503. media: media || 'all'
  504. };
  505. };
  506. }());
  507. // Thank you: https://github.com/sindresorhus/query-string
  508. function parseStyleToObject(str) {
  509. var styleObject = {};
  510. if (typeof str !== 'string') {
  511. return styleObject;
  512. }
  513. str = str.trim().slice(1, -1); // browsers re-quote string style values
  514. if (!str) {
  515. return styleObject;
  516. }
  517. styleObject = str.split('&').reduce(function (ret, param) {
  518. var parts = param.replace(/\+/g, ' ').split('=');
  519. var key = parts[0];
  520. var val = parts[1];
  521. key = decodeURIComponent(key);
  522. // missing `=` should be `null`:
  523. // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
  524. val = val === undefined ? null : decodeURIComponent(val);
  525. if (!ret.hasOwnProperty(key)) {
  526. ret[key] = val;
  527. } else if (Array.isArray(ret[key])) {
  528. ret[key].push(val);
  529. } else {
  530. ret[key] = [ret[key], val];
  531. }
  532. return ret;
  533. }, {});
  534. return styleObject;
  535. }
  536. Foundation.MediaQuery = MediaQuery;
  537. }(jQuery);
  538. 'use strict';
  539. !function ($) {
  540. Foundation.Box = {
  541. ImNotTouchingYou: ImNotTouchingYou,
  542. GetDimensions: GetDimensions,
  543. GetOffsets: GetOffsets
  544. };
  545. /**
  546. * Compares the dimensions of an element to a container and determines collision events with container.
  547. * @function
  548. * @param {jQuery} element - jQuery object to test for collisions.
  549. * @param {jQuery} parent - jQuery object to use as bounding container.
  550. * @param {Boolean} lrOnly - set to true to check left and right values only.
  551. * @param {Boolean} tbOnly - set to true to check top and bottom values only.
  552. * @default if no parent object passed, detects collisions with `window`.
  553. * @returns {Boolean} - true if collision free, false if a collision in any direction.
  554. */
  555. function ImNotTouchingYou(element, parent, lrOnly, tbOnly) {
  556. var eleDims = GetDimensions(element),
  557. top,
  558. bottom,
  559. left,
  560. right;
  561. if (parent) {
  562. var parDims = GetDimensions(parent);
  563. bottom = eleDims.offset.top + eleDims.height <= parDims.height + parDims.offset.top;
  564. top = eleDims.offset.top >= parDims.offset.top;
  565. left = eleDims.offset.left >= parDims.offset.left;
  566. right = eleDims.offset.left + eleDims.width <= parDims.width + parDims.offset.left;
  567. } else {
  568. bottom = eleDims.offset.top + eleDims.height <= eleDims.windowDims.height + eleDims.windowDims.offset.top;
  569. top = eleDims.offset.top >= eleDims.windowDims.offset.top;
  570. left = eleDims.offset.left >= eleDims.windowDims.offset.left;
  571. right = eleDims.offset.left + eleDims.width <= eleDims.windowDims.width;
  572. }
  573. var allDirs = [bottom, top, left, right];
  574. if (lrOnly) {
  575. return left === right === true;
  576. }
  577. if (tbOnly) {
  578. return top === bottom === true;
  579. }
  580. return allDirs.indexOf(false) === -1;
  581. };
  582. /**
  583. * Uses native methods to return an object of dimension values.
  584. * @function
  585. * @param {jQuery || HTML} element - jQuery object or DOM element for which to get the dimensions. Can be any element other that document or window.
  586. * @returns {Object} - nested object of integer pixel values
  587. * TODO - if element is window, return only those values.
  588. */
  589. function GetDimensions(elem, test) {
  590. elem = elem.length ? elem[0] : elem;
  591. if (elem === window || elem === document) {
  592. throw new Error("I'm sorry, Dave. I'm afraid I can't do that.");
  593. }
  594. var rect = elem.getBoundingClientRect(),
  595. parRect = elem.parentNode.getBoundingClientRect(),
  596. winRect = document.body.getBoundingClientRect(),
  597. winY = window.pageYOffset,
  598. winX = window.pageXOffset;
  599. return {
  600. width: rect.width,
  601. height: rect.height,
  602. offset: {
  603. top: rect.top + winY,
  604. left: rect.left + winX
  605. },
  606. parentDims: {
  607. width: parRect.width,
  608. height: parRect.height,
  609. offset: {
  610. top: parRect.top + winY,
  611. left: parRect.left + winX
  612. }
  613. },
  614. windowDims: {
  615. width: winRect.width,
  616. height: winRect.height,
  617. offset: {
  618. top: winY,
  619. left: winX
  620. }
  621. }
  622. };
  623. }
  624. /**
  625. * Returns an object of top and left integer pixel values for dynamically rendered elements,
  626. * such as: Tooltip, Reveal, and Dropdown
  627. * @function
  628. * @param {jQuery} element - jQuery object for the element being positioned.
  629. * @param {jQuery} anchor - jQuery object for the element's anchor point.
  630. * @param {String} position - a string relating to the desired position of the element, relative to it's anchor
  631. * @param {Number} vOffset - integer pixel value of desired vertical separation between anchor and element.
  632. * @param {Number} hOffset - integer pixel value of desired horizontal separation between anchor and element.
  633. * @param {Boolean} isOverflow - if a collision event is detected, sets to true to default the element to full width - any desired offset.
  634. * TODO alter/rewrite to work with `em` values as well/instead of pixels
  635. */
  636. function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
  637. var $eleDims = GetDimensions(element),
  638. $anchorDims = anchor ? GetDimensions(anchor) : null;
  639. switch (position) {
  640. case 'top':
  641. return {
  642. left: Foundation.rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left,
  643. top: $anchorDims.offset.top - ($eleDims.height + vOffset)
  644. };
  645. break;
  646. case 'left':
  647. return {
  648. left: $anchorDims.offset.left - ($eleDims.width + hOffset),
  649. top: $anchorDims.offset.top
  650. };
  651. break;
  652. case 'right':
  653. return {
  654. left: $anchorDims.offset.left + $anchorDims.width + hOffset,
  655. top: $anchorDims.offset.top
  656. };
  657. break;
  658. case 'center top':
  659. return {
  660. left: $anchorDims.offset.left + $anchorDims.width / 2 - $eleDims.width / 2,
  661. top: $anchorDims.offset.top - ($eleDims.height + vOffset)
  662. };
  663. break;
  664. case 'center bottom':
  665. return {
  666. left: isOverflow ? hOffset : $anchorDims.offset.left + $anchorDims.width / 2 - $eleDims.width / 2,
  667. top: $anchorDims.offset.top + $anchorDims.height + vOffset
  668. };
  669. break;
  670. case 'center left':
  671. return {
  672. left: $anchorDims.offset.left - ($eleDims.width + hOffset),
  673. top: $anchorDims.offset.top + $anchorDims.height / 2 - $eleDims.height / 2
  674. };
  675. break;
  676. case 'center right':
  677. return {
  678. left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1,
  679. top: $anchorDims.offset.top + $anchorDims.height / 2 - $eleDims.height / 2
  680. };
  681. break;
  682. case 'center':
  683. return {
  684. left: $eleDims.windowDims.offset.left + $eleDims.windowDims.width / 2 - $eleDims.width / 2,
  685. top: $eleDims.windowDims.offset.top + $eleDims.windowDims.height / 2 - $eleDims.height / 2
  686. };
  687. break;
  688. case 'reveal':
  689. return {
  690. left: ($eleDims.windowDims.width - $eleDims.width) / 2,
  691. top: $eleDims.windowDims.offset.top + vOffset
  692. };
  693. case 'reveal full':
  694. return {
  695. left: $eleDims.windowDims.offset.left,
  696. top: $eleDims.windowDims.offset.top
  697. };
  698. break;
  699. case 'left bottom':
  700. return {
  701. left: $anchorDims.offset.left,
  702. top: $anchorDims.offset.top + $anchorDims.height + vOffset
  703. };
  704. break;
  705. case 'right bottom':
  706. return {
  707. left: $anchorDims.offset.left + $anchorDims.width + hOffset - $eleDims.width,
  708. top: $anchorDims.offset.top + $anchorDims.height + vOffset
  709. };
  710. break;
  711. default:
  712. return {
  713. left: Foundation.rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left + hOffset,
  714. top: $anchorDims.offset.top + $anchorDims.height + vOffset
  715. };
  716. }
  717. }
  718. }(jQuery);
  719. 'use strict';
  720. !function ($) {
  721. /**
  722. * Motion module.
  723. * @module foundation.motion
  724. */
  725. var initClasses = ['mui-enter', 'mui-leave'];
  726. var activeClasses = ['mui-enter-active', 'mui-leave-active'];
  727. var Motion = {
  728. animateIn: function (element, animation, cb) {
  729. animate(true, element, animation, cb);
  730. },
  731. animateOut: function (element, animation, cb) {
  732. animate(false, element, animation, cb);
  733. }
  734. };
  735. function Move(duration, elem, fn) {
  736. var anim,
  737. prog,
  738. start = null;
  739. // console.log('called');
  740. if (duration === 0) {
  741. fn.apply(elem);
  742. elem.trigger('finished.zf.animate', [elem]).triggerHandler('finished.zf.animate', [elem]);
  743. return;
  744. }
  745. function move(ts) {
  746. if (!start) start = ts;
  747. // console.log(start, ts);
  748. prog = ts - start;
  749. fn.apply(elem);
  750. if (prog < duration) {
  751. anim = window.requestAnimationFrame(move, elem);
  752. } else {
  753. window.cancelAnimationFrame(anim);
  754. elem.trigger('finished.zf.animate', [elem]).triggerHandler('finished.zf.animate', [elem]);
  755. }
  756. }
  757. anim = window.requestAnimationFrame(move);
  758. }
  759. /**
  760. * Animates an element in or out using a CSS transition class.
  761. * @function
  762. * @private
  763. * @param {Boolean} isIn - Defines if the animation is in or out.
  764. * @param {Object} element - jQuery or HTML object to animate.
  765. * @param {String} animation - CSS class to use.
  766. * @param {Function} cb - Callback to run when animation is finished.
  767. */
  768. function animate(isIn, element, animation, cb) {
  769. element = $(element).eq(0);
  770. if (!element.length) return;
  771. var initClass = isIn ? initClasses[0] : initClasses[1];
  772. var activeClass = isIn ? activeClasses[0] : activeClasses[1];
  773. // Set up the animation
  774. reset();
  775. element.addClass(animation).css('transition', 'none');
  776. requestAnimationFrame(function () {
  777. element.addClass(initClass);
  778. if (isIn) element.show();
  779. });
  780. // Start the animation
  781. requestAnimationFrame(function () {
  782. element[0].offsetWidth;
  783. element.css('transition', '').addClass(activeClass);
  784. });
  785. // Clean up the animation when it finishes
  786. element.one(Foundation.transitionend(element), finish);
  787. // Hides the element (for out animations), resets the element, and runs a callback
  788. function finish() {
  789. if (!isIn) element.hide();
  790. reset();
  791. if (cb) cb.apply(element);
  792. }
  793. // Resets transitions and removes motion-specific classes
  794. function reset() {
  795. element[0].style.transitionDuration = 0;
  796. element.removeClass(initClass + ' ' + activeClass + ' ' + animation);
  797. }
  798. }
  799. Foundation.Move = Move;
  800. Foundation.Motion = Motion;
  801. }(jQuery);
  802. 'use strict';
  803. !function ($) {
  804. var MutationObserver = function () {
  805. var prefixes = ['WebKit', 'Moz', 'O', 'Ms', ''];
  806. for (var i = 0; i < prefixes.length; i++) {
  807. if (prefixes[i] + 'MutationObserver' in window) {
  808. return window[prefixes[i] + 'MutationObserver'];
  809. }
  810. }
  811. return false;
  812. }();
  813. var triggers = function (el, type) {
  814. el.data(type).split(' ').forEach(function (id) {
  815. $('#' + id)[type === 'close' ? 'trigger' : 'triggerHandler'](type + '.zf.trigger', [el]);
  816. });
  817. };
  818. // Elements with [data-open] will reveal a plugin that supports it when clicked.
  819. $(document).on('click.zf.trigger', '[data-open]', function () {
  820. triggers($(this), 'open');
  821. });
  822. // Elements with [data-close] will close a plugin that supports it when clicked.
  823. // If used without a value on [data-close], the event will bubble, allowing it to close a parent component.
  824. $(document).on('click.zf.trigger', '[data-close]', function () {
  825. var id = $(this).data('close');
  826. if (id) {
  827. triggers($(this), 'close');
  828. } else {
  829. $(this).trigger('close.zf.trigger');
  830. }
  831. });
  832. // Elements with [data-toggle] will toggle a plugin that supports it when clicked.
  833. $(document).on('click.zf.trigger', '[data-toggle]', function () {
  834. var id = $(this).data('toggle');
  835. if (id) {
  836. triggers($(this), 'toggle');
  837. } else {
  838. $(this).trigger('toggle.zf.trigger');
  839. }
  840. });
  841. // Elements with [data-closable] will respond to close.zf.trigger events.
  842. $(document).on('close.zf.trigger', '[data-closable]', function (e) {
  843. e.stopPropagation();
  844. var animation = $(this).data('closable');
  845. if (animation !== '') {
  846. Foundation.Motion.animateOut($(this), animation, function () {
  847. $(this).trigger('closed.zf');
  848. });
  849. } else {
  850. $(this).fadeOut().trigger('closed.zf');
  851. }
  852. });
  853. $(document).on('focus.zf.trigger blur.zf.trigger', '[data-toggle-focus]', function () {
  854. var id = $(this).data('toggle-focus');
  855. $('#' + id).triggerHandler('toggle.zf.trigger', [$(this)]);
  856. });
  857. /**
  858. * Fires once after all other scripts have loaded
  859. * @function
  860. * @private
  861. */
  862. $(window).on('load', function () {
  863. checkListeners();
  864. });
  865. function checkListeners() {
  866. eventsListener();
  867. resizeListener();
  868. scrollListener();
  869. mutateListener();
  870. closemeListener();
  871. }
  872. //******** only fires this function once on load, if there's something to watch ********
  873. function closemeListener(pluginName) {
  874. var yetiBoxes = $('[data-yeti-box]'),
  875. plugNames = ['dropdown', 'tooltip', 'reveal'];
  876. if (pluginName) {
  877. if (typeof pluginName === 'string') {
  878. plugNames.push(pluginName);
  879. } else if (typeof pluginName === 'object' && typeof pluginName[0] === 'string') {
  880. plugNames.concat(pluginName);
  881. } else {
  882. console.error('Plugin names must be strings');
  883. }
  884. }
  885. if (yetiBoxes.length) {
  886. var listeners = plugNames.map(function (name) {
  887. return 'closeme.zf.' + name;
  888. }).join(' ');
  889. $(window).off(listeners).on(listeners, function (e, pluginId) {
  890. var plugin = e.namespace.split('.')[0];
  891. var plugins = $('[data-' + plugin + ']').not('[data-yeti-box="' + pluginId + '"]');
  892. plugins.each(function () {
  893. var _this = $(this);
  894. _this.triggerHandler('close.zf.trigger', [_this]);
  895. });
  896. });
  897. }
  898. }
  899. function resizeListener(debounce) {
  900. var timer = void 0,
  901. $nodes = $('[data-resize]');
  902. if ($nodes.length) {
  903. $(window).off('resize.zf.trigger').on('resize.zf.trigger', function (e) {
  904. if (timer) {
  905. clearTimeout(timer);
  906. }
  907. timer = setTimeout(function () {
  908. if (!MutationObserver) {
  909. //fallback for IE 9
  910. $nodes.each(function () {
  911. $(this).triggerHandler('resizeme.zf.trigger');
  912. });
  913. }
  914. //trigger all listening elements and signal a resize event
  915. $nodes.attr('data-events', "resize");
  916. }, debounce || 10); //default time to emit resize event
  917. });
  918. }
  919. }
  920. function scrollListener(debounce) {
  921. var timer = void 0,
  922. $nodes = $('[data-scroll]');
  923. if ($nodes.length) {
  924. $(window).off('scroll.zf.trigger').on('scroll.zf.trigger', function (e) {
  925. if (timer) {
  926. clearTimeout(timer);
  927. }
  928. timer = setTimeout(function () {
  929. if (!MutationObserver) {
  930. //fallback for IE 9
  931. $nodes.each(function () {
  932. $(this).triggerHandler('scrollme.zf.trigger');
  933. });
  934. }
  935. //trigger all listening elements and signal a scroll event
  936. $nodes.attr('data-events', "scroll");
  937. }, debounce || 10); //default time to emit scroll event
  938. });
  939. }
  940. }
  941. function mutateListener(debounce) {
  942. var $nodes = $('[data-mutate]');
  943. if ($nodes.length && MutationObserver) {
  944. //trigger all listening elements and signal a mutate event
  945. //no IE 9 or 10
  946. $nodes.each(function () {
  947. $(this).triggerHandler('mutateme.zf.trigger');
  948. });
  949. }
  950. }
  951. function eventsListener() {
  952. if (!MutationObserver) {
  953. return false;
  954. }
  955. var nodes = document.querySelectorAll('[data-resize], [data-scroll], [data-mutate]');
  956. //element callback
  957. var listeningElementsMutation = function (mutationRecordsList) {
  958. var $target = $(mutationRecordsList[0].target);
  959. //trigger the event handler for the element depending on type
  960. switch (mutationRecordsList[0].type) {
  961. case "attributes":
  962. if ($target.attr("data-events") === "scroll" && mutationRecordsList[0].attributeName === "data-events") {
  963. $target.triggerHandler('scrollme.zf.trigger', [$target, window.pageYOffset]);
  964. }
  965. if ($target.attr("data-events") === "resize" && mutationRecordsList[0].attributeName === "data-events") {
  966. $target.triggerHandler('resizeme.zf.trigger', [$target]);
  967. }
  968. if (mutationRecordsList[0].attributeName === "style") {
  969. $target.closest("[data-mutate]").attr("data-events", "mutate");
  970. $target.closest("[data-mutate]").triggerHandler('mutateme.zf.trigger', [$target.closest("[data-mutate]")]);
  971. }
  972. break;
  973. case "childList":
  974. $target.closest("[data-mutate]").attr("data-events", "mutate");
  975. $target.closest("[data-mutate]").triggerHandler('mutateme.zf.trigger', [$target.closest("[data-mutate]")]);
  976. break;
  977. default:
  978. return false;
  979. //nothing
  980. }
  981. };
  982. if (nodes.length) {
  983. //for each element that needs to listen for resizing, scrolling, or mutation add a single observer
  984. for (var i = 0; i <= nodes.length - 1; i++) {
  985. var elementObserver = new MutationObserver(listeningElementsMutation);
  986. elementObserver.observe(nodes[i], { attributes: true, childList: true, characterData: false, subtree: true, attributeFilter: ["data-events", "style"] });
  987. }
  988. }
  989. }
  990. // ------------------------------------
  991. // [PH]
  992. // Foundation.CheckWatchers = checkWatchers;
  993. Foundation.IHearYou = checkListeners;
  994. // Foundation.ISeeYou = scrollListener;
  995. // Foundation.IFeelYou = closemeListener;
  996. }(jQuery);
  997. // function domMutationObserver(debounce) {
  998. // // !!! This is coming soon and needs more work; not active !!! //
  999. // var timer,
  1000. // nodes = document.querySelectorAll('[data-mutate]');
  1001. // //
  1002. // if (nodes.length) {
  1003. // // var MutationObserver = (function () {
  1004. // // var prefixes = ['WebKit', 'Moz', 'O', 'Ms', ''];
  1005. // // for (var i=0; i < prefixes.length; i++) {
  1006. // // if (prefixes[i] + 'MutationObserver' in window) {
  1007. // // return window[prefixes[i] + 'MutationObserver'];
  1008. // // }
  1009. // // }
  1010. // // return false;
  1011. // // }());
  1012. //
  1013. //
  1014. // //for the body, we need to listen for all changes effecting the style and class attributes
  1015. // var bodyObserver = new MutationObserver(bodyMutation);
  1016. // bodyObserver.observe(document.body, { attributes: true, childList: true, characterData: false, subtree:true, attributeFilter:["style", "class"]});
  1017. //
  1018. //
  1019. // //body callback
  1020. // function bodyMutation(mutate) {
  1021. // //trigger all listening elements and signal a mutation event
  1022. // if (timer) { clearTimeout(timer); }
  1023. //
  1024. // timer = setTimeout(function() {
  1025. // bodyObserver.disconnect();
  1026. // $('[data-mutate]').attr('data-events',"mutate");
  1027. // }, debounce || 150);
  1028. // }
  1029. // }
  1030. // }
  1031. /*******************************************
  1032. * *
  1033. * This util was created by Marius Olbertz *
  1034. * Please thank Marius on GitHub /owlbertz *
  1035. * or the web http://www.mariusolbertz.de/ *
  1036. * *
  1037. ******************************************/
  1038. 'use strict';
  1039. !function ($) {
  1040. var keyCodes = {
  1041. 9: 'TAB',
  1042. 13: 'ENTER',
  1043. 27: 'ESCAPE',
  1044. 32: 'SPACE',
  1045. 37: 'ARROW_LEFT',
  1046. 38: 'ARROW_UP',
  1047. 39: 'ARROW_RIGHT',
  1048. 40: 'ARROW_DOWN'
  1049. };
  1050. var commands = {};
  1051. var Keyboard = {
  1052. keys: getKeyCodes(keyCodes),
  1053. /**
  1054. * Parses the (keyboard) event and returns a String that represents its key
  1055. * Can be used like Foundation.parseKey(event) === Foundation.keys.SPACE
  1056. * @param {Event} event - the event generated by the event handler
  1057. * @return String key - String that represents the key pressed
  1058. */
  1059. parseKey: function (event) {
  1060. var key = keyCodes[event.which || event.keyCode] || String.fromCharCode(event.which).toUpperCase();
  1061. // Remove un-printable characters, e.g. for `fromCharCode` calls for CTRL only events
  1062. key = key.replace(/\W+/, '');
  1063. if (event.shiftKey) key = 'SHIFT_' + key;
  1064. if (event.ctrlKey) key = 'CTRL_' + key;
  1065. if (event.altKey) key = 'ALT_' + key;
  1066. // Remove trailing underscore, in case only modifiers were used (e.g. only `CTRL_ALT`)
  1067. key = key.replace(/_$/, '');
  1068. return key;
  1069. },
  1070. /**
  1071. * Handles the given (keyboard) event
  1072. * @param {Event} event - the event generated by the event handler
  1073. * @param {String} component - Foundation component's name, e.g. Slider or Reveal
  1074. * @param {Objects} functions - collection of functions that are to be executed
  1075. */
  1076. handleKey: function (event, component, functions) {
  1077. var commandList = commands[component],
  1078. keyCode = this.parseKey(event),
  1079. cmds,
  1080. command,
  1081. fn;
  1082. if (!commandList) return console.warn('Component not defined!');
  1083. if (typeof commandList.ltr === 'undefined') {
  1084. // this component does not differentiate between ltr and rtl
  1085. cmds = commandList; // use plain list
  1086. } else {
  1087. // merge ltr and rtl: if document is rtl, rtl overwrites ltr and vice versa
  1088. if (Foundation.rtl()) cmds = $.extend({}, commandList.ltr, commandList.rtl);else cmds = $.extend({}, commandList.rtl, commandList.ltr);
  1089. }
  1090. command = cmds[keyCode];
  1091. fn = functions[command];
  1092. if (fn && typeof fn === 'function') {
  1093. // execute function if exists
  1094. var returnValue = fn.apply();
  1095. if (functions.handled || typeof functions.handled === 'function') {
  1096. // execute function when event was handled
  1097. functions.handled(returnValue);
  1098. }
  1099. } else {
  1100. if (functions.unhandled || typeof functions.unhandled === 'function') {
  1101. // execute function when event was not handled
  1102. functions.unhandled();
  1103. }
  1104. }
  1105. },
  1106. /**
  1107. * Finds all focusable elements within the given `$element`
  1108. * @param {jQuery} $element - jQuery object to search within
  1109. * @return {jQuery} $focusable - all focusable elements within `$element`
  1110. */
  1111. findFocusable: function ($element) {
  1112. if (!$element) {
  1113. return false;
  1114. }
  1115. return $element.find('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]').filter(function () {
  1116. if (!$(this).is(':visible') || $(this).attr('tabindex') < 0) {
  1117. return false;
  1118. } //only have visible elements and those that have a tabindex greater or equal 0
  1119. return true;
  1120. });
  1121. },
  1122. /**
  1123. * Returns the component name name
  1124. * @param {Object} component - Foundation component, e.g. Slider or Reveal
  1125. * @return String componentName
  1126. */
  1127. register: function (componentName, cmds) {
  1128. commands[componentName] = cmds;
  1129. },
  1130. /**
  1131. * Traps the focus in the given element.
  1132. * @param {jQuery} $element jQuery object to trap the foucs into.
  1133. */
  1134. trapFocus: function ($element) {
  1135. var $focusable = Foundation.Keyboard.findFocusable($element),
  1136. $firstFocusable = $focusable.eq(0),
  1137. $lastFocusable = $focusable.eq(-1);
  1138. $element.on('keydown.zf.trapfocus', function (event) {
  1139. if (event.target === $lastFocusable[0] && Foundation.Keyboard.parseKey(event) === 'TAB') {
  1140. event.preventDefault();
  1141. $firstFocusable.focus();
  1142. } else if (event.target === $firstFocusable[0] && Foundation.Keyboard.parseKey(event) === 'SHIFT_TAB') {
  1143. event.preventDefault();
  1144. $lastFocusable.focus();
  1145. }
  1146. });
  1147. },
  1148. /**
  1149. * Releases the trapped focus from the given element.
  1150. * @param {jQuery} $element jQuery object to release the focus for.
  1151. */
  1152. releaseFocus: function ($element) {
  1153. $element.off('keydown.zf.trapfocus');
  1154. }
  1155. };
  1156. /*
  1157. * Constants for easier comparing.
  1158. * Can be used like Foundation.parseKey(event) === Foundation.keys.SPACE
  1159. */
  1160. function getKeyCodes(kcs) {
  1161. var k = {};
  1162. for (var kc in kcs) {
  1163. k[kcs[kc]] = kcs[kc];
  1164. }return k;
  1165. }
  1166. Foundation.Keyboard = Keyboard;
  1167. }(jQuery);
  1168. 'use strict';
  1169. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  1170. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  1171. !function ($) {
  1172. /**
  1173. * Reveal module.
  1174. * @module foundation.reveal
  1175. * @requires foundation.util.keyboard
  1176. * @requires foundation.util.box
  1177. * @requires foundation.util.triggers
  1178. * @requires foundation.util.mediaQuery
  1179. * @requires foundation.util.motion if using animations
  1180. */
  1181. var Reveal = function () {
  1182. /**
  1183. * Creates a new instance of Reveal.
  1184. * @class
  1185. * @param {jQuery} element - jQuery object to use for the modal.
  1186. * @param {Object} options - optional parameters.
  1187. */
  1188. function Reveal(element, options) {
  1189. _classCallCheck(this, Reveal);
  1190. this.$element = element;
  1191. this.options = $.extend({}, Reveal.defaults, this.$element.data(), options);
  1192. this._init();
  1193. Foundation.registerPlugin(this, 'Reveal');
  1194. Foundation.Keyboard.register('Reveal', {
  1195. 'ENTER': 'open',
  1196. 'SPACE': 'open',
  1197. 'ESCAPE': 'close'
  1198. });
  1199. }
  1200. /**
  1201. * Initializes the modal by adding the overlay and close buttons, (if selected).
  1202. * @private
  1203. */
  1204. _createClass(Reveal, [{
  1205. key: '_init',
  1206. value: function _init() {
  1207. this.id = this.$element.attr('id');
  1208. this.isActive = false;
  1209. this.cached = { mq: Foundation.MediaQuery.current };
  1210. this.isMobile = mobileSniff();
  1211. this.$anchor = $('[data-open="' + this.id + '"]').length ? $('[data-open="' + this.id + '"]') : $('[data-toggle="' + this.id + '"]');
  1212. this.$anchor.attr({
  1213. 'aria-controls': this.id,
  1214. 'aria-haspopup': true,
  1215. 'tabindex': 0
  1216. });
  1217. if (this.options.fullScreen || this.$element.hasClass('full')) {
  1218. this.options.fullScreen = true;
  1219. this.options.overlay = false;
  1220. }
  1221. if (this.options.overlay && !this.$overlay) {
  1222. this.$overlay = this._makeOverlay(this.id);
  1223. }
  1224. this.$element.attr({
  1225. 'role': 'dialog',
  1226. 'aria-hidden': true,
  1227. 'data-yeti-box': this.id,
  1228. 'data-resize': this.id
  1229. });
  1230. if (this.$overlay) {
  1231. this.$element.detach().appendTo(this.$overlay);
  1232. } else {
  1233. this.$element.detach().appendTo($(this.options.appendTo));
  1234. this.$element.addClass('without-overlay');
  1235. }
  1236. this._events();
  1237. if (this.options.deepLink && window.location.hash === '#' + this.id) {
  1238. $(window).one('load.zf.reveal', this.open.bind(this));
  1239. }
  1240. }
  1241. /**
  1242. * Creates an overlay div to display behind the modal.
  1243. * @private
  1244. */
  1245. }, {
  1246. key: '_makeOverlay',
  1247. value: function _makeOverlay() {
  1248. return $('<div></div>').addClass('reveal-overlay').appendTo(this.options.appendTo);
  1249. }
  1250. /**
  1251. * Updates position of modal
  1252. * TODO: Figure out if we actually need to cache these values or if it doesn't matter
  1253. * @private
  1254. */
  1255. }, {
  1256. key: '_updatePosition',
  1257. value: function _updatePosition() {
  1258. var width = this.$element.outerWidth();
  1259. var outerWidth = $(window).width();
  1260. var height = this.$element.outerHeight();
  1261. var outerHeight = $(window).height();
  1262. var left, top;
  1263. if (this.options.hOffset === 'auto') {
  1264. left = parseInt((outerWidth - width) / 2, 10);
  1265. } else {
  1266. left = parseInt(this.options.hOffset, 10);
  1267. }
  1268. if (this.options.vOffset === 'auto') {
  1269. if (height > outerHeight) {
  1270. top = parseInt(Math.min(100, outerHeight / 10), 10);
  1271. } else {
  1272. top = parseInt((outerHeight - height) / 4, 10);
  1273. }
  1274. } else {
  1275. top = parseInt(this.options.vOffset, 10);
  1276. }
  1277. this.$element.css({ top: top + 'px' });
  1278. // only worry about left if we don't have an overlay or we havea horizontal offset,
  1279. // otherwise we're perfectly in the middle
  1280. if (!this.$overlay || this.options.hOffset !== 'auto') {
  1281. this.$element.css({ left: left + 'px' });
  1282. this.$element.css({ margin: '0px' });
  1283. }
  1284. }
  1285. /**
  1286. * Adds event handlers for the modal.
  1287. * @private
  1288. */
  1289. }, {
  1290. key: '_events',
  1291. value: function _events() {
  1292. var _this2 = this;
  1293. var _this = this;
  1294. this.$element.on({
  1295. 'open.zf.trigger': this.open.bind(this),
  1296. 'close.zf.trigger': function (event, $element) {
  1297. if (event.target === _this.$element[0] || $(event.target).parents('[data-closable]')[0] === $element) {
  1298. // only close reveal when it's explicitly called
  1299. return _this2.close.apply(_this2);
  1300. }
  1301. },
  1302. 'toggle.zf.trigger': this.toggle.bind(this),
  1303. 'resizeme.zf.trigger': function () {
  1304. _this._updatePosition();
  1305. }
  1306. });
  1307. if (this.$anchor.length) {
  1308. this.$anchor.on('keydown.zf.reveal', function (e) {
  1309. if (e.which === 13 || e.which === 32) {
  1310. e.stopPropagation();
  1311. e.preventDefault();
  1312. _this.open();
  1313. }
  1314. });
  1315. }
  1316. if (this.options.closeOnClick && this.options.overlay) {
  1317. this.$overlay.off('.zf.reveal').on('click.zf.reveal', function (e) {
  1318. if (e.target === _this.$element[0] || $.contains(_this.$element[0], e.target) || !$.contains(document, e.target)) {
  1319. return;
  1320. }
  1321. _this.close();
  1322. });
  1323. }
  1324. if (this.options.deepLink) {
  1325. $(window).on('popstate.zf.reveal:' + this.id, this._handleState.bind(this));
  1326. }
  1327. }
  1328. /**
  1329. * Handles modal methods on back/forward button clicks or any other event that triggers popstate.
  1330. * @private
  1331. */
  1332. }, {
  1333. key: '_handleState',
  1334. value: function _handleState(e) {
  1335. if (window.location.hash === '#' + this.id && !this.isActive) {
  1336. this.open();
  1337. } else {
  1338. this.close();
  1339. }
  1340. }
  1341. /**
  1342. * Opens the modal controlled by `this.$anchor`, and closes all others by default.
  1343. * @function
  1344. * @fires Reveal#closeme
  1345. * @fires Reveal#open
  1346. */
  1347. }, {
  1348. key: 'open',
  1349. value: function open() {
  1350. var _this3 = this;
  1351. if (this.options.deepLink) {
  1352. var hash = '#' + this.id;
  1353. if (window.history.pushState) {
  1354. window.history.pushState(null, null, hash);
  1355. } else {
  1356. window.location.hash = hash;
  1357. }
  1358. }
  1359. this.isActive = true;
  1360. // Make elements invisible, but remove display: none so we can get size and positioning
  1361. this.$element.css({ 'visibility': 'hidden' }).show().scrollTop(0);
  1362. if (this.options.overlay) {
  1363. this.$overlay.css({ 'visibility': 'hidden' }).show();
  1364. }
  1365. this._updatePosition();
  1366. this.$element.hide().css({ 'visibility': '' });
  1367. if (this.$overlay) {
  1368. this.$overlay.css({ 'visibility': '' }).hide();
  1369. if (this.$element.hasClass('fast')) {
  1370. this.$overlay.addClass('fast');
  1371. } else if (this.$element.hasClass('slow')) {
  1372. this.$overlay.addClass('slow');
  1373. }
  1374. }
  1375. if (!this.options.multipleOpened) {
  1376. /**
  1377. * Fires immediately before the modal opens.
  1378. * Closes any other modals that are currently open
  1379. * @event Reveal#closeme
  1380. */
  1381. this.$element.trigger('closeme.zf.reveal', this.id);
  1382. }
  1383. var _this = this;
  1384. function addRevealOpenClasses() {
  1385. if (_this.isMobile) {
  1386. if (!_this.originalScrollPos) {
  1387. _this.originalScrollPos = window.pageYOffset;
  1388. }
  1389. $('html, body').addClass('is-reveal-open');
  1390. } else {
  1391. $('body').addClass('is-reveal-open');
  1392. }
  1393. }
  1394. // Motion UI method of reveal
  1395. if (this.options.animationIn) {
  1396. (function () {
  1397. var afterAnimation = function () {
  1398. _this.$element.attr({
  1399. 'aria-hidden': false,
  1400. 'tabindex': -1
  1401. }).focus();
  1402. addRevealOpenClasses();
  1403. Foundation.Keyboard.trapFocus(_this.$element);
  1404. };
  1405. if (_this3.options.overlay) {
  1406. Foundation.Motion.animateIn(_this3.$overlay, 'fade-in');
  1407. }
  1408. Foundation.Motion.animateIn(_this3.$element, _this3.options.animationIn, function () {
  1409. if (_this3.$element) {
  1410. // protect against object having been removed
  1411. _this3.focusableElements = Foundation.Keyboard.findFocusable(_this3.$element);
  1412. afterAnimation();
  1413. }
  1414. });
  1415. })();
  1416. }
  1417. // jQuery method of reveal
  1418. else {
  1419. if (this.options.overlay) {
  1420. this.$overlay.show(0);
  1421. }
  1422. this.$element.show(this.options.showDelay);
  1423. }
  1424. // handle accessibility
  1425. this.$element.attr({
  1426. 'aria-hidden': false,
  1427. 'tabindex': -1
  1428. }).focus();
  1429. Foundation.Keyboard.trapFocus(this.$element);
  1430. /**
  1431. * Fires when the modal has successfully opened.
  1432. * @event Reveal#open
  1433. */
  1434. this.$element.trigger('open.zf.reveal');
  1435. addRevealOpenClasses();
  1436. setTimeout(function () {
  1437. _this3._extraHandlers();
  1438. }, 0);
  1439. }
  1440. /**
  1441. * Adds extra event handlers for the body and window if necessary.
  1442. * @private
  1443. */
  1444. }, {
  1445. key: '_extraHandlers',
  1446. value: function _extraHandlers() {
  1447. var _this = this;
  1448. if (!this.$element) {
  1449. return;
  1450. } // If we're in the middle of cleanup, don't freak out
  1451. this.focusableElements = Foundation.Keyboard.findFocusable(this.$element);
  1452. if (!this.options.overlay && this.options.closeOnClick && !this.options.fullScreen) {
  1453. $('body').on('click.zf.reveal', function (e) {
  1454. if (e.target === _this.$element[0] || $.contains(_this.$element[0], e.target) || !$.contains(document, e.target)) {
  1455. return;
  1456. }
  1457. _this.close();
  1458. });
  1459. }
  1460. if (this.options.closeOnEsc) {
  1461. $(window).on('keydown.zf.reveal', function (e) {
  1462. Foundation.Keyboard.handleKey(e, 'Reveal', {
  1463. close: function () {
  1464. if (_this.options.closeOnEsc) {
  1465. _this.close();
  1466. _this.$anchor.focus();
  1467. }
  1468. }
  1469. });
  1470. });
  1471. }
  1472. // lock focus within modal while tabbing
  1473. this.$element.on('keydown.zf.reveal', function (e) {
  1474. var $target = $(this);
  1475. // handle keyboard event with keyboard util
  1476. Foundation.Keyboard.handleKey(e, 'Reveal', {
  1477. open: function () {
  1478. if (_this.$element.find(':focus').is(_this.$element.find('[data-close]'))) {
  1479. setTimeout(function () {
  1480. // set focus back to anchor if close button has been activated
  1481. _this.$anchor.focus();
  1482. }, 1);
  1483. } else if ($target.is(_this.focusableElements)) {
  1484. // dont't trigger if acual element has focus (i.e. inputs, links, ...)
  1485. _this.open();
  1486. }
  1487. },
  1488. close: function () {
  1489. if (_this.options.closeOnEsc) {
  1490. _this.close();
  1491. _this.$anchor.focus();
  1492. }
  1493. },
  1494. handled: function (preventDefault) {
  1495. if (preventDefault) {
  1496. e.preventDefault();
  1497. }
  1498. }
  1499. });
  1500. });
  1501. }
  1502. /**
  1503. * Closes the modal.
  1504. * @function
  1505. * @fires Reveal#closed
  1506. */
  1507. }, {
  1508. key: 'close',
  1509. value: function close() {
  1510. if (!this.isActive || !this.$element.is(':visible')) {
  1511. return false;
  1512. }
  1513. var _this = this;
  1514. // Motion UI method of hiding
  1515. if (this.options.animationOut) {
  1516. if (this.options.overlay) {
  1517. Foundation.Motion.animateOut(this.$overlay, 'fade-out', finishUp);
  1518. } else {
  1519. finishUp();
  1520. }
  1521. Foundation.Motion.animateOut(this.$element, this.options.animationOut);
  1522. }
  1523. // jQuery method of hiding
  1524. else {
  1525. if (this.options.overlay) {
  1526. this.$overlay.hide(0, finishUp);
  1527. } else {
  1528. finishUp();
  1529. }
  1530. this.$element.hide(this.options.hideDelay);
  1531. }
  1532. // Conditionals to remove extra event listeners added on open
  1533. if (this.options.closeOnEsc) {
  1534. $(window).off('keydown.zf.reveal');
  1535. }
  1536. if (!this.options.overlay && this.options.closeOnClick) {
  1537. $('body').off('click.zf.reveal');
  1538. }
  1539. this.$element.off('keydown.zf.reveal');
  1540. function finishUp() {
  1541. if (_this.isMobile) {
  1542. $('html, body').removeClass('is-reveal-open');
  1543. if (_this.originalScrollPos) {
  1544. $('body').scrollTop(_this.originalScrollPos);
  1545. _this.originalScrollPos = null;
  1546. }
  1547. } else {
  1548. $('body').removeClass('is-reveal-open');
  1549. }
  1550. Foundation.Keyboard.releaseFocus(_this.$element);
  1551. _this.$element.attr('aria-hidden', true);
  1552. /**
  1553. * Fires when the modal is done closing.
  1554. * @event Reveal#closed
  1555. */
  1556. _this.$element.trigger('closed.zf.reveal');
  1557. }
  1558. /**
  1559. * Resets the modal content
  1560. * This prevents a running video to keep going in the background
  1561. */
  1562. if (this.options.resetOnClose) {
  1563. this.$element.html(this.$element.html());
  1564. }
  1565. this.isActive = false;
  1566. if (_this.options.deepLink) {
  1567. if (window.history.replaceState) {
  1568. window.history.replaceState('', document.title, window.location.href.replace('#' + this.id, ''));
  1569. } else {
  1570. window.location.hash = '';
  1571. }
  1572. }
  1573. }
  1574. /**
  1575. * Toggles the open/closed state of a modal.
  1576. * @function
  1577. */
  1578. }, {
  1579. key: 'toggle',
  1580. value: function toggle() {
  1581. if (this.isActive) {
  1582. this.close();
  1583. } else {
  1584. this.open();
  1585. }
  1586. }
  1587. }, {
  1588. key: 'destroy',
  1589. /**
  1590. * Destroys an instance of a modal.
  1591. * @function
  1592. */
  1593. value: function destroy() {
  1594. if (this.options.overlay) {
  1595. this.$element.appendTo($(this.options.appendTo)); // move $element outside of $overlay to prevent error unregisterPlugin()
  1596. this.$overlay.hide().off().remove();
  1597. }
  1598. this.$element.hide().off();
  1599. this.$anchor.off('.zf');
  1600. $(window).off('.zf.reveal:' + this.id);
  1601. Foundation.unregisterPlugin(this);
  1602. }
  1603. }]);
  1604. return Reveal;
  1605. }();
  1606. Reveal.defaults = {
  1607. /**
  1608. * Motion-UI class to use for animated elements. If none used, defaults to simple show/hide.
  1609. * @option
  1610. * @type {string}
  1611. * @default ''
  1612. */
  1613. animationIn: '',
  1614. /**
  1615. * Motion-UI class to use for animated elements. If none used, defaults to simple show/hide.
  1616. * @option
  1617. * @type {string}
  1618. * @default ''
  1619. */
  1620. animationOut: '',
  1621. /**
  1622. * Time, in ms, to delay the opening of a modal after a click if no animation used.
  1623. * @option
  1624. * @type {number}
  1625. * @default 0
  1626. */
  1627. showDelay: 0,
  1628. /**
  1629. * Time, in ms, to delay the closing of a modal after a click if no animation used.
  1630. * @option
  1631. * @type {number}
  1632. * @default 0
  1633. */
  1634. hideDelay: 0,
  1635. /**
  1636. * Allows a click on the body/overlay to close the modal.
  1637. * @option
  1638. * @type {boolean}
  1639. * @default true
  1640. */
  1641. closeOnClick: true,
  1642. /**
  1643. * Allows the modal to close if the user presses the `ESCAPE` key.
  1644. * @option
  1645. * @type {boolean}
  1646. * @default true
  1647. */
  1648. closeOnEsc: true,
  1649. /**
  1650. * If true, allows multiple modals to be displayed at once.
  1651. * @option
  1652. * @type {boolean}
  1653. * @default false
  1654. */
  1655. multipleOpened: false,
  1656. /**
  1657. * Distance, in pixels, the modal should push down from the top of the screen.
  1658. * @option
  1659. * @type {number|string}
  1660. * @default auto
  1661. */
  1662. vOffset: 'auto',
  1663. /**
  1664. * Distance, in pixels, the modal should push in from the side of the screen.
  1665. * @option
  1666. * @type {number|string}
  1667. * @default auto
  1668. */
  1669. hOffset: 'auto',
  1670. /**
  1671. * Allows the modal to be fullscreen, completely blocking out the rest of the view. JS checks for this as well.
  1672. * @option
  1673. * @type {boolean}
  1674. * @default false
  1675. */
  1676. fullScreen: false,
  1677. /**
  1678. * Percentage of screen height the modal should push up from the bottom of the view.
  1679. * @option
  1680. * @type {number}
  1681. * @default 10
  1682. */
  1683. btmOffsetPct: 10,
  1684. /**
  1685. * Allows the modal to generate an overlay div, which will cover the view when modal opens.
  1686. * @option
  1687. * @type {boolean}
  1688. * @default true
  1689. */
  1690. overlay: true,
  1691. /**
  1692. * Allows the modal to remove and reinject markup on close. Should be true if using video elements w/o using provider's api, otherwise, videos will continue to play in the background.
  1693. * @option
  1694. * @type {boolean}
  1695. * @default false
  1696. */
  1697. resetOnClose: false,
  1698. /**
  1699. * Allows the modal to alter the url on open/close, and allows the use of the `back` button to close modals. ALSO, allows a modal to auto-maniacally open on page load IF the hash === the modal's user-set id.
  1700. * @option
  1701. * @type {boolean}
  1702. * @default false
  1703. */
  1704. deepLink: false,
  1705. /**
  1706. * Allows the modal to append to custom div.
  1707. * @option
  1708. * @type {string}
  1709. * @default "body"
  1710. */
  1711. appendTo: "body"
  1712. };
  1713. // Window exports
  1714. Foundation.plugin(Reveal, 'Reveal');
  1715. function iPhoneSniff() {
  1716. return (/iP(ad|hone|od).*OS/.test(window.navigator.userAgent)
  1717. );
  1718. }
  1719. function androidSniff() {
  1720. return (/Android/.test(window.navigator.userAgent)
  1721. );
  1722. }
  1723. function mobileSniff() {
  1724. return iPhoneSniff() || androidSniff();
  1725. }
  1726. }(jQuery);