remodal.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. /* Remodal from https://github.com/vodkabears/Remodal
  2. * With Stackable option from https://github.com/antstorm/Remodal patch
  3. */
  4. import $ from 'jquery';
  5. !(function(root, factory) {
  6. return factory(root, $);
  7. })(this, function(global, $) {
  8. 'use strict';
  9. /**
  10. * Name of the plugin
  11. * @private
  12. * @const
  13. * @type {String}
  14. */
  15. var PLUGIN_NAME = 'remodal';
  16. /**
  17. * Namespace for CSS and events
  18. * @private
  19. * @const
  20. * @type {String}
  21. */
  22. var NAMESPACE = window.REMODAL_GLOBALS && window.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME;
  23. /**
  24. * Animationstart event with vendor prefixes
  25. * @private
  26. * @const
  27. * @type {String}
  28. */
  29. var ANIMATIONSTART_EVENTS = $.map(
  30. ['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'],
  31. function(eventName) {
  32. return eventName + '.' + NAMESPACE;
  33. }
  34. ).join(' ');
  35. /**
  36. * Animationend event with vendor prefixes
  37. * @private
  38. * @const
  39. * @type {String}
  40. */
  41. var ANIMATIONEND_EVENTS = $.map(
  42. ['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'],
  43. function(eventName) {
  44. return eventName + '.' + NAMESPACE;
  45. }
  46. ).join(' ');
  47. /**
  48. * Default settings
  49. * @private
  50. * @const
  51. * @type {Object}
  52. */
  53. var DEFAULTS = $.extend({
  54. hashTracking: true,
  55. closeOnConfirm: true,
  56. closeOnCancel: true,
  57. closeOnEscape: true,
  58. closeOnOutsideClick: true,
  59. modifier: '',
  60. stack: false,
  61. appendTo: null
  62. }, window.REMODAL_GLOBALS && window.REMODAL_GLOBALS.DEFAULTS);
  63. /**
  64. * States of the Remodal
  65. * @private
  66. * @const
  67. * @enum {String}
  68. */
  69. var STATES = {
  70. CLOSING: 'closing',
  71. CLOSED: 'closed',
  72. OPENING: 'opening',
  73. OPENED: 'opened'
  74. };
  75. /**
  76. * Reasons of the state change.
  77. * @private
  78. * @const
  79. * @enum {String}
  80. */
  81. var STATE_CHANGE_REASONS = {
  82. CONFIRMATION: 'confirmation',
  83. CANCELLATION: 'cancellation'
  84. };
  85. /**
  86. * Is animation supported?
  87. * @private
  88. * @const
  89. * @type {Boolean}
  90. */
  91. var IS_ANIMATION = (function() {
  92. var style = document.createElement('div').style;
  93. return style.animationName !== undefined ||
  94. style.WebkitAnimationName !== undefined ||
  95. style.MozAnimationName !== undefined ||
  96. style.msAnimationName !== undefined ||
  97. style.OAnimationName !== undefined;
  98. })();
  99. /**
  100. * Is iOS?
  101. * @private
  102. * @const
  103. * @type {Boolean}
  104. */
  105. var IS_IOS = /iPad|iPhone|iPod/.test(navigator.platform);
  106. /**
  107. * Current modal
  108. * @private
  109. * @type {Remodal}
  110. */
  111. var openModals = [];
  112. /**
  113. * Scrollbar position
  114. * @private
  115. * @type {Number}
  116. */
  117. var scrollTop;
  118. /**
  119. * Returns an animation duration
  120. * @private
  121. * @param {jQuery} $elem
  122. * @returns {Number}
  123. */
  124. function getAnimationDuration($elem) {
  125. if (
  126. IS_ANIMATION &&
  127. $elem.css('animation-name') === 'none' &&
  128. $elem.css('-webkit-animation-name') === 'none' &&
  129. $elem.css('-moz-animation-name') === 'none' &&
  130. $elem.css('-o-animation-name') === 'none' &&
  131. $elem.css('-ms-animation-name') === 'none'
  132. ) {
  133. return 0;
  134. }
  135. var duration = $elem.css('animation-duration') ||
  136. $elem.css('-webkit-animation-duration') ||
  137. $elem.css('-moz-animation-duration') ||
  138. $elem.css('-o-animation-duration') ||
  139. $elem.css('-ms-animation-duration') ||
  140. '0s';
  141. var delay = $elem.css('animation-delay') ||
  142. $elem.css('-webkit-animation-delay') ||
  143. $elem.css('-moz-animation-delay') ||
  144. $elem.css('-o-animation-delay') ||
  145. $elem.css('-ms-animation-delay') ||
  146. '0s';
  147. var iterationCount = $elem.css('animation-iteration-count') ||
  148. $elem.css('-webkit-animation-iteration-count') ||
  149. $elem.css('-moz-animation-iteration-count') ||
  150. $elem.css('-o-animation-iteration-count') ||
  151. $elem.css('-ms-animation-iteration-count') ||
  152. '1';
  153. var max;
  154. var len;
  155. var num;
  156. var i;
  157. duration = duration.split(', ');
  158. delay = delay.split(', ');
  159. iterationCount = iterationCount.split(', ');
  160. // The 'duration' size is the same as the 'delay' size
  161. for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) {
  162. num = parseFloat(duration[i]) * parseInt(iterationCount[i], 10) + parseFloat(delay[i]);
  163. if (num > max) {
  164. max = num;
  165. }
  166. }
  167. return max;
  168. }
  169. /**
  170. * Returns a scrollbar width
  171. * @private
  172. * @returns {Number}
  173. */
  174. function getScrollbarWidth() {
  175. if ($(document).height() <= $(window).height()) {
  176. return 0;
  177. }
  178. var outer = document.createElement('div');
  179. var inner = document.createElement('div');
  180. var widthNoScroll;
  181. var widthWithScroll;
  182. outer.style.visibility = 'hidden';
  183. outer.style.width = '100px';
  184. document.body.appendChild(outer);
  185. widthNoScroll = outer.offsetWidth;
  186. // Force scrollbars
  187. outer.style.overflow = 'scroll';
  188. // Add inner div
  189. inner.style.width = '100%';
  190. outer.appendChild(inner);
  191. widthWithScroll = inner.offsetWidth;
  192. // Remove divs
  193. outer.parentNode.removeChild(outer);
  194. return widthNoScroll - widthWithScroll;
  195. }
  196. /**
  197. * Locks the screen
  198. * @private
  199. */
  200. function lockScreen() {
  201. if (IS_IOS) {
  202. return;
  203. }
  204. var $html = $('html');
  205. var lockedClass = namespacify('is-locked');
  206. var paddingRight;
  207. var $body;
  208. if (!$html.hasClass(lockedClass)) {
  209. $body = $(document.body);
  210. // Zepto does not support '-=', '+=' in the `css` method
  211. paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth();
  212. $body.css('padding-right', paddingRight + 'px');
  213. $html.addClass(lockedClass);
  214. }
  215. }
  216. /**
  217. * Unlocks the screen
  218. * @private
  219. */
  220. function unlockScreen() {
  221. if (IS_IOS) {
  222. return;
  223. }
  224. var $html = $('html');
  225. var lockedClass = namespacify('is-locked');
  226. var paddingRight;
  227. var $body;
  228. if ($html.hasClass(lockedClass)) {
  229. $body = $(document.body);
  230. // Zepto does not support '-=', '+=' in the `css` method
  231. paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth();
  232. $body.css('padding-right', paddingRight + 'px');
  233. $html.removeClass(lockedClass);
  234. }
  235. }
  236. /**
  237. * Sets a state for an instance
  238. * @private
  239. * @param {Remodal} instance
  240. * @param {STATES} state
  241. * @param {Boolean} isSilent If true, Remodal does not trigger events
  242. * @param {String} Reason of a state change.
  243. */
  244. function setState(instance, state, isSilent, reason) {
  245. var newState = namespacify('is', state);
  246. var allStates = [namespacify('is', STATES.CLOSING),
  247. namespacify('is', STATES.OPENING),
  248. namespacify('is', STATES.CLOSED),
  249. namespacify('is', STATES.OPENED)].join(' ');
  250. instance.$bg
  251. .removeClass(allStates)
  252. .addClass(newState);
  253. instance.$overlay
  254. .removeClass(allStates)
  255. .addClass(newState);
  256. instance.$wrapper
  257. .removeClass(allStates)
  258. .addClass(newState);
  259. instance.$modal
  260. .removeClass(allStates)
  261. .addClass(newState);
  262. instance.state = state;
  263. !isSilent && instance.$modal.trigger({
  264. type: state,
  265. reason: reason
  266. }, [{ reason: reason }]);
  267. }
  268. /**
  269. * Synchronizes with the animation
  270. * @param {Function} doBeforeAnimation
  271. * @param {Function} doAfterAnimation
  272. * @param {Remodal} instance
  273. */
  274. function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) {
  275. var runningAnimationsCount = 0;
  276. var handleAnimationStart = function(e) {
  277. if (e.target !== this) {
  278. return;
  279. }
  280. runningAnimationsCount++;
  281. };
  282. var handleAnimationEnd = function(e) {
  283. if (e.target !== this) {
  284. return;
  285. }
  286. if (--runningAnimationsCount === 0) {
  287. // Remove event listeners
  288. $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
  289. instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
  290. });
  291. doAfterAnimation();
  292. }
  293. };
  294. $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
  295. instance[elemName]
  296. .on(ANIMATIONSTART_EVENTS, handleAnimationStart)
  297. .on(ANIMATIONEND_EVENTS, handleAnimationEnd);
  298. });
  299. doBeforeAnimation();
  300. // If the animation is not supported by a browser or its duration is 0
  301. if (
  302. getAnimationDuration(instance.$bg) === 0 &&
  303. getAnimationDuration(instance.$overlay) === 0 &&
  304. getAnimationDuration(instance.$wrapper) === 0 &&
  305. getAnimationDuration(instance.$modal) === 0
  306. ) {
  307. // Remove event listeners
  308. $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
  309. instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
  310. });
  311. doAfterAnimation();
  312. }
  313. }
  314. /**
  315. * Closes immediately
  316. * @private
  317. * @param {Remodal} instance
  318. */
  319. function halt(instance) {
  320. if (instance.state === STATES.CLOSED) {
  321. return;
  322. }
  323. $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
  324. instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
  325. });
  326. removeModal(instance);
  327. instance.$bg.removeClass(instance.settings.modifier);
  328. instance.$overlay.removeClass(instance.settings.modifier).hide();
  329. instance.$wrapper.hide();
  330. if (openModals.length === 0) {
  331. unlockScreen();
  332. }
  333. setState(instance, STATES.CLOSED, true);
  334. }
  335. /**
  336. * Parses a string with options
  337. * @private
  338. * @param str
  339. * @returns {Object}
  340. */
  341. function parseOptions(str) {
  342. var obj = {};
  343. var arr;
  344. var len;
  345. var val;
  346. var i;
  347. // Remove spaces before and after delimiters
  348. str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',');
  349. // Parse a string
  350. arr = str.split(',');
  351. for (i = 0, len = arr.length; i < len; i++) {
  352. arr[i] = arr[i].split(':');
  353. val = arr[i][1];
  354. // Convert a string value if it is like a boolean
  355. if (typeof val === 'string' || val instanceof String) {
  356. val = val === 'true' || (val === 'false' ? false : val);
  357. }
  358. // Convert a string value if it is like a number
  359. if (typeof val === 'string' || val instanceof String) {
  360. val = !isNaN(val) ? +val : val;
  361. }
  362. obj[arr[i][0]] = val;
  363. }
  364. return obj;
  365. }
  366. /**
  367. * Generates a string separated by dashes and prefixed with NAMESPACE
  368. * @private
  369. * @param {...String}
  370. * @returns {String}
  371. */
  372. function namespacify() {
  373. var result = NAMESPACE;
  374. for (var i = 0; i < arguments.length; ++i) {
  375. result += '-' + arguments[i];
  376. }
  377. return result;
  378. }
  379. /**
  380. * Handles the hashchange event
  381. * @private
  382. * @listens hashchange
  383. */
  384. function handleHashChangeEvent() {
  385. var id = location.hash.replace('#', '');
  386. var instance;
  387. var $elem;
  388. var current = currentModal();
  389. if (!id) {
  390. // Check if we have currently opened modal and animation was completed
  391. if (current && current.state === STATES.OPENED && current.settings.hashTracking) {
  392. current.close();
  393. }
  394. } else {
  395. if (!current || current.id !== id) {
  396. // Catch syntax error if your hash is bad
  397. try {
  398. $elem = $(
  399. '[data-' + PLUGIN_NAME + '-id="' + id + '"]'
  400. );
  401. } catch (err) {
  402. }
  403. if ($elem && $elem.length) {
  404. instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
  405. if (instance && instance.settings.hashTracking) {
  406. instance.open();
  407. }
  408. }
  409. }
  410. }
  411. }
  412. function currentModal() {
  413. return openModals[openModals.length - 1];
  414. }
  415. function removeModal(remodal) {
  416. var index = openModals.indexOf(remodal);
  417. if (index >= 0) {
  418. openModals.slice(index, 1);
  419. }
  420. }
  421. /**
  422. * Remodal constructor
  423. * @constructor
  424. * @param {jQuery} $modal
  425. * @param {Object} options
  426. */
  427. function Remodal($modal, options) {
  428. var $body = $(document.body);
  429. var $appendTo = $body;
  430. var remodal = this;
  431. remodal.id = $modal.attr('data-' + PLUGIN_NAME + '-id');
  432. remodal.settings = $.extend({}, DEFAULTS, options);
  433. remodal.index = $[PLUGIN_NAME].lookup.push(remodal) - 1;
  434. remodal.state = STATES.CLOSED;
  435. // remodal.$overlay = $('.' + namespacify('overlay'));
  436. if (remodal.settings.appendTo !== null && remodal.settings.appendTo.length) {
  437. $appendTo = $(remodal.settings.appendTo);
  438. }
  439. if (!remodal.$overlay) {
  440. remodal.$overlay = $('<div>').addClass(namespacify('overlay') + ' ' + namespacify('is', STATES.CLOSED)).hide();
  441. $appendTo.append(remodal.$overlay);
  442. }
  443. remodal.$bg = $('.' + namespacify('bg')).addClass(namespacify('is', STATES.CLOSED));
  444. remodal.$modal = $modal
  445. .addClass(
  446. NAMESPACE + ' ' +
  447. namespacify('is-initialized') + ' ' +
  448. remodal.settings.modifier + ' ' +
  449. namespacify('is', STATES.CLOSED))
  450. .attr('tabindex', '-1');
  451. remodal.$wrapper = $('<div>')
  452. .addClass(
  453. namespacify('wrapper') + ' ' +
  454. remodal.settings.modifier + ' ' +
  455. namespacify('is', STATES.CLOSED))
  456. .hide()
  457. .append(remodal.$modal);
  458. $appendTo.append(remodal.$wrapper);
  459. // Add the event listener for the close button
  460. remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="close"]', function(e) {
  461. e.preventDefault();
  462. remodal.close();
  463. });
  464. // Add the event listener for the cancel button
  465. remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="cancel"]', function(e) {
  466. e.preventDefault();
  467. remodal.$modal.trigger(STATE_CHANGE_REASONS.CANCELLATION);
  468. if (remodal.settings.closeOnCancel) {
  469. remodal.close(STATE_CHANGE_REASONS.CANCELLATION);
  470. }
  471. });
  472. // Add the event listener for the confirm button
  473. remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="confirm"]', function(e) {
  474. e.preventDefault();
  475. remodal.$modal.trigger(STATE_CHANGE_REASONS.CONFIRMATION);
  476. if (remodal.settings.closeOnConfirm) {
  477. remodal.close(STATE_CHANGE_REASONS.CONFIRMATION);
  478. }
  479. });
  480. // Add the event listener for the overlay
  481. remodal.$wrapper.on('click.' + NAMESPACE, function(e) {
  482. var $target = $(e.target);
  483. var isWrapper = $target.hasClass(namespacify('wrapper'));
  484. var isWithin = $target.closest('.' + namespacify('is', STATES.OPENED)).length;
  485. if (!isWrapper && isWithin) {
  486. return;
  487. }
  488. if (remodal.settings.closeOnOutsideClick) {
  489. remodal.close();
  490. }
  491. });
  492. }
  493. /**
  494. * Opens a modal window
  495. * @public
  496. */
  497. Remodal.prototype.open = function() {
  498. var remodal = this;
  499. var current;
  500. var modalCount;
  501. // Check if the animation was completed
  502. if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) {
  503. return;
  504. }
  505. // id = remodal.$modal.attr('data-' + PLUGIN_NAME + '-id');
  506. if (remodal.id && remodal.settings.hashTracking) {
  507. scrollTop = $(window).scrollTop();
  508. location.hash = remodal.id;
  509. }
  510. if (!remodal.settings.stack) {
  511. current = currentModal();
  512. if (current && current !== remodal) {
  513. halt(current);
  514. }
  515. }
  516. modalCount = openModals.push(remodal);
  517. remodal.$overlay.css('z-index', function(_, value) { return parseInt(value, 10) + modalCount; });
  518. remodal.$wrapper.css('z-index', function(_, value) { return parseInt(value, 10) + modalCount; });
  519. lockScreen();
  520. remodal.$bg.addClass(remodal.settings.modifier);
  521. remodal.$overlay.addClass(remodal.settings.modifier).show();
  522. remodal.$wrapper.show().scrollTop(0);
  523. remodal.$modal.focus();
  524. syncWithAnimation(
  525. function() {
  526. setState(remodal, STATES.OPENING);
  527. },
  528. function() {
  529. setState(remodal, STATES.OPENED);
  530. },
  531. remodal);
  532. };
  533. /**
  534. * Closes a modal window
  535. * @public
  536. * @param {String} reason
  537. */
  538. Remodal.prototype.close = function(reason) {
  539. var remodal = this;
  540. var current;
  541. // Check if the animation was completed
  542. if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING || remodal.state === STATES.CLOSED) {
  543. return;
  544. }
  545. removeModal(remodal);
  546. if (
  547. remodal.settings.hashTracking &&
  548. remodal.id === location.hash.substr(1)
  549. ) {
  550. current = currentModal();
  551. if (current) {
  552. location.hash = current.id;
  553. } else {
  554. location.hash = '';
  555. $(window).scrollTop(scrollTop);
  556. }
  557. }
  558. syncWithAnimation(
  559. function() {
  560. setState(remodal, STATES.CLOSING, false, reason);
  561. },
  562. function() {
  563. remodal.$bg.removeClass(remodal.settings.modifier);
  564. remodal.$overlay.removeClass(remodal.settings.modifier).hide();
  565. remodal.$wrapper.hide();
  566. if (openModals.length === 0) {
  567. unlockScreen();
  568. }
  569. setState(remodal, STATES.CLOSED, false, reason);
  570. },
  571. remodal);
  572. };
  573. /**
  574. * Returns a current state of a modal
  575. * @public
  576. * @returns {STATES}
  577. */
  578. Remodal.prototype.getState = function() {
  579. return this.state;
  580. };
  581. /**
  582. * Destroys a modal
  583. * @public
  584. */
  585. Remodal.prototype.destroy = function() {
  586. var lookup = $[PLUGIN_NAME].lookup;
  587. var instanceCount;
  588. halt(this);
  589. this.$wrapper.remove();
  590. delete lookup[this.index];
  591. instanceCount = $.grep(lookup, function(instance) {
  592. return !!instance;
  593. }).length;
  594. if (instanceCount === 0) {
  595. this.$overlay.remove();
  596. this.$bg.removeClass(
  597. namespacify('is', STATES.CLOSING) + ' ' +
  598. namespacify('is', STATES.OPENING) + ' ' +
  599. namespacify('is', STATES.CLOSED) + ' ' +
  600. namespacify('is', STATES.OPENED));
  601. }
  602. };
  603. /**
  604. * Special plugin object for instances
  605. * @public
  606. * @type {Object}
  607. */
  608. $[PLUGIN_NAME] = {
  609. lookup: []
  610. };
  611. /**
  612. * Plugin constructor
  613. * @constructor
  614. * @param {Object} options
  615. * @returns {JQuery}
  616. */
  617. $.fn[PLUGIN_NAME] = function(opts) {
  618. var instance;
  619. var $elem;
  620. this.each(function(index, elem) {
  621. $elem = $(elem);
  622. if ($elem.data(PLUGIN_NAME) == null) {
  623. instance = new Remodal($elem, opts);
  624. $elem.data(PLUGIN_NAME, instance.index);
  625. if (
  626. instance.settings.hashTracking &&
  627. instance.id === location.hash.substr(1)
  628. ) {
  629. instance.open();
  630. }
  631. } else {
  632. instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
  633. }
  634. });
  635. return instance;
  636. };
  637. $(document).ready(function() {
  638. // data-remodal-target opens a modal window with the special Id
  639. $(document).on('click', '[data-' + PLUGIN_NAME + '-target]', function(e) {
  640. e.preventDefault();
  641. var elem = e.currentTarget;
  642. var id = elem.getAttribute('data-' + PLUGIN_NAME + '-target');
  643. var $target = $('[data-' + PLUGIN_NAME + '-id="' + id + '"]');
  644. $[PLUGIN_NAME].lookup[$target.data(PLUGIN_NAME)].open();
  645. });
  646. // Auto initialization of modal windows
  647. // They should have the 'remodal' class attribute
  648. // Also you can write the `data-remodal-options` attribute to pass params into the modal
  649. $(document).find('.' + NAMESPACE).each(function(i, container) {
  650. var $container = $(container);
  651. var options = $container.data(PLUGIN_NAME + '-options');
  652. if (!options) {
  653. options = {};
  654. } else if (typeof options === 'string' || options instanceof String) {
  655. options = parseOptions(options);
  656. }
  657. $container[PLUGIN_NAME](options);
  658. });
  659. // Handles the keydown event
  660. $(document).on('keydown.' + NAMESPACE, function(e) {
  661. var current = currentModal();
  662. if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) {
  663. current.close();
  664. }
  665. });
  666. // Handles the hashchange event
  667. $(window).on('hashchange.' + NAMESPACE, handleHashChangeEvent);
  668. });
  669. });