magnify.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'jquery',
  7. 'underscore',
  8. 'magnifier/magnifier'
  9. ], function ($, _) {
  10. 'use strict';
  11. return function (config, element) {
  12. var isTouchEnabled = 'ontouchstart' in document.documentElement,
  13. gallerySelector = '[data-gallery-role="gallery"]',
  14. magnifierSelector = '[data-gallery-role="magnifier"]',
  15. magnifierZoomSelector = '[data-gallery-role="magnifier-zoom"]',
  16. zoomInButtonSelector = '[data-gallery-role="fotorama__zoom-in"]',
  17. zoomOutButtonSelector = '[data-gallery-role="fotorama__zoom-out"]',
  18. fullscreenImageSelector = '[data-gallery-role="stage-shaft"] [data-active="true"] .fotorama__img--full',
  19. imageDraggableClass = 'fotorama__img--draggable',
  20. imageZoommable = 'fotorama__img--zoommable',
  21. zoomInLoaded = 'zoom-in-loaded',
  22. zoomOutLoaded = 'zoom-out-loaded',
  23. zoomInDisabled = 'fotorama__zoom-in--disabled',
  24. zoomOutDisabled = 'fotorama__zoom-out--disabled',
  25. keyboardNavigation,
  26. videoContainerClass = 'fotorama-video-container',
  27. hideMagnifier,
  28. dragFlag,
  29. endX,
  30. transitionEnabled,
  31. transitionActive = false,
  32. tapFlag = 0,
  33. allowZoomOut = false,
  34. allowZoomIn = true;
  35. (function () {
  36. var style = document.documentElement.style,
  37. transitionEnabled = style.transition !== undefined ||
  38. style.WebkitTransition !== undefined ||
  39. style.MozTransition !== undefined ||
  40. style.MsTransition !== undefined ||
  41. style.OTransition !== undefined;
  42. })();
  43. /**
  44. * Return width and height of original image
  45. * @param img original image node
  46. * @returns {{rw: number, rh: number}}
  47. */
  48. function getImageSize(img) {
  49. return {
  50. rw: img.naturalWidth,
  51. rh: img.naturalHeight
  52. };
  53. }
  54. /**
  55. * Sets min-height and min-width for image to avoid transition bug
  56. * @param $image - fullscreen image
  57. */
  58. function calculateMinSize($image) {
  59. var minHeight,
  60. minWidth,
  61. height = $image.height(),
  62. width = $image.width(),
  63. parentHeight = $image.parent().height(),
  64. parentWidth = $image.parent().width();
  65. if (width > parentWidth || height > parentHeight) {
  66. if (width / height < parentWidth / parentHeight) {
  67. minHeight = parentHeight;
  68. minWidth = width * (parentHeight / height);
  69. } else {
  70. minWidth = parentWidth;
  71. minHeight = height * parentWidth / width;
  72. }
  73. $image.css({
  74. 'min-width': minWidth,
  75. 'min-height': minHeight
  76. });
  77. }
  78. }
  79. function toggleZoomable($image, flag) {
  80. if (flag) {
  81. $image.css({
  82. 'min-width': $image.width(),
  83. 'min-height': $image.height(),
  84. 'width': $image.width(),
  85. 'height': $image.height()
  86. }).addClass(imageZoommable);
  87. } else {
  88. $image.css({
  89. width: '',
  90. height: '',
  91. top: '',
  92. left: '',
  93. right: '',
  94. bottom: ''
  95. }).removeClass(imageZoommable);
  96. calculateMinSize($image);
  97. }
  98. }
  99. function resetVars($image) {
  100. allowZoomIn = true;
  101. allowZoomOut = dragFlag = transitionActive = false;
  102. $image.hasClass(imageDraggableClass) && $image.removeClass(imageDraggableClass);
  103. toggleZoomable($image, false);
  104. }
  105. /**
  106. * Set state for zoom controls.
  107. * If state is true, zoom controls will be visible.
  108. * IF state is false, zoom controls will be hidden.
  109. * @param isHide
  110. */
  111. function hideZoomControls(isHide) {
  112. if (isHide) {
  113. $(zoomInButtonSelector).addClass(zoomInDisabled);
  114. $(zoomOutButtonSelector).addClass(zoomOutDisabled);
  115. } else {
  116. $(zoomInButtonSelector).removeClass(zoomInDisabled);
  117. $(zoomOutButtonSelector).removeClass(zoomOutDisabled);
  118. }
  119. }
  120. /**
  121. * Asynchronus control visibility of zoom buttons.
  122. * If image bigger than her wrapper. Zoom controls must visible.
  123. * @param path - image source path
  124. * @param $image
  125. */
  126. function asyncToggleZoomButtons(path, $image) {
  127. var img = new Image();
  128. img.onload = function () {
  129. this.height > $image.parent().height() || this.width > $image.parent().width() ?
  130. hideZoomControls(false) : hideZoomControls(true);
  131. };
  132. img.src = path;
  133. }
  134. /**
  135. * Control visibility of zoom buttons.
  136. * Zoom controls must be invisible for video content and touch devices.
  137. * On touch devices active pinchIn/pinchOut.
  138. * @param $image
  139. * @param isTouchScreen - true for touch devices
  140. * @param isVideoActiveFrame - true for active video frame
  141. */
  142. function toggleZoomButtons($image, isTouchScreen, isVideoActiveFrame) {
  143. var path = $image.attr('src');
  144. if (path && !isTouchScreen && !isVideoActiveFrame) {
  145. asyncToggleZoomButtons(path, $image);
  146. } else {
  147. hideZoomControls(true);
  148. }
  149. }
  150. /**
  151. * Handle resize event in fullscreen.
  152. * @param $image - Fullscreen image.
  153. * @param e - Event.
  154. */
  155. function resizeHandler(e, $image) {
  156. var imageSize,
  157. parentWidth,
  158. parentHeight,
  159. isImageSmall,
  160. isImageFit;
  161. if (!e.data.$image || !e.data.$image.length)
  162. return;
  163. imageSize = getImageSize($(fullscreenImageSelector)[0]);
  164. parentWidth = e.data.$image.parent().width();
  165. parentHeight = e.data.$image.parent().height();
  166. isImageSmall = parentWidth >= imageSize.rw && parentHeight >= imageSize.rh;
  167. isImageFit = parentWidth > e.data.$image.width() && parentHeight > e.data.$image.height();
  168. toggleZoomButtons(e.data.$image, isTouchEnabled, checkForVideo(e.data.fotorama.activeFrame.$stageFrame));
  169. calculateMinSize(e.data.$image);
  170. if (e.data.$image.hasClass(imageZoommable) && !allowZoomOut || isImageSmall || isImageFit) {
  171. resetVars(e.data.$image);
  172. }
  173. if (!isImageSmall) {
  174. toggleStandartNavigation();
  175. }
  176. }
  177. function getTopValue($image, topProp, step, height, containerHeight) {
  178. var top;
  179. if (parseInt($image.css('marginTop')) || parseInt($image.css('marginLeft'))) {
  180. top = dragFlag ? topProp - step / 4 : 0;
  181. top = top < containerHeight - height ? containerHeight - height : top;
  182. top = top > height - containerHeight ? height - containerHeight : top;
  183. } else {
  184. top = topProp + step / 2;
  185. top = top < containerHeight - height ? containerHeight - height : top;
  186. top = top > 0 ? 0 : top;
  187. if (!dragFlag && step < 0) {
  188. top = top < (containerHeight - height) / 2 ? (containerHeight - height) / 2 : top;
  189. }
  190. }
  191. return top;
  192. }
  193. function getLeftValue(leftProp, step, width, containerWidth) {
  194. var left;
  195. left = leftProp + step / 2;
  196. left = left < containerWidth - width ? containerWidth - width : left;
  197. left = left > 0 ? 0 : left;
  198. if (!dragFlag && step < 0) {
  199. left = left < (containerWidth - width) / 2 ? (containerWidth - width) / 2 : left;
  200. }
  201. return left;
  202. }
  203. function checkFullscreenImagePosition($image, dimentions, widthStep, heightStep) {
  204. var $imageContainer,
  205. containerWidth,
  206. containerHeight,
  207. settings,
  208. top,
  209. left,
  210. right,
  211. bottom,
  212. ratio;
  213. if ($(gallerySelector).data('fotorama').fullScreen) {
  214. transitionActive = true;
  215. $imageContainer = $image.parent();
  216. containerWidth = $imageContainer.width();
  217. containerHeight = $imageContainer.height();
  218. top = $image.position().top;
  219. left = $image.position().left;
  220. ratio = $image.width() / $image.height();
  221. dimentions.height = isNaN(dimentions.height) ? dimentions.width / ratio : dimentions.height;
  222. dimentions.width = isNaN(dimentions.width) ? dimentions.height * ratio : dimentions.width;
  223. top = dimentions.height >= containerHeight ?
  224. getTopValue($image, top, heightStep, dimentions.height, containerHeight) : 0;
  225. left = dimentions.width >= containerWidth ?
  226. getLeftValue(left, widthStep, dimentions.width, containerWidth) : 0;
  227. right = dragFlag && left < (containerWidth - dimentions.width) / 2 ? 0 : left;
  228. bottom = dragFlag ? 0 : top;
  229. settings = $.extend(dimentions, {
  230. top: top,
  231. left: left,
  232. right: right
  233. });
  234. $image.css(settings);
  235. }
  236. }
  237. /**
  238. * Toggles fotorama's keyboard and mouse/touch navigation.
  239. */
  240. function toggleStandartNavigation() {
  241. var $selectable =
  242. $('a[href], area[href], input, select, textarea, button, iframe, object, embed, *[tabindex], *[contenteditable]')
  243. .not('[tabindex=-1], [disabled], :hidden'),
  244. fotorama = $(gallerySelector).data('fotorama'),
  245. $focus = $(':focus'),
  246. index;
  247. if (fotorama.fullScreen) {
  248. $selectable.each(function (number) {
  249. if ($(this).is($focus)) {
  250. index = number;
  251. }
  252. });
  253. fotorama.setOptions({
  254. swipe: !allowZoomOut,
  255. keyboard: !allowZoomOut
  256. });
  257. if (_.isNumber(index)) {
  258. $selectable.eq(index).focus();
  259. }
  260. }
  261. }
  262. function zoomIn(e, xStep, yStep) {
  263. var $image,
  264. imgOriginalSize,
  265. imageWidth,
  266. imageHeight,
  267. zoomWidthStep,
  268. zoomHeightStep,
  269. widthResult,
  270. heightResult,
  271. ratio,
  272. dimentions = {};
  273. if (allowZoomIn && (!transitionEnabled || !transitionActive) && (isTouchEnabled ||
  274. !$(zoomInButtonSelector).hasClass(zoomInDisabled))) {
  275. $image = $(fullscreenImageSelector);
  276. imgOriginalSize = getImageSize($image[0]);
  277. imageWidth = $image.width();
  278. imageHeight = $image.height();
  279. ratio = imageWidth / imageHeight;
  280. allowZoomOut = true;
  281. toggleStandartNavigation();
  282. if (!$image.hasClass(imageZoommable)) {
  283. toggleZoomable($image, true);
  284. }
  285. e.preventDefault();
  286. if (imageWidth >= imageHeight) {
  287. zoomWidthStep = xStep || Math.ceil(imageWidth * parseFloat(config.magnifierOpts.fullscreenzoom) / 100);
  288. widthResult = imageWidth + zoomWidthStep;
  289. if (widthResult >= imgOriginalSize.rw) {
  290. widthResult = imgOriginalSize.rw;
  291. zoomWidthStep = xStep || widthResult - imageWidth;
  292. allowZoomIn = false;
  293. }
  294. heightResult = widthResult / ratio;
  295. zoomHeightStep = yStep || heightResult - imageHeight;
  296. } else {
  297. zoomHeightStep = yStep || Math.ceil(imageHeight * parseFloat(config.magnifierOpts.fullscreenzoom) / 100);
  298. heightResult = imageHeight + zoomHeightStep;
  299. if (heightResult >= imgOriginalSize.rh) {
  300. heightResult = imgOriginalSize.rh;
  301. zoomHeightStep = yStep || heightResult - imageHeight;
  302. allowZoomIn = false;
  303. }
  304. widthResult = heightResult * ratio;
  305. zoomWidthStep = xStep || widthResult - imageWidth;
  306. }
  307. if (imageWidth >= imageHeight && imageWidth !== imgOriginalSize.rw) {
  308. dimentions = $.extend(dimentions, {
  309. width: widthResult,
  310. height: 'auto'
  311. });
  312. checkFullscreenImagePosition($image, dimentions, -zoomWidthStep, -zoomHeightStep);
  313. } else if (imageWidth < imageHeight && imageHeight !== imgOriginalSize.rh) {
  314. dimentions = $.extend(dimentions, {
  315. width: 'auto',
  316. height: heightResult
  317. });
  318. checkFullscreenImagePosition($image, dimentions, -zoomWidthStep, -zoomHeightStep);
  319. }
  320. }
  321. return false;
  322. }
  323. function zoomOut(e, xStep, yStep) {
  324. var $image,
  325. widthResult,
  326. heightResult,
  327. dimentions,
  328. parentWidth,
  329. parentHeight,
  330. imageWidth,
  331. imageHeight,
  332. zoomWidthStep,
  333. zoomHeightStep,
  334. ratio,
  335. fitIntoParent;
  336. if (allowZoomOut && (!transitionEnabled || !transitionActive) && (isTouchEnabled ||
  337. !$(zoomOutButtonSelector).hasClass(zoomOutDisabled))) {
  338. allowZoomIn = true;
  339. $image = $(fullscreenImageSelector);
  340. parentWidth = $image.parent().width();
  341. parentHeight = $image.parent().height();
  342. imageWidth = $image.width();
  343. imageHeight = $image.height();
  344. ratio = imageWidth / imageHeight;
  345. e.preventDefault();
  346. if (imageWidth >= imageHeight) {
  347. zoomWidthStep = xStep || Math.ceil(imageWidth * parseFloat(config.magnifierOpts.fullscreenzoom) / 100);
  348. widthResult = imageWidth - zoomWidthStep;
  349. heightResult = widthResult / ratio;
  350. zoomHeightStep = yStep || imageHeight - heightResult;
  351. } else {
  352. zoomHeightStep = yStep || Math.ceil(imageHeight * parseFloat(config.magnifierOpts.fullscreenzoom) / 100);
  353. heightResult = imageHeight - zoomHeightStep;
  354. widthResult = heightResult * ratio;
  355. zoomWidthStep = xStep || imageWidth - widthResult;
  356. }
  357. fitIntoParent = function () {
  358. if (ratio > parentWidth / parentHeight) {
  359. widthResult = parentWidth;
  360. zoomWidthStep = imageWidth - widthResult;
  361. heightResult = widthResult / ratio;
  362. zoomHeightStep = imageHeight - heightResult;
  363. dimentions = {
  364. width: widthResult,
  365. height: 'auto'
  366. };
  367. } else {
  368. heightResult = parentHeight;
  369. zoomHeightStep = imageHeight - heightResult;
  370. widthResult = heightResult * ratio;
  371. zoomWidthStep = imageWidth - widthResult;
  372. dimentions = {
  373. width: 'auto',
  374. height: heightResult
  375. };
  376. }
  377. checkFullscreenImagePosition($image, dimentions, zoomWidthStep, zoomHeightStep);
  378. };
  379. if (imageWidth >= imageHeight) {
  380. if (widthResult > parentWidth) {
  381. dimentions = {
  382. width: widthResult,
  383. height: 'auto'
  384. };
  385. checkFullscreenImagePosition($image, dimentions, zoomWidthStep, zoomHeightStep);
  386. } else if (heightResult > parentHeight) {
  387. dimentions = {
  388. width: widthResult,
  389. height: 'auto'
  390. };
  391. checkFullscreenImagePosition($image, dimentions, zoomWidthStep, zoomHeightStep);
  392. } else {
  393. allowZoomOut = dragFlag = false;
  394. toggleStandartNavigation();
  395. fitIntoParent();
  396. }
  397. } else if (heightResult > parentHeight) {
  398. dimentions = {
  399. width: 'auto',
  400. height: heightResult
  401. };
  402. checkFullscreenImagePosition($image, dimentions, zoomWidthStep, zoomHeightStep);
  403. } else if (widthResult > parentWidth) {
  404. dimentions = {
  405. width: 'auto',
  406. height: heightResult
  407. };
  408. checkFullscreenImagePosition($image, dimentions, zoomWidthStep, zoomHeightStep);
  409. } else {
  410. allowZoomOut = dragFlag = false;
  411. toggleStandartNavigation();
  412. fitIntoParent();
  413. }
  414. }
  415. return false;
  416. }
  417. /**
  418. * Bind event on scroll on active item in fotorama
  419. * @param e
  420. * @param fotorama - object of fotorama
  421. */
  422. function mousewheel(e, fotorama, element) {
  423. var $fotoramaStage = fotorama.activeFrame.$stageFrame,
  424. fotoramaStage = $fotoramaStage.get(0);
  425. function onWheel(e) {
  426. var delta = e.deltaY || e.wheelDelta,
  427. ev = e || window.event;
  428. if ($(gallerySelector).data('fotorama').fullScreen) {
  429. if (e.deltaY) {
  430. if (delta > 0) {
  431. zoomOut(ev);
  432. } else {
  433. zoomIn(ev);
  434. }
  435. } else if (delta > 0) {
  436. zoomIn(ev);
  437. } else {
  438. zoomOut(ev);
  439. }
  440. e.preventDefault ? e.preventDefault() : e.returnValue = false;
  441. }
  442. }
  443. if (!$fotoramaStage.hasClass('magnify-wheel-loaded')) {
  444. if (fotoramaStage && fotoramaStage.addEventListener) {
  445. if ('onwheel' in document) {
  446. fotoramaStage.addEventListener('wheel', onWheel);
  447. } else if ('onmousewheel' in document) {
  448. fotoramaStage.addEventListener('mousewheel', onWheel);
  449. } else {
  450. fotoramaStage.addEventListener('MozMousePixelScroll', onWheel);
  451. }
  452. $fotoramaStage.addClass('magnify-wheel-loaded');
  453. }
  454. }
  455. }
  456. /**
  457. * Method which makes draggable picture. Also work on touch devices.
  458. */
  459. function magnifierFullscreen(fotorama) {
  460. var isDragActive = false,
  461. startX,
  462. startY,
  463. imagePosX,
  464. imagePosY,
  465. touch,
  466. swipeSlide,
  467. $gallery = $(gallerySelector),
  468. $image = $(fullscreenImageSelector, $gallery),
  469. $imageContainer = $('[data-gallery-role="stage-shaft"] [data-active="true"]'),
  470. gallery = $gallery.data('fotorama'),
  471. pinchDimention;
  472. swipeSlide = _.throttle(function (direction) {
  473. $(gallerySelector).data('fotorama').show(direction);
  474. }, 500, {
  475. trailing: false
  476. });
  477. /**
  478. * Returns top position value for passed jQuery object.
  479. *
  480. * @param $el
  481. * @return {number}
  482. */
  483. function getTop($el) {
  484. return parseInt($el.get(0).style.top);
  485. }
  486. function shiftImage(dx, dy, e) {
  487. var top = +imagePosY + dy,
  488. left = +imagePosX + dx,
  489. swipeCondition = $image.width() / 10 + 20;
  490. dragFlag = true;
  491. if ($image.offset().left === $imageContainer.offset().left + $imageContainer.width() - $image.width() && e.keyCode === 39 ||
  492. endX - 1 < $imageContainer.offset().left + $imageContainer.width() - $image.width() && dx < 0 &&
  493. _.isNumber(endX) &&
  494. (e.type === 'mousemove' || e.type === 'touchmove' || e.type === 'pointermove' || e.type === 'MSPointerMove')) {
  495. endX = null;
  496. swipeSlide('>');
  497. return;
  498. }
  499. if ($image.offset().left === $imageContainer.offset().left && dx !== 0 && e.keyCode === 37 ||
  500. endX === $imageContainer.offset().left && dx > 0 &&
  501. (e.type === 'mousemove' || e.type === 'touchmove' || e.type === 'pointermove' || e.type === 'MSPointerMove')) {
  502. endX = null;
  503. swipeSlide('<');
  504. return;
  505. }
  506. if ($image.height() > $imageContainer.height()) {
  507. if ($imageContainer.height() > $image.height() + top) {
  508. $image.css('top', $imageContainer.height() - $image.height());
  509. } else {
  510. top = $image.height() - getTop($image) - $imageContainer.height();
  511. dy = dy < top ? dy : top;
  512. $image.css('top', getTop($image) + dy);
  513. }
  514. }
  515. if ($image.width() > $imageContainer.width()) {
  516. if ($imageContainer.offset().left + $imageContainer.width() > left + $image.width()) {
  517. left = $imageContainer.offset().left + $imageContainer.width() - $image.width();
  518. } else {
  519. left = $imageContainer.offset().left < left ? $imageContainer.offset().left : left;
  520. }
  521. $image.offset({
  522. 'left': left
  523. });
  524. $image.css('right', '');
  525. } else if (Math.abs(dy) < 1 && allowZoomOut &&
  526. !(e.type === 'mousemove' || e.type === 'touchmove' || e.type === 'pointermove' || e.type === 'MSPointerMove')) {
  527. dx < 0 ? $(gallerySelector).data('fotorama').show('>') : $(gallerySelector).data('fotorama').show('<');
  528. }
  529. if ($image.width() <= $imageContainer.width() && allowZoomOut &&
  530. (e.type === 'mousemove' || e.type === 'touchmove' || e.type === 'pointermove' || e.type === 'MSPointerMove') &&
  531. Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > swipeCondition) {
  532. dx < 0 ? swipeSlide('>') : swipeSlide('<');
  533. }
  534. }
  535. /**
  536. * Sets image size to original or fit in parent block
  537. * @param e - event object
  538. */
  539. function dblClickHandler(e) {
  540. var imgOriginalSize = getImageSize($image[0]),
  541. proportions;
  542. if (imgOriginalSize.rh < $image.parent().height() && imgOriginalSize.rw < $image.parent().width()) {
  543. return;
  544. }
  545. proportions = imgOriginalSize.rw / imgOriginalSize.rh;
  546. if (allowZoomIn) {
  547. zoomIn(e, imgOriginalSize.rw - $image.width(), imgOriginalSize.rh - $image.height());
  548. } else if (proportions > $imageContainer.width() / $imageContainer.height()) {
  549. zoomOut(e, imgOriginalSize.rw - $imageContainer.width(), imgOriginalSize.rw / proportions);
  550. } else {
  551. zoomOut(e, imgOriginalSize.rw * proportions, imgOriginalSize.rh - $imageContainer.height());
  552. }
  553. }
  554. function detectDoubleTap(e) {
  555. var now = new Date().getTime(),
  556. timesince = now - tapFlag;
  557. if (timesince < 400 && timesince > 0) {
  558. transitionActive = false;
  559. tapFlag = 0;
  560. dblClickHandler(e);
  561. } else {
  562. tapFlag = new Date().getTime();
  563. }
  564. }
  565. if (isTouchEnabled) {
  566. $image.off('tap');
  567. $image.on('tap', function (e) {
  568. if (e.originalEvent.originalEvent.touches.length === 0) {
  569. detectDoubleTap(e);
  570. }
  571. });
  572. } else {
  573. $image.unbind('dblclick');
  574. $image.dblclick(dblClickHandler);
  575. }
  576. if (gallery.fullScreen) {
  577. toggleZoomButtons($image, isTouchEnabled, checkForVideo(fotorama.activeFrame.$stageFrame));
  578. }
  579. function getDimention(event) {
  580. return Math.sqrt(
  581. (event.touches[0].clientX - event.touches[1].clientX) * (event.touches[0].clientX - event.touches[1].clientX) +
  582. (event.touches[0].clientY - event.touches[1].clientY) * (event.touches[0].clientY - event.touches[1].clientY));
  583. }
  584. $image.off(isTouchEnabled ? 'touchstart' : 'pointerdown mousedown MSPointerDown');
  585. $image.on(isTouchEnabled ? 'touchstart' : 'pointerdown mousedown MSPointerDown', function (e) {
  586. if (e && e.originalEvent.touches && e.originalEvent.touches.length >= 2) {
  587. e.preventDefault();
  588. pinchDimention = getDimention(e.originalEvent);
  589. isDragActive = false;
  590. if ($image.hasClass(imageDraggableClass)) {
  591. $image.removeClass(imageDraggableClass);
  592. }
  593. } else if (gallery.fullScreen && (!transitionEnabled || !transitionActive)) {
  594. e.preventDefault();
  595. imagePosY = getTop($image);
  596. imagePosX = $image.offset().left;
  597. if (isTouchEnabled) {
  598. touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
  599. e.clientX = touch.pageX;
  600. e.clientY = touch.pageY;
  601. }
  602. startX = e.clientX || e.originalEvent.clientX;
  603. startY = e.clientY || e.originalEvent.clientY;
  604. isDragActive = true;
  605. }
  606. if ($image.offset() && $image.width() > $imageContainer.width()) {
  607. endX = $image.offset().left;
  608. }
  609. });
  610. $image.off(isTouchEnabled ? 'touchmove' : 'mousemove pointermove MSPointerMove');
  611. $image.on(isTouchEnabled ? 'touchmove' : 'mousemove pointermove MSPointerMove', function (e) {
  612. if (e && e.originalEvent.touches && e.originalEvent.touches.length >= 2) {
  613. e.preventDefault();
  614. var currentDimention = getDimention(e.originalEvent);
  615. if ($image.hasClass(imageDraggableClass)) {
  616. $image.removeClass(imageDraggableClass);
  617. }
  618. if (currentDimention < pinchDimention) {
  619. zoomOut(e);
  620. pinchDimention = currentDimention;
  621. } else if (currentDimention > pinchDimention) {
  622. zoomIn(e);
  623. pinchDimention = currentDimention;
  624. }
  625. } else {
  626. var clientX,
  627. clientY;
  628. if (gallery.fullScreen && isDragActive && (!transitionEnabled || !transitionActive)) {
  629. if (allowZoomOut && !$image.hasClass(imageDraggableClass)) {
  630. $image.addClass(imageDraggableClass);
  631. }
  632. clientX = e.clientX || e.originalEvent.clientX;
  633. clientY = e.clientY || e.originalEvent.clientY;
  634. e.preventDefault();
  635. if (isTouchEnabled) {
  636. touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
  637. clientX = touch.pageX;
  638. clientY = touch.pageY;
  639. }
  640. if (allowZoomOut) {
  641. imagePosY = getTop($(fullscreenImageSelector, $gallery));
  642. shiftImage(clientX - startX, clientY - startY, e);
  643. }
  644. }
  645. }
  646. });
  647. $image.off('transitionend webkitTransitionEnd mozTransitionEnd msTransitionEnd ');
  648. $image.on('transitionend webkitTransitionEnd mozTransitionEnd msTransitionEnd', function () {
  649. transitionActive = false;
  650. });
  651. if (keyboardNavigation) {
  652. $(document).unbind('keydown', keyboardNavigation);
  653. }
  654. /**
  655. * Replaces original navigations with better one
  656. * @param e - event object
  657. */
  658. keyboardNavigation = function (e) {
  659. var step = 40,
  660. $focus = $(':focus'),
  661. isFullScreen = $(gallerySelector).data('fotorama').fullScreen,
  662. initVars = function () {
  663. imagePosX = $(fullscreenImageSelector, $gallery).offset().left;
  664. imagePosY = getTop($(fullscreenImageSelector, $gallery));
  665. };
  666. if (($focus.attr('data-gallery-role') || !$focus.length) && allowZoomOut) {
  667. if (isFullScreen) {
  668. imagePosX = $(fullscreenImageSelector, $(gallerySelector)).offset().left;
  669. imagePosY = getTop($(fullscreenImageSelector, $(gallerySelector)));
  670. }
  671. if (e.keyCode === 39) {
  672. if (isFullScreen) {
  673. initVars();
  674. shiftImage(-step, 0, e);
  675. }
  676. }
  677. if (e.keyCode === 38) {
  678. if (isFullScreen) {
  679. initVars();
  680. shiftImage(0, step, e);
  681. }
  682. }
  683. if (e.keyCode === 37) {
  684. if (isFullScreen) {
  685. initVars();
  686. shiftImage(step, 0, e);
  687. }
  688. }
  689. if (e.keyCode === 40) {
  690. if (isFullScreen) {
  691. e.preventDefault();
  692. initVars();
  693. shiftImage(0, -step, e);
  694. }
  695. }
  696. }
  697. if (e.keyCode === 27 && isFullScreen && allowZoomOut) {
  698. $(gallerySelector).data('fotorama').cancelFullScreen();
  699. }
  700. };
  701. /**
  702. * @todo keyboard navigation through Fotorama Api.
  703. */
  704. $(document).keydown(keyboardNavigation);
  705. $(document).on(isTouchEnabled ? 'touchend' : 'mouseup pointerup MSPointerUp', function (e) {
  706. if (gallery.fullScreen) {
  707. if ($image.offset() && $image.width() > $imageContainer.width()) {
  708. endX = $image.offset().left;
  709. }
  710. isDragActive = false;
  711. $image.removeClass(imageDraggableClass);
  712. }
  713. });
  714. $(window).off('resize', resizeHandler);
  715. $(window).on('resize', {
  716. $image: $image,
  717. fotorama: fotorama
  718. }, resizeHandler);
  719. }
  720. /**
  721. * Hides magnifier preview and zoom blocks.
  722. */
  723. hideMagnifier = function () {
  724. $(magnifierSelector).empty().hide();
  725. $(magnifierZoomSelector).remove();
  726. };
  727. /**
  728. * Check is active frame in gallery include video content.
  729. * If true activeFrame contain video.
  730. * @param $stageFrame - active frame in gallery
  731. * @returns {*|Boolean}
  732. */
  733. function checkForVideo($stageFrame) {
  734. return $stageFrame.hasClass(videoContainerClass);
  735. }
  736. /**
  737. * Hides magnifier on drag and while arrow click.
  738. */
  739. function behaveOnDrag(e, initPos) {
  740. var pos = [e.pageX, e.pageY],
  741. isArrow = $(e.target).data('gallery-role') === 'arrow',
  742. isClick = initPos[0] === pos[0] && initPos[1] === pos[1],
  743. isImg = $(e.target).parent().data('active');
  744. if (isArrow || isImg && !isClick) {
  745. hideMagnifier();
  746. }
  747. }
  748. if (config.magnifierOpts.enabled) {
  749. $(element).on('pointerdown mousedown MSPointerDown', function (e) {
  750. var pos = [e.pageX, e.pageY];
  751. $(element).on('mousemove pointermove MSPointerMove', function (ev) {
  752. navigator.msPointerEnabled ? hideMagnifier() : behaveOnDrag(ev, pos);
  753. });
  754. $(document).on('mouseup pointerup MSPointerUp', function () {
  755. $(element).off('mousemove pointermove MSPointerMove');
  756. });
  757. });
  758. }
  759. $.extend(config.magnifierOpts, {
  760. zoomable: false,
  761. thumb: '.fotorama__img',
  762. largeWrapper: '[data-gallery-role="magnifier"]',
  763. height: config.magnifierOpts.height || function () {
  764. return $('[data-active="true"]').height();
  765. },
  766. width: config.magnifierOpts.width || function () {
  767. var productMedia = $(gallerySelector).parent().parent();
  768. return productMedia.parent().width() - productMedia.width() - 20;
  769. },
  770. left: config.magnifierOpts.left || function () {
  771. return $(gallerySelector).offset().left + $(gallerySelector).width() + 20;
  772. },
  773. top: config.magnifierOpts.top || function () {
  774. return $(gallerySelector).offset().top;
  775. }
  776. });
  777. $(element).on('fotorama:load fotorama:showend fotorama:fullscreenexit fotorama:ready', function (e, fotorama) {
  778. var $activeStageFrame = $(gallerySelector).data('fotorama').activeFrame.$stageFrame;
  779. if (!$activeStageFrame.find(magnifierZoomSelector).length) {
  780. hideMagnifier();
  781. if (config.magnifierOpts) {
  782. config.magnifierOpts.large = $(gallerySelector).data('fotorama').activeFrame.img;
  783. config.magnifierOpts.full = fotorama.data[fotorama.activeIndex].original;
  784. !checkForVideo($activeStageFrame) && $($activeStageFrame).magnify(config.magnifierOpts);
  785. }
  786. }
  787. });
  788. $(element).on('gallery:loaded', function (e) {
  789. var $prevImage;
  790. $(element).find(gallerySelector)
  791. .on('fotorama:ready', function (e, fotorama) {
  792. var $zoomIn = $(zoomInButtonSelector),
  793. $zoomOut = $(zoomOutButtonSelector);
  794. if (!$zoomIn.hasClass(zoomInLoaded)) {
  795. $zoomIn.on('click touchstart', zoomIn);
  796. $zoomIn.on('mousedown', function (e) {
  797. e.stopPropagation();
  798. });
  799. $zoomIn.keyup(function (e) {
  800. if (e.keyCode === 13) {
  801. zoomIn(e);
  802. }
  803. });
  804. $(window).keyup(function (e) {
  805. if (e.keyCode === 107 || fotorama.fullscreen) {
  806. zoomIn(e);
  807. }
  808. });
  809. $zoomIn.addClass(zoomInLoaded);
  810. }
  811. if (!$zoomOut.hasClass(zoomOutLoaded)) {
  812. $zoomOut.on('click touchstart', zoomOut);
  813. $zoomOut.on('mousedown', function (e) {
  814. e.stopPropagation();
  815. });
  816. $zoomOut.keyup(function (e) {
  817. if (e.keyCode === 13) {
  818. zoomOut(e);
  819. }
  820. });
  821. $(window).keyup(function (e) {
  822. if (e.keyCode === 109 || fotorama.fullscreen) {
  823. zoomOut(e);
  824. }
  825. });
  826. $zoomOut.addClass(zoomOutLoaded);
  827. }
  828. })
  829. .on('fotorama:fullscreenenter fotorama:showend', function (e, fotorama) {
  830. hideMagnifier();
  831. if (!$(fullscreenImageSelector).is($prevImage)) {
  832. resetVars($(fullscreenImageSelector));
  833. }
  834. magnifierFullscreen(fotorama);
  835. mousewheel(e, fotorama, element);
  836. if ($prevImage) {
  837. calculateMinSize($prevImage);
  838. if (!$(fullscreenImageSelector).is($prevImage)) {
  839. resetVars($prevImage);
  840. }
  841. }
  842. toggleStandartNavigation();
  843. })
  844. .on('fotorama:load', function (e, fotorama) {
  845. if ($(gallerySelector).data('fotorama').fullScreen) {
  846. toggleZoomButtons($(fullscreenImageSelector), isTouchEnabled,
  847. checkForVideo(fotorama.activeFrame.$stageFrame));
  848. }
  849. magnifierFullscreen(fotorama);
  850. })
  851. .on('fotorama:show', function (e, fotorama) {
  852. $prevImage = _.clone($(fullscreenImageSelector));
  853. hideMagnifier();
  854. })
  855. .on('fotorama:fullscreenexit', function (e, fotorama) {
  856. resetVars($(fullscreenImageSelector));
  857. hideMagnifier();
  858. hideZoomControls(true);
  859. });
  860. });
  861. return config;
  862. };
  863. });