Personal portfolio website created with bootstrap.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1859 lines
41 KiB

  1. /*! Magnific Popup - v1.1.0 - 2016-02-20
  2. * http://dimsemenov.com/plugins/magnific-popup/
  3. * Copyright (c) 2016 Dmitry Semenov; */
  4. ;(function (factory) {
  5. if (typeof define === 'function' && define.amd) {
  6. // AMD. Register as an anonymous module.
  7. define(['jquery'], factory);
  8. } else if (typeof exports === 'object') {
  9. // Node/CommonJS
  10. factory(require('jquery'));
  11. } else {
  12. // Browser globals
  13. factory(window.jQuery || window.Zepto);
  14. }
  15. }(function($) {
  16. /*>>core*/
  17. /**
  18. *
  19. * Magnific Popup Core JS file
  20. *
  21. */
  22. /**
  23. * Private static constants
  24. */
  25. var CLOSE_EVENT = 'Close',
  26. BEFORE_CLOSE_EVENT = 'BeforeClose',
  27. AFTER_CLOSE_EVENT = 'AfterClose',
  28. BEFORE_APPEND_EVENT = 'BeforeAppend',
  29. MARKUP_PARSE_EVENT = 'MarkupParse',
  30. OPEN_EVENT = 'Open',
  31. CHANGE_EVENT = 'Change',
  32. NS = 'mfp',
  33. EVENT_NS = '.' + NS,
  34. READY_CLASS = 'mfp-ready',
  35. REMOVING_CLASS = 'mfp-removing',
  36. PREVENT_CLOSE_CLASS = 'mfp-prevent-close';
  37. /**
  38. * Private vars
  39. */
  40. /*jshint -W079 */
  41. var mfp, // As we have only one instance of MagnificPopup object, we define it locally to not to use 'this'
  42. MagnificPopup = function(){},
  43. _isJQ = !!(window.jQuery),
  44. _prevStatus,
  45. _window = $(window),
  46. _document,
  47. _prevContentType,
  48. _wrapClasses,
  49. _currPopupType;
  50. /**
  51. * Private functions
  52. */
  53. var _mfpOn = function(name, f) {
  54. mfp.ev.on(NS + name + EVENT_NS, f);
  55. },
  56. _getEl = function(className, appendTo, html, raw) {
  57. var el = document.createElement('div');
  58. el.className = 'mfp-'+className;
  59. if(html) {
  60. el.innerHTML = html;
  61. }
  62. if(!raw) {
  63. el = $(el);
  64. if(appendTo) {
  65. el.appendTo(appendTo);
  66. }
  67. } else if(appendTo) {
  68. appendTo.appendChild(el);
  69. }
  70. return el;
  71. },
  72. _mfpTrigger = function(e, data) {
  73. mfp.ev.triggerHandler(NS + e, data);
  74. if(mfp.st.callbacks) {
  75. // converts "mfpEventName" to "eventName" callback and triggers it if it's present
  76. e = e.charAt(0).toLowerCase() + e.slice(1);
  77. if(mfp.st.callbacks[e]) {
  78. mfp.st.callbacks[e].apply(mfp, $.isArray(data) ? data : [data]);
  79. }
  80. }
  81. },
  82. _getCloseBtn = function(type) {
  83. if(type !== _currPopupType || !mfp.currTemplate.closeBtn) {
  84. mfp.currTemplate.closeBtn = $( mfp.st.closeMarkup.replace('%title%', mfp.st.tClose ) );
  85. _currPopupType = type;
  86. }
  87. return mfp.currTemplate.closeBtn;
  88. },
  89. // Initialize Magnific Popup only when called at least once
  90. _checkInstance = function() {
  91. if(!$.magnificPopup.instance) {
  92. /*jshint -W020 */
  93. mfp = new MagnificPopup();
  94. mfp.init();
  95. $.magnificPopup.instance = mfp;
  96. }
  97. },
  98. // CSS transition detection, http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
  99. supportsTransitions = function() {
  100. var s = document.createElement('p').style, // 's' for style. better to create an element if body yet to exist
  101. v = ['ms','O','Moz','Webkit']; // 'v' for vendor
  102. if( s['transition'] !== undefined ) {
  103. return true;
  104. }
  105. while( v.length ) {
  106. if( v.pop() + 'Transition' in s ) {
  107. return true;
  108. }
  109. }
  110. return false;
  111. };
  112. /**
  113. * Public functions
  114. */
  115. MagnificPopup.prototype = {
  116. constructor: MagnificPopup,
  117. /**
  118. * Initializes Magnific Popup plugin.
  119. * This function is triggered only once when $.fn.magnificPopup or $.magnificPopup is executed
  120. */
  121. init: function() {
  122. var appVersion = navigator.appVersion;
  123. mfp.isLowIE = mfp.isIE8 = document.all && !document.addEventListener;
  124. mfp.isAndroid = (/android/gi).test(appVersion);
  125. mfp.isIOS = (/iphone|ipad|ipod/gi).test(appVersion);
  126. mfp.supportsTransition = supportsTransitions();
  127. // We disable fixed positioned lightbox on devices that don't handle it nicely.
  128. // If you know a better way of detecting this - let me know.
  129. mfp.probablyMobile = (mfp.isAndroid || mfp.isIOS || /(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent) );
  130. _document = $(document);
  131. mfp.popupsCache = {};
  132. },
  133. /**
  134. * Opens popup
  135. * @param data [description]
  136. */
  137. open: function(data) {
  138. var i;
  139. if(data.isObj === false) {
  140. // convert jQuery collection to array to avoid conflicts later
  141. mfp.items = data.items.toArray();
  142. mfp.index = 0;
  143. var items = data.items,
  144. item;
  145. for(i = 0; i < items.length; i++) {
  146. item = items[i];
  147. if(item.parsed) {
  148. item = item.el[0];
  149. }
  150. if(item === data.el[0]) {
  151. mfp.index = i;
  152. break;
  153. }
  154. }
  155. } else {
  156. mfp.items = $.isArray(data.items) ? data.items : [data.items];
  157. mfp.index = data.index || 0;
  158. }
  159. // if popup is already opened - we just update the content
  160. if(mfp.isOpen) {
  161. mfp.updateItemHTML();
  162. return;
  163. }
  164. mfp.types = [];
  165. _wrapClasses = '';
  166. if(data.mainEl && data.mainEl.length) {
  167. mfp.ev = data.mainEl.eq(0);
  168. } else {
  169. mfp.ev = _document;
  170. }
  171. if(data.key) {
  172. if(!mfp.popupsCache[data.key]) {
  173. mfp.popupsCache[data.key] = {};
  174. }
  175. mfp.currTemplate = mfp.popupsCache[data.key];
  176. } else {
  177. mfp.currTemplate = {};
  178. }
  179. mfp.st = $.extend(true, {}, $.magnificPopup.defaults, data );
  180. mfp.fixedContentPos = mfp.st.fixedContentPos === 'auto' ? !mfp.probablyMobile : mfp.st.fixedContentPos;
  181. if(mfp.st.modal) {
  182. mfp.st.closeOnContentClick = false;
  183. mfp.st.closeOnBgClick = false;
  184. mfp.st.showCloseBtn = false;
  185. mfp.st.enableEscapeKey = false;
  186. }
  187. // Building markup
  188. // main containers are created only once
  189. if(!mfp.bgOverlay) {
  190. // Dark overlay
  191. mfp.bgOverlay = _getEl('bg').on('click'+EVENT_NS, function() {
  192. mfp.close();
  193. });
  194. mfp.wrap = _getEl('wrap').attr('tabindex', -1).on('click'+EVENT_NS, function(e) {
  195. if(mfp._checkIfClose(e.target)) {
  196. mfp.close();
  197. }
  198. });
  199. mfp.container = _getEl('container', mfp.wrap);
  200. }
  201. mfp.contentContainer = _getEl('content');
  202. if(mfp.st.preloader) {
  203. mfp.preloader = _getEl('preloader', mfp.container, mfp.st.tLoading);
  204. }
  205. // Initializing modules
  206. var modules = $.magnificPopup.modules;
  207. for(i = 0; i < modules.length; i++) {
  208. var n = modules[i];
  209. n = n.charAt(0).toUpperCase() + n.slice(1);
  210. mfp['init'+n].call(mfp);
  211. }
  212. _mfpTrigger('BeforeOpen');
  213. if(mfp.st.showCloseBtn) {
  214. // Close button
  215. if(!mfp.st.closeBtnInside) {
  216. mfp.wrap.append( _getCloseBtn() );
  217. } else {
  218. _mfpOn(MARKUP_PARSE_EVENT, function(e, template, values, item) {
  219. values.close_replaceWith = _getCloseBtn(item.type);
  220. });
  221. _wrapClasses += ' mfp-close-btn-in';
  222. }
  223. }
  224. if(mfp.st.alignTop) {
  225. _wrapClasses += ' mfp-align-top';
  226. }
  227. if(mfp.fixedContentPos) {
  228. mfp.wrap.css({
  229. overflow: mfp.st.overflowY,
  230. overflowX: 'hidden',
  231. overflowY: mfp.st.overflowY
  232. });
  233. } else {
  234. mfp.wrap.css({
  235. top: _window.scrollTop(),
  236. position: 'absolute'
  237. });
  238. }
  239. if( mfp.st.fixedBgPos === false || (mfp.st.fixedBgPos === 'auto' && !mfp.fixedContentPos) ) {
  240. mfp.bgOverlay.css({
  241. height: _document.height(),
  242. position: 'absolute'
  243. });
  244. }
  245. if(mfp.st.enableEscapeKey) {
  246. // Close on ESC key
  247. _document.on('keyup' + EVENT_NS, function(e) {
  248. if(e.keyCode === 27) {
  249. mfp.close();
  250. }
  251. });
  252. }
  253. _window.on('resize' + EVENT_NS, function() {
  254. mfp.updateSize();
  255. });
  256. if(!mfp.st.closeOnContentClick) {
  257. _wrapClasses += ' mfp-auto-cursor';
  258. }
  259. if(_wrapClasses)
  260. mfp.wrap.addClass(_wrapClasses);
  261. // this triggers recalculation of layout, so we get it once to not to trigger twice
  262. var windowHeight = mfp.wH = _window.height();
  263. var windowStyles = {};
  264. if( mfp.fixedContentPos ) {
  265. if(mfp._hasScrollBar(windowHeight)){
  266. var s = mfp._getScrollbarSize();
  267. if(s) {
  268. windowStyles.marginRight = s;
  269. }
  270. }
  271. }
  272. if(mfp.fixedContentPos) {
  273. if(!mfp.isIE7) {
  274. windowStyles.overflow = 'hidden';
  275. } else {
  276. // ie7 double-scroll bug
  277. $('body, html').css('overflow', 'hidden');
  278. }
  279. }
  280. var classesToadd = mfp.st.mainClass;
  281. if(mfp.isIE7) {
  282. classesToadd += ' mfp-ie7';
  283. }
  284. if(classesToadd) {
  285. mfp._addClassToMFP( classesToadd );
  286. }
  287. // add content
  288. mfp.updateItemHTML();
  289. _mfpTrigger('BuildControls');
  290. // remove scrollbar, add margin e.t.c
  291. $('html').css(windowStyles);
  292. // add everything to DOM
  293. mfp.bgOverlay.add(mfp.wrap).prependTo( mfp.st.prependTo || $(document.body) );
  294. // Save last focused element
  295. mfp._lastFocusedEl = document.activeElement;
  296. // Wait for next cycle to allow CSS transition
  297. setTimeout(function() {
  298. if(mfp.content) {
  299. mfp._addClassToMFP(READY_CLASS);
  300. mfp._setFocus();
  301. } else {
  302. // if content is not defined (not loaded e.t.c) we add class only for BG
  303. mfp.bgOverlay.addClass(READY_CLASS);
  304. }
  305. // Trap the focus in popup
  306. _document.on('focusin' + EVENT_NS, mfp._onFocusIn);
  307. }, 16);
  308. mfp.isOpen = true;
  309. mfp.updateSize(windowHeight);
  310. _mfpTrigger(OPEN_EVENT);
  311. return data;
  312. },
  313. /**
  314. * Closes the popup
  315. */
  316. close: function() {
  317. if(!mfp.isOpen) return;
  318. _mfpTrigger(BEFORE_CLOSE_EVENT);
  319. mfp.isOpen = false;
  320. // for CSS3 animation
  321. if(mfp.st.removalDelay && !mfp.isLowIE && mfp.supportsTransition ) {
  322. mfp._addClassToMFP(REMOVING_CLASS);
  323. setTimeout(function() {
  324. mfp._close();
  325. }, mfp.st.removalDelay);
  326. } else {
  327. mfp._close();
  328. }
  329. },
  330. /**
  331. * Helper for close() function
  332. */
  333. _close: function() {
  334. _mfpTrigger(CLOSE_EVENT);
  335. var classesToRemove = REMOVING_CLASS + ' ' + READY_CLASS + ' ';
  336. mfp.bgOverlay.detach();
  337. mfp.wrap.detach();
  338. mfp.container.empty();
  339. if(mfp.st.mainClass) {
  340. classesToRemove += mfp.st.mainClass + ' ';
  341. }
  342. mfp._removeClassFromMFP(classesToRemove);
  343. if(mfp.fixedContentPos) {
  344. var windowStyles = {marginRight: ''};
  345. if(mfp.isIE7) {
  346. $('body, html').css('overflow', '');
  347. } else {
  348. windowStyles.overflow = '';
  349. }
  350. $('html').css(windowStyles);
  351. }
  352. _document.off('keyup' + EVENT_NS + ' focusin' + EVENT_NS);
  353. mfp.ev.off(EVENT_NS);
  354. // clean up DOM elements that aren't removed
  355. mfp.wrap.attr('class', 'mfp-wrap').removeAttr('style');
  356. mfp.bgOverlay.attr('class', 'mfp-bg');
  357. mfp.container.attr('class', 'mfp-container');
  358. // remove close button from target element
  359. if(mfp.st.showCloseBtn &&
  360. (!mfp.st.closeBtnInside || mfp.currTemplate[mfp.currItem.type] === true)) {
  361. if(mfp.currTemplate.closeBtn)
  362. mfp.currTemplate.closeBtn.detach();
  363. }
  364. if(mfp.st.autoFocusLast && mfp._lastFocusedEl) {
  365. $(mfp._lastFocusedEl).focus(); // put tab focus back
  366. }
  367. mfp.currItem = null;
  368. mfp.content = null;
  369. mfp.currTemplate = null;
  370. mfp.prevHeight = 0;
  371. _mfpTrigger(AFTER_CLOSE_EVENT);
  372. },
  373. updateSize: function(winHeight) {
  374. if(mfp.isIOS) {
  375. // fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2
  376. var zoomLevel = document.documentElement.clientWidth / window.innerWidth;
  377. var height = window.innerHeight * zoomLevel;
  378. mfp.wrap.css('height', height);
  379. mfp.wH = height;
  380. } else {
  381. mfp.wH = winHeight || _window.height();
  382. }
  383. // Fixes #84: popup incorrectly positioned with position:relative on body
  384. if(!mfp.fixedContentPos) {
  385. mfp.wrap.css('height', mfp.wH);
  386. }
  387. _mfpTrigger('Resize');
  388. },
  389. /**
  390. * Set content of popup based on current index
  391. */
  392. updateItemHTML: function() {
  393. var item = mfp.items[mfp.index];
  394. // Detach and perform modifications
  395. mfp.contentContainer.detach();
  396. if(mfp.content)
  397. mfp.content.detach();
  398. if(!item.parsed) {
  399. item = mfp.parseEl( mfp.index );
  400. }
  401. var type = item.type;
  402. _mfpTrigger('BeforeChange', [mfp.currItem ? mfp.currItem.type : '', type]);
  403. // BeforeChange event works like so:
  404. // _mfpOn('BeforeChange', function(e, prevType, newType) { });
  405. mfp.currItem = item;
  406. if(!mfp.currTemplate[type]) {
  407. var markup = mfp.st[type] ? mfp.st[type].markup : false;
  408. // allows to modify markup
  409. _mfpTrigger('FirstMarkupParse', markup);
  410. if(markup) {
  411. mfp.currTemplate[type] = $(markup);
  412. } else {
  413. // if there is no markup found we just define that template is parsed
  414. mfp.currTemplate[type] = true;
  415. }
  416. }
  417. if(_prevContentType && _prevContentType !== item.type) {
  418. mfp.container.removeClass('mfp-'+_prevContentType+'-holder');
  419. }
  420. var newContent = mfp['get' + type.charAt(0).toUpperCase() + type.slice(1)](item, mfp.currTemplate[type]);
  421. mfp.appendContent(newContent, type);
  422. item.preloaded = true;
  423. _mfpTrigger(CHANGE_EVENT, item);
  424. _prevContentType = item.type;
  425. // Append container back after its content changed
  426. mfp.container.prepend(mfp.contentContainer);
  427. _mfpTrigger('AfterChange');
  428. },
  429. /**
  430. * Set HTML content of popup
  431. */
  432. appendContent: function(newContent, type) {
  433. mfp.content = newContent;
  434. if(newContent) {
  435. if(mfp.st.showCloseBtn && mfp.st.closeBtnInside &&
  436. mfp.currTemplate[type] === true) {
  437. // if there is no markup, we just append close button element inside
  438. if(!mfp.content.find('.mfp-close').length) {
  439. mfp.content.append(_getCloseBtn());
  440. }
  441. } else {
  442. mfp.content = newContent;
  443. }
  444. } else {
  445. mfp.content = '';
  446. }
  447. _mfpTrigger(BEFORE_APPEND_EVENT);
  448. mfp.container.addClass('mfp-'+type+'-holder');
  449. mfp.contentContainer.append(mfp.content);
  450. },
  451. /**
  452. * Creates Magnific Popup data object based on given data
  453. * @param {int} index Index of item to parse
  454. */
  455. parseEl: function(index) {
  456. var item = mfp.items[index],
  457. type;
  458. if(item.tagName) {
  459. item = { el: $(item) };
  460. } else {
  461. type = item.type;
  462. item = { data: item, src: item.src };
  463. }
  464. if(item.el) {
  465. var types = mfp.types;
  466. // check for 'mfp-TYPE' class
  467. for(var i = 0; i < types.length; i++) {
  468. if( item.el.hasClass('mfp-'+types[i]) ) {
  469. type = types[i];
  470. break;
  471. }
  472. }
  473. item.src = item.el.attr('data-mfp-src');
  474. if(!item.src) {
  475. item.src = item.el.attr('href');
  476. }
  477. }
  478. item.type = type || mfp.st.type || 'inline';
  479. item.index = index;
  480. item.parsed = true;
  481. mfp.items[index] = item;
  482. _mfpTrigger('ElementParse', item);
  483. return mfp.items[index];
  484. },
  485. /**
  486. * Initializes single popup or a group of popups
  487. */
  488. addGroup: function(el, options) {
  489. var eHandler = function(e) {
  490. e.mfpEl = this;
  491. mfp._openClick(e, el, options);
  492. };
  493. if(!options) {
  494. options = {};
  495. }
  496. var eName = 'click.magnificPopup';
  497. options.mainEl = el;
  498. if(options.items) {
  499. options.isObj = true;
  500. el.off(eName).on(eName, eHandler);
  501. } else {
  502. options.isObj = false;
  503. if(options.delegate) {
  504. el.off(eName).on(eName, options.delegate , eHandler);
  505. } else {
  506. options.items = el;
  507. el.off(eName).on(eName, eHandler);
  508. }
  509. }
  510. },
  511. _openClick: function(e, el, options) {
  512. var midClick = options.midClick !== undefined ? options.midClick : $.magnificPopup.defaults.midClick;
  513. if(!midClick && ( e.which === 2 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey ) ) {
  514. return;
  515. }
  516. var disableOn = options.disableOn !== undefined ? options.disableOn : $.magnificPopup.defaults.disableOn;
  517. if(disableOn) {
  518. if($.isFunction(disableOn)) {
  519. if( !disableOn.call(mfp) ) {
  520. return true;
  521. }
  522. } else { // else it's number
  523. if( _window.width() < disableOn ) {
  524. return true;
  525. }
  526. }
  527. }
  528. if(e.type) {
  529. e.preventDefault();
  530. // This will prevent popup from closing if element is inside and popup is already opened
  531. if(mfp.isOpen) {
  532. e.stopPropagation();
  533. }
  534. }
  535. options.el = $(e.mfpEl);
  536. if(options.delegate) {
  537. options.items = el.find(options.delegate);
  538. }
  539. mfp.open(options);
  540. },
  541. /**
  542. * Updates text on preloader
  543. */
  544. updateStatus: function(status, text) {
  545. if(mfp.preloader) {
  546. if(_prevStatus !== status) {
  547. mfp.container.removeClass('mfp-s-'+_prevStatus);
  548. }
  549. if(!text && status === 'loading') {
  550. text = mfp.st.tLoading;
  551. }
  552. var data = {
  553. status: status,
  554. text: text
  555. };
  556. // allows to modify status
  557. _mfpTrigger('UpdateStatus', data);
  558. status = data.status;
  559. text = data.text;
  560. mfp.preloader.html(text);
  561. mfp.preloader.find('a').on('click', function(e) {
  562. e.stopImmediatePropagation();
  563. });
  564. mfp.container.addClass('mfp-s-'+status);
  565. _prevStatus = status;
  566. }
  567. },
  568. /*
  569. "Private" helpers that aren't private at all
  570. */
  571. // Check to close popup or not
  572. // "target" is an element that was clicked
  573. _checkIfClose: function(target) {
  574. if($(target).hasClass(PREVENT_CLOSE_CLASS)) {
  575. return;
  576. }
  577. var closeOnContent = mfp.st.closeOnContentClick;
  578. var closeOnBg = mfp.st.closeOnBgClick;
  579. if(closeOnContent && closeOnBg) {
  580. return true;
  581. } else {
  582. // We close the popup if click is on close button or on preloader. Or if there is no content.
  583. if(!mfp.content || $(target).hasClass('mfp-close') || (mfp.preloader && target === mfp.preloader[0]) ) {
  584. return true;
  585. }
  586. // if click is outside the content
  587. if( (target !== mfp.content[0] && !$.contains(mfp.content[0], target)) ) {
  588. if(closeOnBg) {
  589. // last check, if the clicked element is in DOM, (in case it's removed onclick)
  590. if( $.contains(document, target) ) {
  591. return true;
  592. }
  593. }
  594. } else if(closeOnContent) {
  595. return true;
  596. }
  597. }
  598. return false;
  599. },
  600. _addClassToMFP: function(cName) {
  601. mfp.bgOverlay.addClass(cName);
  602. mfp.wrap.addClass(cName);
  603. },
  604. _removeClassFromMFP: function(cName) {
  605. this.bgOverlay.removeClass(cName);
  606. mfp.wrap.removeClass(cName);
  607. },
  608. _hasScrollBar: function(winHeight) {
  609. return ( (mfp.isIE7 ? _document.height() : document.body.scrollHeight) > (winHeight || _window.height()) );
  610. },
  611. _setFocus: function() {
  612. (mfp.st.focus ? mfp.content.find(mfp.st.focus).eq(0) : mfp.wrap).focus();
  613. },
  614. _onFocusIn: function(e) {
  615. if( e.target !== mfp.wrap[0] && !$.contains(mfp.wrap[0], e.target) ) {
  616. mfp._setFocus();
  617. return false;
  618. }
  619. },
  620. _parseMarkup: function(template, values, item) {
  621. var arr;
  622. if(item.data) {
  623. values = $.extend(item.data, values);
  624. }
  625. _mfpTrigger(MARKUP_PARSE_EVENT, [template, values, item] );
  626. $.each(values, function(key, value) {
  627. if(value === undefined || value === false) {
  628. return true;
  629. }
  630. arr = key.split('_');
  631. if(arr.length > 1) {
  632. var el = template.find(EVENT_NS + '-'+arr[0]);
  633. if(el.length > 0) {
  634. var attr = arr[1];
  635. if(attr === 'replaceWith') {
  636. if(el[0] !== value[0]) {
  637. el.replaceWith(value);
  638. }
  639. } else if(attr === 'img') {
  640. if(el.is('img')) {
  641. el.attr('src', value);
  642. } else {
  643. el.replaceWith( $('<img>').attr('src', value).attr('class', el.attr('class')) );
  644. }
  645. } else {
  646. el.attr(arr[1], value);
  647. }
  648. }
  649. } else {
  650. template.find(EVENT_NS + '-'+key).html(value);
  651. }
  652. });
  653. },
  654. _getScrollbarSize: function() {
  655. // thx David
  656. if(mfp.scrollbarSize === undefined) {
  657. var scrollDiv = document.createElement("div");
  658. scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
  659. document.body.appendChild(scrollDiv);
  660. mfp.scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth;
  661. document.body.removeChild(scrollDiv);
  662. }
  663. return mfp.scrollbarSize;
  664. }
  665. }; /* MagnificPopup core prototype end */
  666. /**
  667. * Public static functions
  668. */
  669. $.magnificPopup = {
  670. instance: null,
  671. proto: MagnificPopup.prototype,
  672. modules: [],
  673. open: function(options, index) {
  674. _checkInstance();
  675. if(!options) {
  676. options = {};
  677. } else {
  678. options = $.extend(true, {}, options);
  679. }
  680. options.isObj = true;
  681. options.index = index || 0;
  682. return this.instance.open(options);
  683. },
  684. close: function() {
  685. return $.magnificPopup.instance && $.magnificPopup.instance.close();
  686. },
  687. registerModule: function(name, module) {
  688. if(module.options) {
  689. $.magnificPopup.defaults[name] = module.options;
  690. }
  691. $.extend(this.proto, module.proto);
  692. this.modules.push(name);
  693. },
  694. defaults: {
  695. // Info about options is in docs:
  696. // http://dimsemenov.com/plugins/magnific-popup/documentation.html#options
  697. disableOn: 0,
  698. key: null,
  699. midClick: false,
  700. mainClass: '',
  701. preloader: true,
  702. focus: '', // CSS selector of input to focus after popup is opened
  703. closeOnContentClick: false,
  704. closeOnBgClick: true,
  705. closeBtnInside: true,
  706. showCloseBtn: true,
  707. enableEscapeKey: true,
  708. modal: false,
  709. alignTop: false,
  710. removalDelay: 0,
  711. prependTo: null,
  712. fixedContentPos: 'auto',
  713. fixedBgPos: 'auto',
  714. overflowY: 'auto',
  715. closeMarkup: '<button title="%title%" type="button" class="mfp-close">&#215;</button>',
  716. tClose: 'Close (Esc)',
  717. tLoading: 'Loading...',
  718. autoFocusLast: true
  719. }
  720. };
  721. $.fn.magnificPopup = function(options) {
  722. _checkInstance();
  723. var jqEl = $(this);
  724. // We call some API method of first param is a string
  725. if (typeof options === "string" ) {
  726. if(options === 'open') {
  727. var items,
  728. itemOpts = _isJQ ? jqEl.data('magnificPopup') : jqEl[0].magnificPopup,
  729. index = parseInt(arguments[1], 10) || 0;
  730. if(itemOpts.items) {
  731. items = itemOpts.items[index];
  732. } else {
  733. items = jqEl;
  734. if(itemOpts.delegate) {
  735. items = items.find(itemOpts.delegate);
  736. }
  737. items = items.eq( index );
  738. }
  739. mfp._openClick({mfpEl:items}, jqEl, itemOpts);
  740. } else {
  741. if(mfp.isOpen)
  742. mfp[options].apply(mfp, Array.prototype.slice.call(arguments, 1));
  743. }
  744. } else {
  745. // clone options obj
  746. options = $.extend(true, {}, options);
  747. /*
  748. * As Zepto doesn't support .data() method for objects
  749. * and it works only in normal browsers
  750. * we assign "options" object directly to the DOM element. FTW!
  751. */
  752. if(_isJQ) {
  753. jqEl.data('magnificPopup', options);
  754. } else {
  755. jqEl[0].magnificPopup = options;
  756. }
  757. mfp.addGroup(jqEl, options);
  758. }
  759. return jqEl;
  760. };
  761. /*>>core*/
  762. /*>>inline*/
  763. var INLINE_NS = 'inline',
  764. _hiddenClass,
  765. _inlinePlaceholder,
  766. _lastInlineElement,
  767. _putInlineElementsBack = function() {
  768. if(_lastInlineElement) {
  769. _inlinePlaceholder.after( _lastInlineElement.addClass(_hiddenClass) ).detach();
  770. _lastInlineElement = null;
  771. }
  772. };
  773. $.magnificPopup.registerModule(INLINE_NS, {
  774. options: {
  775. hiddenClass: 'hide', // will be appended with `mfp-` prefix
  776. markup: '',
  777. tNotFound: 'Content not found'
  778. },
  779. proto: {
  780. initInline: function() {
  781. mfp.types.push(INLINE_NS);
  782. _mfpOn(CLOSE_EVENT+'.'+INLINE_NS, function() {
  783. _putInlineElementsBack();
  784. });
  785. },
  786. getInline: function(item, template) {
  787. _putInlineElementsBack();
  788. if(item.src) {
  789. var inlineSt = mfp.st.inline,
  790. el = $(item.src);
  791. if(el.length) {
  792. // If target element has parent - we replace it with placeholder and put it back after popup is closed
  793. var parent = el[0].parentNode;
  794. if(parent && parent.tagName) {
  795. if(!_inlinePlaceholder) {
  796. _hiddenClass = inlineSt.hiddenClass;
  797. _inlinePlaceholder = _getEl(_hiddenClass);
  798. _hiddenClass = 'mfp-'+_hiddenClass;
  799. }
  800. // replace target inline element with placeholder
  801. _lastInlineElement = el.after(_inlinePlaceholder).detach().removeClass(_hiddenClass);
  802. }
  803. mfp.updateStatus('ready');
  804. } else {
  805. mfp.updateStatus('error', inlineSt.tNotFound);
  806. el = $('<div>');
  807. }
  808. item.inlineElement = el;
  809. return el;
  810. }
  811. mfp.updateStatus('ready');
  812. mfp._parseMarkup(template, {}, item);
  813. return template;
  814. }
  815. }
  816. });
  817. /*>>inline*/
  818. /*>>ajax*/
  819. var AJAX_NS = 'ajax',
  820. _ajaxCur,
  821. _removeAjaxCursor = function() {
  822. if(_ajaxCur) {
  823. $(document.body).removeClass(_ajaxCur);
  824. }
  825. },
  826. _destroyAjaxRequest = function() {
  827. _removeAjaxCursor();
  828. if(mfp.req) {
  829. mfp.req.abort();
  830. }
  831. };
  832. $.magnificPopup.registerModule(AJAX_NS, {
  833. options: {
  834. settings: null,
  835. cursor: 'mfp-ajax-cur',
  836. tError: '<a href="%url%">The content</a> could not be loaded.'
  837. },
  838. proto: {
  839. initAjax: function() {
  840. mfp.types.push(AJAX_NS);
  841. _ajaxCur = mfp.st.ajax.cursor;
  842. _mfpOn(CLOSE_EVENT+'.'+AJAX_NS, _destroyAjaxRequest);
  843. _mfpOn('BeforeChange.' + AJAX_NS, _destroyAjaxRequest);
  844. },
  845. getAjax: function(item) {
  846. if(_ajaxCur) {
  847. $(document.body).addClass(_ajaxCur);
  848. }
  849. mfp.updateStatus('loading');
  850. var opts = $.extend({
  851. url: item.src,
  852. success: function(data, textStatus, jqXHR) {
  853. var temp = {
  854. data:data,
  855. xhr:jqXHR
  856. };
  857. _mfpTrigger('ParseAjax', temp);
  858. mfp.appendContent( $(temp.data), AJAX_NS );
  859. item.finished = true;
  860. _removeAjaxCursor();
  861. mfp._setFocus();
  862. setTimeout(function() {
  863. mfp.wrap.addClass(READY_CLASS);
  864. }, 16);
  865. mfp.updateStatus('ready');
  866. _mfpTrigger('AjaxContentAdded');
  867. },
  868. error: function() {
  869. _removeAjaxCursor();
  870. item.finished = item.loadError = true;
  871. mfp.updateStatus('error', mfp.st.ajax.tError.replace('%url%', item.src));
  872. }
  873. }, mfp.st.ajax.settings);
  874. mfp.req = $.ajax(opts);
  875. return '';
  876. }
  877. }
  878. });
  879. /*>>ajax*/
  880. /*>>image*/
  881. var _imgInterval,
  882. _getTitle = function(item) {
  883. if(item.data && item.data.title !== undefined)
  884. return item.data.title;
  885. var src = mfp.st.image.titleSrc;
  886. if(src) {
  887. if($.isFunction(src)) {
  888. return src.call(mfp, item);
  889. } else if(item.el) {
  890. return item.el.attr(src) || '';
  891. }
  892. }
  893. return '';
  894. };
  895. $.magnificPopup.registerModule('image', {
  896. options: {
  897. markup: '<div class="mfp-figure">'+
  898. '<div class="mfp-close"></div>'+
  899. '<figure>'+
  900. '<div class="mfp-img"></div>'+
  901. '<figcaption>'+
  902. '<div class="mfp-bottom-bar">'+
  903. '<div class="mfp-title"></div>'+
  904. '<div class="mfp-counter"></div>'+
  905. '</div>'+
  906. '</figcaption>'+
  907. '</figure>'+
  908. '</div>',
  909. cursor: 'mfp-zoom-out-cur',
  910. titleSrc: 'title',
  911. verticalFit: true,
  912. tError: '<a href="%url%">The image</a> could not be loaded.'
  913. },
  914. proto: {
  915. initImage: function() {
  916. var imgSt = mfp.st.image,
  917. ns = '.image';
  918. mfp.types.push('image');
  919. _mfpOn(OPEN_EVENT+ns, function() {
  920. if(mfp.currItem.type === 'image' && imgSt.cursor) {
  921. $(document.body).addClass(imgSt.cursor);
  922. }
  923. });
  924. _mfpOn(CLOSE_EVENT+ns, function() {
  925. if(imgSt.cursor) {
  926. $(document.body).removeClass(imgSt.cursor);
  927. }
  928. _window.off('resize' + EVENT_NS);
  929. });
  930. _mfpOn('Resize'+ns, mfp.resizeImage);
  931. if(mfp.isLowIE) {
  932. _mfpOn('AfterChange', mfp.resizeImage);
  933. }
  934. },
  935. resizeImage: function() {
  936. var item = mfp.currItem;
  937. if(!item || !item.img) return;
  938. if(mfp.st.image.verticalFit) {
  939. var decr = 0;
  940. // fix box-sizing in ie7/8
  941. if(mfp.isLowIE) {
  942. decr = parseInt(item.img.css('padding-top'), 10) + parseInt(item.img.css('padding-bottom'),10);
  943. }
  944. item.img.css('max-height', mfp.wH-decr);
  945. }
  946. },
  947. _onImageHasSize: function(item) {
  948. if(item.img) {
  949. item.hasSize = true;
  950. if(_imgInterval) {
  951. clearInterval(_imgInterval);
  952. }
  953. item.isCheckingImgSize = false;
  954. _mfpTrigger('ImageHasSize', item);
  955. if(item.imgHidden) {
  956. if(mfp.content)
  957. mfp.content.removeClass('mfp-loading');
  958. item.imgHidden = false;
  959. }
  960. }
  961. },
  962. /**
  963. * Function that loops until the image has size to display elements that rely on it asap
  964. */
  965. findImageSize: function(item) {
  966. var counter = 0,
  967. img = item.img[0],
  968. mfpSetInterval = function(delay) {
  969. if(_imgInterval) {
  970. clearInterval(_imgInterval);
  971. }
  972. // decelerating interval that checks for size of an image
  973. _imgInterval = setInterval(function() {
  974. if(img.naturalWidth > 0) {
  975. mfp._onImageHasSize(item);
  976. return;
  977. }
  978. if(counter > 200) {
  979. clearInterval(_imgInterval);
  980. }
  981. counter++;
  982. if(counter === 3) {
  983. mfpSetInterval(10);
  984. } else if(counter === 40) {
  985. mfpSetInterval(50);
  986. } else if(counter === 100) {
  987. mfpSetInterval(500);
  988. }
  989. }, delay);
  990. };
  991. mfpSetInterval(1);
  992. },
  993. getImage: function(item, template) {
  994. var guard = 0,
  995. // image load complete handler
  996. onLoadComplete = function() {
  997. if(item) {
  998. if (item.img[0].complete) {
  999. item.img.off('.mfploader');
  1000. if(item === mfp.currItem){
  1001. mfp._onImageHasSize(item);
  1002. mfp.updateStatus('ready');
  1003. }
  1004. item.hasSize = true;
  1005. item.loaded = true;
  1006. _mfpTrigger('ImageLoadComplete');
  1007. }
  1008. else {
  1009. // if image complete check fails 200 times (20 sec), we assume that there was an error.
  1010. guard++;
  1011. if(guard < 200) {
  1012. setTimeout(onLoadComplete,100);
  1013. } else {
  1014. onLoadError();
  1015. }
  1016. }
  1017. }
  1018. },
  1019. // image error handler
  1020. onLoadError = function() {
  1021. if(item) {
  1022. item.img.off('.mfploader');
  1023. if(item === mfp.currItem){
  1024. mfp._onImageHasSize(item);
  1025. mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) );
  1026. }
  1027. item.hasSize = true;
  1028. item.loaded = true;
  1029. item.loadError = true;
  1030. }
  1031. },
  1032. imgSt = mfp.st.image;
  1033. var el = template.find('.mfp-img');
  1034. if(el.length) {
  1035. var img = document.createElement('img');
  1036. img.className = 'mfp-img';
  1037. if(item.el && item.el.find('img').length) {
  1038. img.alt = item.el.find('img').attr('alt');
  1039. }
  1040. item.img = $(img).on('load.mfploader', onLoadComplete).on('error.mfploader', onLoadError);
  1041. img.src = item.src;
  1042. // without clone() "error" event is not firing when IMG is replaced by new IMG
  1043. // TODO: find a way to avoid such cloning
  1044. if(el.is('img')) {
  1045. item.img = item.img.clone();
  1046. }
  1047. img = item.img[0];
  1048. if(img.naturalWidth > 0) {
  1049. item.hasSize = true;
  1050. } else if(!img.width) {
  1051. item.hasSize = false;
  1052. }
  1053. }
  1054. mfp._parseMarkup(template, {
  1055. title: _getTitle(item),
  1056. img_replaceWith: item.img
  1057. }, item);
  1058. mfp.resizeImage();
  1059. if(item.hasSize) {
  1060. if(_imgInterval) clearInterval(_imgInterval);
  1061. if(item.loadError) {
  1062. template.addClass('mfp-loading');
  1063. mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) );
  1064. } else {
  1065. template.removeClass('mfp-loading');
  1066. mfp.updateStatus('ready');
  1067. }
  1068. return template;
  1069. }
  1070. mfp.updateStatus('loading');
  1071. item.loading = true;
  1072. if(!item.hasSize) {
  1073. item.imgHidden = true;
  1074. template.addClass('mfp-loading');
  1075. mfp.findImageSize(item);
  1076. }
  1077. return template;
  1078. }
  1079. }
  1080. });
  1081. /*>>image*/
  1082. /*>>zoom*/
  1083. var hasMozTransform,
  1084. getHasMozTransform = function() {
  1085. if(hasMozTransform === undefined) {
  1086. hasMozTransform = document.createElement('p').style.MozTransform !== undefined;
  1087. }
  1088. return hasMozTransform;
  1089. };
  1090. $.magnificPopup.registerModule('zoom', {
  1091. options: {
  1092. enabled: false,
  1093. easing: 'ease-in-out',
  1094. duration: 300,
  1095. opener: function(element) {
  1096. return element.is('img') ? element : element.find('img');
  1097. }
  1098. },
  1099. proto: {
  1100. initZoom: function() {
  1101. var zoomSt = mfp.st.zoom,
  1102. ns = '.zoom',
  1103. image;
  1104. if(!zoomSt.enabled || !mfp.supportsTransition) {
  1105. return;
  1106. }
  1107. var duration = zoomSt.duration,
  1108. getElToAnimate = function(image) {
  1109. var newImg = image.clone().removeAttr('style').removeAttr('class').addClass('mfp-animated-image'),
  1110. transition = 'all '+(zoomSt.duration/1000)+'s ' + zoomSt.easing,
  1111. cssObj = {
  1112. position: 'fixed',
  1113. zIndex: 9999,
  1114. left: 0,
  1115. top: 0,
  1116. '-webkit-backface-visibility': 'hidden'
  1117. },
  1118. t = 'transition';
  1119. cssObj['-webkit-'+t] = cssObj['-moz-'+t] = cssObj['-o-'+t] = cssObj[t] = transition;
  1120. newImg.css(cssObj);
  1121. return newImg;
  1122. },
  1123. showMainContent = function() {
  1124. mfp.content.css('visibility', 'visible');
  1125. },
  1126. openTimeout,
  1127. animatedImg;
  1128. _mfpOn('BuildControls'+ns, function() {
  1129. if(mfp._allowZoom()) {
  1130. clearTimeout(openTimeout);
  1131. mfp.content.css('visibility', 'hidden');
  1132. // Basically, all code below does is clones existing image, puts in on top of the current one and animated it
  1133. image = mfp._getItemToZoom();
  1134. if(!image) {
  1135. showMainContent();
  1136. return;
  1137. }
  1138. animatedImg = getElToAnimate(image);
  1139. animatedImg.css( mfp._getOffset() );
  1140. mfp.wrap.append(animatedImg);
  1141. openTimeout = setTimeout(function() {
  1142. animatedImg.css( mfp._getOffset( true ) );
  1143. openTimeout = setTimeout(function() {
  1144. showMainContent();
  1145. setTimeout(function() {
  1146. animatedImg.remove();
  1147. image = animatedImg = null;
  1148. _mfpTrigger('ZoomAnimationEnded');
  1149. }, 16); // avoid blink when switching images
  1150. }, duration); // this timeout equals animation duration
  1151. }, 16); // by adding this timeout we avoid short glitch at the beginning of animation
  1152. // Lots of timeouts...
  1153. }
  1154. });
  1155. _mfpOn(BEFORE_CLOSE_EVENT+ns, function() {
  1156. if(mfp._allowZoom()) {
  1157. clearTimeout(openTimeout);
  1158. mfp.st.removalDelay = duration;
  1159. if(!image) {
  1160. image = mfp._getItemToZoom();
  1161. if(!image) {
  1162. return;
  1163. }
  1164. animatedImg = getElToAnimate(image);
  1165. }
  1166. animatedImg.css( mfp._getOffset(true) );
  1167. mfp.wrap.append(animatedImg);
  1168. mfp.content.css('visibility', 'hidden');
  1169. setTimeout(function() {
  1170. animatedImg.css( mfp._getOffset() );
  1171. }, 16);
  1172. }
  1173. });
  1174. _mfpOn(CLOSE_EVENT+ns, function() {
  1175. if(mfp._allowZoom()) {
  1176. showMainContent();
  1177. if(animatedImg) {
  1178. animatedImg.remove();
  1179. }
  1180. image = null;
  1181. }
  1182. });
  1183. },
  1184. _allowZoom: function() {
  1185. return mfp.currItem.type === 'image';
  1186. },
  1187. _getItemToZoom: function() {
  1188. if(mfp.currItem.hasSize) {
  1189. return mfp.currItem.img;
  1190. } else {
  1191. return false;
  1192. }
  1193. },
  1194. // Get element postion relative to viewport
  1195. _getOffset: function(isLarge) {
  1196. var el;
  1197. if(isLarge) {
  1198. el = mfp.currItem.img;
  1199. } else {
  1200. el = mfp.st.zoom.opener(mfp.currItem.el || mfp.currItem);
  1201. }
  1202. var offset = el.offset();
  1203. var paddingTop = parseInt(el.css('padding-top'),10);
  1204. var paddingBottom = parseInt(el.css('padding-bottom'),10);
  1205. offset.top -= ( $(window).scrollTop() - paddingTop );
  1206. /*
  1207. Animating left + top + width/height looks glitchy in Firefox, but perfect in Chrome. And vice-versa.
  1208. */
  1209. var obj = {
  1210. width: el.width(),
  1211. // fix Zepto height+padding issue
  1212. height: (_isJQ ? el.innerHeight() : el[0].offsetHeight) - paddingBottom - paddingTop
  1213. };
  1214. // I hate to do this, but there is no another option
  1215. if( getHasMozTransform() ) {
  1216. obj['-moz-transform'] = obj['transform'] = 'translate(' + offset.left + 'px,' + offset.top + 'px)';
  1217. } else {
  1218. obj.left = offset.left;
  1219. obj.top = offset.top;
  1220. }
  1221. return obj;
  1222. }
  1223. }
  1224. });
  1225. /*>>zoom*/
  1226. /*>>iframe*/
  1227. var IFRAME_NS = 'iframe',
  1228. _emptyPage = '//about:blank',
  1229. _fixIframeBugs = function(isShowing) {
  1230. if(mfp.currTemplate[IFRAME_NS]) {
  1231. var el = mfp.currTemplate[IFRAME_NS].find('iframe');
  1232. if(el.length) {
  1233. // reset src after the popup is closed to avoid "video keeps playing after popup is closed" bug
  1234. if(!isShowing) {
  1235. el[0].src = _emptyPage;
  1236. }
  1237. // IE8 black screen bug fix
  1238. if(mfp.isIE8) {
  1239. el.css('display', isShowing ? 'block' : 'none');
  1240. }
  1241. }
  1242. }
  1243. };
  1244. $.magnificPopup.registerModule(IFRAME_NS, {
  1245. options: {
  1246. markup: '<div class="mfp-iframe-scaler">'+
  1247. '<div class="mfp-close"></div>'+
  1248. '<iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe>'+
  1249. '</div>',
  1250. srcAction: 'iframe_src',
  1251. // we don't care and support only one default type of URL by default
  1252. patterns: {
  1253. youtube: {
  1254. index: 'youtube.com',
  1255. id: 'v=',
  1256. src: '//www.youtube.com/embed/%id%?autoplay=1'
  1257. },
  1258. vimeo: {
  1259. index: 'vimeo.com/',
  1260. id: '/',
  1261. src: '//player.vimeo.com/video/%id%?autoplay=1'
  1262. },
  1263. gmaps: {
  1264. index: '//maps.google.',
  1265. src: '%id%&output=embed'
  1266. }
  1267. }
  1268. },
  1269. proto: {
  1270. initIframe: function() {
  1271. mfp.types.push(IFRAME_NS);
  1272. _mfpOn('BeforeChange', function(e, prevType, newType) {
  1273. if(prevType !== newType) {
  1274. if(prevType === IFRAME_NS) {
  1275. _fixIframeBugs(); // iframe if removed
  1276. } else if(newType === IFRAME_NS) {
  1277. _fixIframeBugs(true); // iframe is showing
  1278. }
  1279. }// else {
  1280. // iframe source is switched, don't do anything
  1281. //}
  1282. });
  1283. _mfpOn(CLOSE_EVENT + '.' + IFRAME_NS, function() {
  1284. _fixIframeBugs();
  1285. });
  1286. },
  1287. getIframe: function(item, template) {
  1288. var embedSrc = item.src;
  1289. var iframeSt = mfp.st.iframe;
  1290. $.each(iframeSt.patterns, function() {
  1291. if(embedSrc.indexOf( this.index ) > -1) {
  1292. if(this.id) {
  1293. if(typeof this.id === 'string') {
  1294. embedSrc = embedSrc.substr(embedSrc.lastIndexOf(this.id)+this.id.length, embedSrc.length);
  1295. } else {
  1296. embedSrc = this.id.call( this, embedSrc );
  1297. }
  1298. }
  1299. embedSrc = this.src.replace('%id%', embedSrc );
  1300. return false; // break;
  1301. }
  1302. });
  1303. var dataObj = {};
  1304. if(iframeSt.srcAction) {
  1305. dataObj[iframeSt.srcAction] = embedSrc;
  1306. }
  1307. mfp._parseMarkup(template, dataObj, item);
  1308. mfp.updateStatus('ready');
  1309. return template;
  1310. }
  1311. }
  1312. });
  1313. /*>>iframe*/
  1314. /*>>gallery*/
  1315. /**
  1316. * Get looped index depending on number of slides
  1317. */
  1318. var _getLoopedId = function(index) {
  1319. var numSlides = mfp.items.length;
  1320. if(index > numSlides - 1) {
  1321. return index - numSlides;
  1322. } else if(index < 0) {
  1323. return numSlides + index;
  1324. }
  1325. return index;
  1326. },
  1327. _replaceCurrTotal = function(text, curr, total) {
  1328. return text.replace(/%curr%/gi, curr + 1).replace(/%total%/gi, total);
  1329. };
  1330. $.magnificPopup.registerModule('gallery', {
  1331. options: {
  1332. enabled: false,
  1333. arrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',
  1334. preload: [0,2],
  1335. navigateByImgClick: true,
  1336. arrows: true,
  1337. tPrev: 'Previous (Left arrow key)',
  1338. tNext: 'Next (Right arrow key)',
  1339. tCounter: '%curr% of %total%'
  1340. },
  1341. proto: {
  1342. initGallery: function() {
  1343. var gSt = mfp.st.gallery,
  1344. ns = '.mfp-gallery';
  1345. mfp.direction = true; // true - next, false - prev
  1346. if(!gSt || !gSt.enabled ) return false;
  1347. _wrapClasses += ' mfp-gallery';
  1348. _mfpOn(OPEN_EVENT+ns, function() {
  1349. if(gSt.navigateByImgClick) {
  1350. mfp.wrap.on('click'+ns, '.mfp-img', function() {
  1351. if(mfp.items.length > 1) {
  1352. mfp.next();
  1353. return false;
  1354. }
  1355. });
  1356. }
  1357. _document.on('keydown'+ns, function(e) {
  1358. if (e.keyCode === 37) {
  1359. mfp.prev();
  1360. } else if (e.keyCode === 39) {
  1361. mfp.next();
  1362. }
  1363. });
  1364. });
  1365. _mfpOn('UpdateStatus'+ns, function(e, data) {
  1366. if(data.text) {
  1367. data.text = _replaceCurrTotal(data.text, mfp.currItem.index, mfp.items.length);
  1368. }
  1369. });
  1370. _mfpOn(MARKUP_PARSE_EVENT+ns, function(e, element, values, item) {
  1371. var l = mfp.items.length;
  1372. values.counter = l > 1 ? _replaceCurrTotal(gSt.tCounter, item.index, l) : '';
  1373. });
  1374. _mfpOn('BuildControls' + ns, function() {
  1375. if(mfp.items.length > 1 && gSt.arrows && !mfp.arrowLeft) {
  1376. var markup = gSt.arrowMarkup,
  1377. arrowLeft = mfp.arrowLeft = $( markup.replace(/%title%/gi, gSt.tPrev).replace(/%dir%/gi, 'left') ).addClass(PREVENT_CLOSE_CLASS),
  1378. arrowRight = mfp.arrowRight = $( markup.replace(/%title%/gi, gSt.tNext).replace(/%dir%/gi, 'right') ).addClass(PREVENT_CLOSE_CLASS);
  1379. arrowLeft.click(function() {
  1380. mfp.prev();
  1381. });
  1382. arrowRight.click(function() {
  1383. mfp.next();
  1384. });
  1385. mfp.container.append(arrowLeft.add(arrowRight));
  1386. }
  1387. });
  1388. _mfpOn(CHANGE_EVENT+ns, function() {
  1389. if(mfp._preloadTimeout) clearTimeout(mfp._preloadTimeout);
  1390. mfp._preloadTimeout = setTimeout(function() {
  1391. mfp.preloadNearbyImages();
  1392. mfp._preloadTimeout = null;
  1393. }, 16);
  1394. });
  1395. _mfpOn(CLOSE_EVENT+ns, function() {
  1396. _document.off(ns);
  1397. mfp.wrap.off('click'+ns);
  1398. mfp.arrowRight = mfp.arrowLeft = null;
  1399. });
  1400. },
  1401. next: function() {
  1402. mfp.direction = true;
  1403. mfp.index = _getLoopedId(mfp.index + 1);
  1404. mfp.updateItemHTML();
  1405. },
  1406. prev: function() {
  1407. mfp.direction = false;
  1408. mfp.index = _getLoopedId(mfp.index - 1);
  1409. mfp.updateItemHTML();
  1410. },
  1411. goTo: function(newIndex) {
  1412. mfp.direction = (newIndex >= mfp.index);
  1413. mfp.index = newIndex;
  1414. mfp.updateItemHTML();
  1415. },
  1416. preloadNearbyImages: function() {
  1417. var p = mfp.st.gallery.preload,
  1418. preloadBefore = Math.min(p[0], mfp.items.length),
  1419. preloadAfter = Math.min(p[1], mfp.items.length),
  1420. i;
  1421. for(i = 1; i <= (mfp.direction ? preloadAfter : preloadBefore); i++) {
  1422. mfp._preloadItem(mfp.index+i);
  1423. }
  1424. for(i = 1; i <= (mfp.direction ? preloadBefore : preloadAfter); i++) {
  1425. mfp._preloadItem(mfp.index-i);
  1426. }
  1427. },
  1428. _preloadItem: function(index) {
  1429. index = _getLoopedId(index);
  1430. if(mfp.items[index].preloaded) {
  1431. return;
  1432. }
  1433. var item = mfp.items[index];
  1434. if(!item.parsed) {
  1435. item = mfp.parseEl( index );
  1436. }
  1437. _mfpTrigger('LazyLoad', item);
  1438. if(item.type === 'image') {
  1439. item.img = $('<img class="mfp-img" />').on('load.mfploader', function() {
  1440. item.hasSize = true;
  1441. }).on('error.mfploader', function() {
  1442. item.hasSize = true;
  1443. item.loadError = true;
  1444. _mfpTrigger('LazyLoadError', item);
  1445. }).attr('src', item.src);
  1446. }
  1447. item.preloaded = true;
  1448. }
  1449. }
  1450. });
  1451. /*>>gallery*/
  1452. /*>>retina*/
  1453. var RETINA_NS = 'retina';
  1454. $.magnificPopup.registerModule(RETINA_NS, {
  1455. options: {
  1456. replaceSrc: function(item) {
  1457. return item.src.replace(/\.\w+$/, function(m) { return '@2x' + m; });
  1458. },
  1459. ratio: 1 // Function or number. Set to 1 to disable.
  1460. },
  1461. proto: {
  1462. initRetina: function() {
  1463. if(window.devicePixelRatio > 1) {
  1464. var st = mfp.st.retina,
  1465. ratio = st.ratio;
  1466. ratio = !isNaN(ratio) ? ratio : ratio();
  1467. if(ratio > 1) {
  1468. _mfpOn('ImageHasSize' + '.' + RETINA_NS, function(e, item) {
  1469. item.img.css({
  1470. 'max-width': item.img[0].naturalWidth / ratio,
  1471. 'width': '100%'
  1472. });
  1473. });
  1474. _mfpOn('ElementParse' + '.' + RETINA_NS, function(e, item) {
  1475. item.src = st.replaceSrc(item, ratio);
  1476. });
  1477. }
  1478. }
  1479. }
  1480. }
  1481. });
  1482. /*>>retina*/
  1483. _checkInstance(); }));