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.
 
 
 
 
 
 

614 lines
20 KiB

  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. (function ($) {
  6. $.fn.magnify = function (options) {
  7. 'use strict';
  8. var magnify = new Magnify($(this), options);
  9. /* events must be tracked here */
  10. /**
  11. * Return that from _init function
  12. *
  13. */
  14. return magnify;
  15. };
  16. function Magnify(element, options) {
  17. var customUserOptions = options || {},
  18. $box = $(element),
  19. $thumb,
  20. that = this,
  21. largeWrapper = options.largeWrapper || '.magnifier-preview',
  22. $magnifierPreview = $(largeWrapper);
  23. curThumb = null,
  24. magnifierOptions = {
  25. x: 0,
  26. y: 0,
  27. w: 0,
  28. h: 0,
  29. lensW: 0,
  30. lensH: 0,
  31. lensBgX: 0,
  32. lensBgY: 0,
  33. largeW: 0,
  34. largeH: 0,
  35. largeL: 0,
  36. largeT: 0,
  37. zoom: 2,
  38. zoomMin: 1.1,
  39. zoomMax: 5,
  40. mode: 'outside',
  41. eventType: 'click',
  42. status: 0,
  43. zoomAttached: false,
  44. zoomable: customUserOptions.zoomable !== undefined ?
  45. customUserOptions.zoomable
  46. : false,
  47. onthumbenter: customUserOptions.onthumbenter !== undefined ?
  48. customUserOptions.onthumbenter
  49. : null,
  50. onthumbmove: customUserOptions.onthumbmove !== undefined ?
  51. customUserOptions.onthumbmove
  52. : null,
  53. onthumbleave: customUserOptions.onthumbleave !== undefined ?
  54. customUserOptions.onthumbleave
  55. : null,
  56. onzoom: customUserOptions.onzoom !== undefined ?
  57. customUserOptions.onzoom
  58. : null
  59. },
  60. pos = {
  61. t: 0,
  62. l: 0,
  63. x: 0,
  64. y: 0
  65. },
  66. gId = 0,
  67. status = 0,
  68. curIdx = '',
  69. curLens = null,
  70. curLarge = null,
  71. lensbg = customUserOptions.bg !== undefined ?
  72. customUserOptions.lensbg
  73. : true,
  74. gZoom = customUserOptions.zoom !== undefined ?
  75. customUserOptions.zoom
  76. : magnifierOptions.zoom,
  77. gZoomMin = customUserOptions.zoomMin !== undefined ?
  78. customUserOptions.zoomMin
  79. : magnifierOptions.zoomMin,
  80. gZoomMax = customUserOptions.zoomMax !== undefined ?
  81. customUserOptions.zoomMax
  82. : magnifierOptions.zoomMax,
  83. gMode = customUserOptions.mode || magnifierOptions.mode,
  84. gEventType = customUserOptions.eventType || magnifierOptions.eventType,
  85. data = {},
  86. inBounds = false,
  87. isOverThumb = false,
  88. rate = 1,
  89. paddingX = 0,
  90. paddingY = 0,
  91. enabled = true,
  92. showWrapper = true;
  93. var MagnifyCls = {
  94. magnifyHidden: 'magnify-hidden',
  95. magnifyOpaque: 'magnify-opaque',
  96. magnifyFull: 'magnify-fullimage'
  97. };
  98. /**
  99. * Update Lens positon on.
  100. *
  101. */
  102. that.update = function () {
  103. updateLensOnLoad();
  104. };
  105. /**
  106. * Init new Magnifier
  107. *
  108. */
  109. that.init = function () {
  110. _init($box, options);
  111. };
  112. function _toBoolean(str) {
  113. if (typeof str === 'string') {
  114. if (str === 'true') {
  115. return true;
  116. } else if (str === 'false' || '') {
  117. return false;
  118. }
  119. console.warn('Wrong type: can\'t be transformed to Boolean');
  120. } else if (typeof str === 'boolean') {
  121. return str;
  122. }
  123. }
  124. function createLens(thumb) {
  125. if ($(thumb).siblings('.magnify-lens').length) {
  126. return false;
  127. }
  128. var lens = $('<div class="magnify-lens magnify-hidden" data-gallery-role="magnifier-zoom"></div>');
  129. $(thumb).parent().append(lens);
  130. }
  131. function updateLensOnLoad(idSelectorMainImg, thumb, largeImgInMagnifyLens, largeWrapper) {
  132. var magnifyLensElement= $box.find('.magnify-lens'),
  133. textWrapper;
  134. if (data[idSelectorMainImg].status === 1) {
  135. textWrapper = $('<div class="magnifier-loader-text"></div>');
  136. magnifyLensElement.className = 'magnifier-loader magnify-hidden';
  137. textWrapper.html('Loading...');
  138. magnifyLensElement.html('').append(textWrapper);
  139. } else if (data[idSelectorMainImg].status === 2) {
  140. magnifyLensElement.addClass(MagnifyCls.magnifyHidden);
  141. magnifyLensElement.html('');
  142. largeImgInMagnifyLens.id = idSelectorMainImg + '-large';
  143. largeImgInMagnifyLens.style.width = data[idSelectorMainImg].largeImgInMagnifyLensWidth + 'px';
  144. largeImgInMagnifyLens.style.height = data[idSelectorMainImg].largeImgInMagnifyLensHeight + 'px';
  145. largeImgInMagnifyLens.className = 'magnifier-large magnify-hidden';
  146. if (data[idSelectorMainImg].mode === 'inside') {
  147. magnifyLensElement.append(largeImgInMagnifyLens);
  148. } else {
  149. largeWrapper.html('').append(largeImgInMagnifyLens);
  150. }
  151. }
  152. data[idSelectorMainImg].lensH = data[idSelectorMainImg].lensH > $thumb.height() ? $thumb.height() : data[idSelectorMainImg].lensH;
  153. if (Math.round(data[idSelectorMainImg].lensW) === 0) {
  154. magnifyLensElement.css('display', 'none');
  155. } else {
  156. magnifyLensElement.css({
  157. width: Math.round(data[idSelectorMainImg].lensW) + 'px',
  158. height: Math.round(data[idSelectorMainImg].lensH) + 'px',
  159. display: ''
  160. });
  161. }
  162. }
  163. function getMousePos() {
  164. var xPos = pos.x - magnifierOptions.x,
  165. yPos = pos.y - magnifierOptions.y,
  166. t,
  167. l;
  168. inBounds = xPos < 0 || yPos < 0 || xPos > magnifierOptions.w || yPos > magnifierOptions.h ? false : true;
  169. l = xPos - magnifierOptions.lensW / 2;
  170. t = yPos - magnifierOptions.lensH / 2;
  171. if (xPos < magnifierOptions.lensW / 2) {
  172. l = 0;
  173. }
  174. if (yPos < magnifierOptions.lensH / 2) {
  175. t = 0;
  176. }
  177. if (xPos - magnifierOptions.w + Math.ceil(magnifierOptions.lensW / 2) > 0) {
  178. l = magnifierOptions.w - Math.ceil(magnifierOptions.lensW + 2);
  179. }
  180. if (yPos - magnifierOptions.h + Math.ceil(magnifierOptions.lensH / 2) > 0) {
  181. t = magnifierOptions.h - Math.ceil(magnifierOptions.lensH);
  182. }
  183. pos.l = l;
  184. pos.t = t;
  185. magnifierOptions.lensBgX = pos.l;
  186. magnifierOptions.lensBgY = pos.t;
  187. if (magnifierOptions.mode === 'inside') {
  188. magnifierOptions.largeL = Math.round(xPos * (magnifierOptions.zoom - magnifierOptions.lensW / magnifierOptions.w));
  189. magnifierOptions.largeT = Math.round(yPos * (magnifierOptions.zoom - magnifierOptions.lensH / magnifierOptions.h));
  190. } else {
  191. magnifierOptions.largeL = Math.round(magnifierOptions.lensBgX * magnifierOptions.zoom * (magnifierOptions.largeWrapperW / magnifierOptions.w));
  192. magnifierOptions.largeT = Math.round(magnifierOptions.lensBgY * magnifierOptions.zoom * (magnifierOptions.largeWrapperH / magnifierOptions.h));
  193. }
  194. }
  195. function onThumbEnter() {
  196. if (_toBoolean(enabled)) {
  197. magnifierOptions = data[curIdx];
  198. curLens = $box.find('.magnify-lens');
  199. if (magnifierOptions.status === 2) {
  200. curLens.removeClass(MagnifyCls.magnifyOpaque);
  201. curLarge = $('#' + curIdx + '-large');
  202. curLarge.removeClass(MagnifyCls.magnifyHidden);
  203. } else if (magnifierOptions.status === 1) {
  204. curLens.className = 'magnifier-loader';
  205. }
  206. }
  207. }
  208. function onThumbLeave() {
  209. if (magnifierOptions.status > 0) {
  210. var handler = magnifierOptions.onthumbleave;
  211. if (handler !== null) {
  212. handler({
  213. thumb: curThumb,
  214. lens: curLens,
  215. large: curLarge,
  216. x: pos.x,
  217. y: pos.y
  218. });
  219. }
  220. if (!curLens.hasClass(MagnifyCls.magnifyHidden)) {
  221. curLens.addClass(MagnifyCls.magnifyHidden);
  222. //$curThumb.removeClass(MagnifyCls.magnifyOpaque);
  223. if (curLarge !== null) {
  224. curLarge.addClass(MagnifyCls.magnifyHidden);
  225. }
  226. }
  227. }
  228. }
  229. function move() {
  230. if (_toBoolean(enabled)) {
  231. if (status !== magnifierOptions.status) {
  232. onThumbEnter();
  233. }
  234. if (magnifierOptions.status > 0) {
  235. curThumb.className = magnifierOptions.thumbCssClass + ' magnify-opaque';
  236. if (magnifierOptions.status === 1) {
  237. curLens.className = 'magnifier-loader';
  238. } else if (magnifierOptions.status === 2) {
  239. curLens.removeClass(MagnifyCls.magnifyHidden);
  240. curLarge.removeClass(MagnifyCls.magnifyHidden);
  241. curLarge.css({
  242. left: '-' + magnifierOptions.largeL + 'px',
  243. top: '-' + magnifierOptions.largeT + 'px'
  244. });
  245. }
  246. var borderOffset = 2; // Offset for magnify-lens border
  247. pos.t = pos.t <= 0 ? 0 : pos.t - borderOffset;
  248. curLens.css({
  249. left: pos.l + paddingX + 'px',
  250. top: pos.t + paddingY + 'px'
  251. });
  252. if (lensbg) {
  253. curLens.css({
  254. 'background-color': 'rgba(f,f,f,.5)'
  255. });
  256. } else {
  257. curLens.get(0).style.backgroundPosition = '-' +
  258. magnifierOptions.lensBgX + 'px -' +
  259. magnifierOptions.lensBgY + 'px';
  260. }
  261. var handler = magnifierOptions.onthumbmove;
  262. if (handler !== null) {
  263. handler({
  264. thumb: curThumb,
  265. lens: curLens,
  266. large: curLarge,
  267. x: pos.x,
  268. y: pos.y
  269. });
  270. }
  271. }
  272. status = magnifierOptions.status;
  273. }
  274. }
  275. function setThumbData(mainImage, mainImageData) {
  276. var thumbBounds = mainImage.getBoundingClientRect(),
  277. w = 0,
  278. h = 0;
  279. mainImageData.x = Math.round(thumbBounds.left);
  280. mainImageData.y = Math.round(thumbBounds.top);
  281. mainImageData.w = Math.round(thumbBounds.right - mainImageData.x);
  282. mainImageData.h = Math.round(thumbBounds.bottom - mainImageData.y);
  283. if (mainImageData.mode === 'inside') {
  284. w = mainImageData.w;
  285. h = mainImageData.h;
  286. } else {
  287. w = mainImageData.largeWrapperW;
  288. h = mainImageData.largeWrapperH;
  289. }
  290. mainImageData.largeImgInMagnifyLensWidth = Math.round(mainImageData.zoom * w);
  291. mainImageData.largeImgInMagnifyLensHeight = Math.round(mainImageData.zoom * h);
  292. mainImageData.lensW = Math.round(mainImageData.w / mainImageData.zoom);
  293. mainImageData.lensH = Math.round(mainImageData.h / mainImageData.zoom);
  294. }
  295. function _init($box, options) {
  296. var opts = {};
  297. if (options.thumb === undefined) {
  298. return false;
  299. }
  300. $thumb = $box.find(options.thumb);
  301. if ($thumb.length) {
  302. for (var key in options) {
  303. opts[key] = options[key];
  304. }
  305. opts.thumb = $thumb;
  306. enabled = opts.enabled;
  307. if (_toBoolean(enabled)) {
  308. $magnifierPreview.show().css('display', '');
  309. $magnifierPreview.addClass(MagnifyCls.magnifyHidden);
  310. set(opts);
  311. } else {
  312. $magnifierPreview.empty().hide();
  313. }
  314. }
  315. return that;
  316. }
  317. function hoverEvents(thumb) {
  318. $(thumb).on('mouseover', function (e) {
  319. if (showWrapper) {
  320. if (magnifierOptions.status !== 0) {
  321. onThumbLeave();
  322. }
  323. handleEvents(e);
  324. isOverThumb = inBounds;
  325. }
  326. }).trigger('mouseover');
  327. }
  328. function clickEvents(thumb) {
  329. $(thumb).on('click', function (e) {
  330. if (showWrapper) {
  331. if (!isOverThumb) {
  332. if (magnifierOptions.status !== 0) {
  333. onThumbLeave();
  334. }
  335. handleEvents(e);
  336. isOverThumb = true;
  337. }
  338. }
  339. });
  340. }
  341. function bindEvents(eType, thumb) {
  342. var eventFlag = 'hasBoundEvent_' + eType;
  343. if (thumb[eventFlag]) {
  344. // Events are already bound, no need to bind in duplicate
  345. return;
  346. }
  347. thumb[eventFlag] = true;
  348. switch (eType) {
  349. case 'hover':
  350. hoverEvents(thumb);
  351. break;
  352. case 'click':
  353. clickEvents(thumb);
  354. break;
  355. }
  356. }
  357. function handleEvents(e) {
  358. var src = e.target;
  359. curIdx = src.id;
  360. curThumb = src;
  361. onThumbEnter(src);
  362. setThumbData(curThumb, magnifierOptions);
  363. pos.x = e.clientX;
  364. pos.y = e.clientY;
  365. getMousePos();
  366. move();
  367. var handler = magnifierOptions.onthumbenter;
  368. if (handler !== null) {
  369. handler({
  370. thumb: curThumb,
  371. lens: curLens,
  372. large: curLarge,
  373. x: pos.x,
  374. y: pos.y
  375. });
  376. }
  377. }
  378. function set(options) {
  379. if (data[options.thumb.id] !== undefined) {
  380. curThumb = options.thumb;
  381. return false;
  382. }
  383. var thumbObj = new Image(),
  384. largeObj = new Image(),
  385. $thumb = options.thumb,
  386. thumb = $thumb.get(0),
  387. idx = thumb.id,
  388. largeUrl,
  389. largeWrapper = $(options.largeWrapper),
  390. zoom = options.zoom || thumb.getAttribute('data-zoom') || gZoom,
  391. zoomMin = options.zoomMin || gZoomMin,
  392. zoomMax = options.zoomMax || gZoomMax,
  393. mode = options.mode || thumb.getAttribute('data-mode') || gMode,
  394. eventType = options.eventType || thumb.getAttribute('data-eventType') || gEventType,
  395. onthumbenter = options.onthumbenter !== undefined ?
  396. options.onthumbenter
  397. : magnifierOptions.onthumbenter,
  398. onthumbleave = options.onthumbleave !== undefined ?
  399. options.onthumbleave
  400. : magnifierOptions.onthumbleave,
  401. onthumbmove = options.onthumbmove !== undefined ?
  402. options.onthumbmove
  403. : magnifierOptions.onthumbmove;
  404. largeUrl = $thumb.data('original') || customUserOptions.full || $thumb.attr('src');
  405. if (thumb.id === '') {
  406. idx = thumb.id = 'magnifier-item-' + gId;
  407. gId += 1;
  408. }
  409. createLens(thumb, idx);
  410. if (options.width) {
  411. largeWrapper.width(options.width);
  412. }
  413. if (options.height) {
  414. largeWrapper.height(options.height);
  415. }
  416. if (options.top) {
  417. if (typeof options.top == 'function') {
  418. var top = options.top() + 'px';
  419. } else {
  420. var top = options.top + 'px';
  421. }
  422. if (largeWrapper.length) {
  423. largeWrapper[0].style.top = top.replace('%px', '%');
  424. }
  425. }
  426. if (options.left) {
  427. if (typeof options.left == 'function') {
  428. var left = options.left() + 'px';
  429. } else {
  430. var left = options.left + 'px';
  431. }
  432. if (largeWrapper.length) {
  433. largeWrapper[0].style.left = left.replace('%px', '%');
  434. }
  435. }
  436. data[idx] = {
  437. zoom: zoom,
  438. zoomMin: zoomMin,
  439. zoomMax: zoomMax,
  440. mode: mode,
  441. eventType: eventType,
  442. thumbCssClass: thumb.className,
  443. zoomAttached: false,
  444. status: 0,
  445. largeUrl: largeUrl,
  446. largeWrapperId: mode === 'outside' ? largeWrapper.attr('id') : null,
  447. largeWrapperW: mode === 'outside' ? largeWrapper.width() : null,
  448. largeWrapperH: mode === 'outside' ? largeWrapper.height() : null,
  449. onthumbenter: onthumbenter,
  450. onthumbleave: onthumbleave,
  451. onthumbmove: onthumbmove
  452. };
  453. paddingX = ($thumb.parent().width() - $thumb.width()) / 2;
  454. paddingY = ($thumb.parent().height() - $thumb.height()) / 2;
  455. showWrapper = false;
  456. $(thumbObj).on('load', function () {
  457. if (data.length > 0) {
  458. data[idx].status = 1;
  459. $(largeObj).on('load', function () {
  460. if (largeObj.width > largeWrapper.width() || largeObj.height > largeWrapper.height()) {
  461. showWrapper = true;
  462. bindEvents(eventType, thumb);
  463. data[idx].status = 2;
  464. if (largeObj.width > largeObj.height) {
  465. data[idx].zoom = largeObj.width / largeWrapper.width();
  466. } else {
  467. data[idx].zoom = largeObj.height / largeWrapper.height();
  468. }
  469. setThumbData(thumb, data[idx]);
  470. updateLensOnLoad(idx, thumb, largeObj, largeWrapper);
  471. }
  472. });
  473. largeObj.src = data[idx].largeUrl;
  474. }
  475. });
  476. thumbObj.src = thumb.src;
  477. }
  478. /**
  479. * Hide magnifier when mouse exceeds image bounds.
  480. */
  481. function onMouseLeave() {
  482. onThumbLeave();
  483. isOverThumb = false;
  484. $magnifierPreview.addClass(MagnifyCls.magnifyHidden);
  485. }
  486. function onMousemove(e) {
  487. pos.x = e.clientX;
  488. pos.y = e.clientY;
  489. getMousePos();
  490. if (gEventType === 'hover') {
  491. isOverThumb = inBounds;
  492. }
  493. if (inBounds && isOverThumb) {
  494. if (gMode === 'outside') {
  495. $magnifierPreview.removeClass(MagnifyCls.magnifyHidden);
  496. }
  497. move();
  498. }
  499. }
  500. function onScroll() {
  501. if (curThumb !== null) {
  502. setThumbData(curThumb, magnifierOptions);
  503. }
  504. }
  505. $(window).on('scroll', onScroll);
  506. $(window).on('resize', function () {
  507. _init($box, customUserOptions);
  508. });
  509. $box.on('mousemove', onMousemove);
  510. $box.on('mouseleave', onMouseLeave);
  511. _init($box, customUserOptions);
  512. }
  513. }(jQuery));