1/*! 2 * jQuery Magnify Plugin v2.3.3 by T. H. Doan (https://thdoan.github.io/magnify/) 3 * Based on http://thecodeplayer.com/walkthrough/magnifying-glass-for-images-using-jquery-and-css3 4 * 5 * jQuery Magnify by T. H. Doan is licensed under the MIT License. 6 * Read a copy of the license in the LICENSE file or at https://choosealicense.com/licenses/mit/ 7 */ 8 9(function($) { 10 $.fn.magnify = function(oOptions) { 11 // Default options 12 oOptions = $.extend({ 13 'src': '', 14 'speed': 100, 15 'timeout': -1, 16 'touchBottomOffset': 0, 17 'finalWidth': null, 18 'finalHeight': null, 19 'magnifiedWidth': null, 20 'magnifiedHeight': null, 21 'limitBounds': false, 22 'mobileCloseEvent': 'touchstart', 23 'afterLoad': function(){} 24 }, oOptions); 25 26 var $that = this, // Preserve scope 27 $html = $('html'), 28 29 // Initiate 30 init = function(el) { 31 var $image = $(el), 32 $anchor = $image.closest('a'), 33 oDataAttr = {}; 34 35 // Get data attributes 36 for (var i in oOptions) { 37 oDataAttr[i] = $image.attr('data-magnify-' + i.toLowerCase()); 38 } 39 40 // Disable zooming if no valid large image source 41 var sZoomSrc = oDataAttr['src'] || oOptions['src'] || $anchor.attr('href') || ''; 42 if (!sZoomSrc) return; 43 44 var $container, 45 $lens, 46 nImageWidth, 47 nImageHeight, 48 nMagnifiedWidth, 49 nMagnifiedHeight, 50 nLensWidth, 51 nLensHeight, 52 nBoundX = 0, 53 nBoundY = 0, 54 nPosX, nPosY, // Absolute cursor position 55 nX, nY, // Relative cursor position 56 oContainerOffset, // Relative to document 57 oImageOffset, // Relative to container 58 // Get true offsets 59 getOffset = function() { 60 var o = $container.offset(); 61 // Store offsets from container border to image inside 62 // NOTE: .offset() does NOT take into consideration image border and padding. 63 oImageOffset = { 64 'top': ($image.offset().top-o.top) + parseInt($image.css('border-top-width')) + parseInt($image.css('padding-top')), 65 'left': ($image.offset().left-o.left) + parseInt($image.css('border-left-width')) + parseInt($image.css('padding-left')) 66 }; 67 o.top += oImageOffset['top']; 68 o.left += oImageOffset['left']; 69 return o; 70 }, 71 // Hide the lens 72 hideLens = function() { 73 if ($lens.is(':visible')) $lens.fadeOut(oOptions['speed'], function() { 74 $html.removeClass('magnifying').trigger('magnifyend'); // Reset overflow-x 75 }); 76 }, 77 moveLens = function(e) { 78 // Reinitialize if image initially hidden 79 if (!nImageHeight) { 80 refresh(); 81 return; 82 } 83 if (e) { 84 e.preventDefault(); 85 // Save last coordinates in case we need to call this function directly (required when 86 // updating magnifiedWidth/magnifiedHeight while the lens is visible). 87 nPosX = e.pageX || e.originalEvent.touches[0].pageX; 88 nPosY = e.pageY || e.originalEvent.touches[0].pageY; 89 $image.data('lastPos', { 90 'x': nPosX, 91 'y': nPosY 92 }); 93 } else { 94 nPosX = $image.data('lastPos').x; 95 nPosY = $image.data('lastPos').y; 96 } 97 // x/y coordinates of the mouse pointer or touch point. This is the position of 98 // .magnify relative to the document. 99 // 100 // We deduct the positions of .magnify from the mouse or touch positions relative to 101 // the document to get the mouse or touch positions relative to the container. 102 nX = nPosX - oContainerOffset['left'], 103 nY = (nPosY - oContainerOffset['top']) - oOptions['touchBottomOffset']; 104 // Toggle magnifying lens 105 if (!$lens.is(':animated')) { 106 if (nX>nBoundX && nX<nImageWidth-nBoundX && nY>nBoundY && nY<nImageHeight-nBoundY) { 107 if ($lens.is(':hidden')) { 108 $html.addClass('magnifying').trigger('magnifystart'); // Hide overflow-x while zooming 109 $lens.fadeIn(oOptions['speed']); 110 } 111 } else { 112 hideLens(); 113 } 114 } 115 if ($lens.is(':visible')) { 116 // Move the magnifying lens with the mouse 117 var sBgPos = ''; 118 if (nMagnifiedWidth && nMagnifiedHeight) { 119 // Change the background position of .magnify-lens according to the position of 120 // the mouse over the .magnify-image image. This allows us to get the ratio of 121 // the pixel under the mouse pointer with respect to the image and use that to 122 // position the large image inside the magnifying lens. 123 var nRatioX = -Math.round(nX/nImageWidth*nMagnifiedWidth-nLensWidth/2), 124 nRatioY = -Math.round(nY/nImageHeight*nMagnifiedHeight-nLensHeight/2); 125 if (oOptions['limitBounds']) { 126 // Enforce bounds to ensure only image is visible in lens 127 var nBoundRight = -Math.round((nImageWidth-nBoundX)/nImageWidth*nMagnifiedWidth-nLensWidth/2), 128 nBoundBottom = -Math.round((nImageHeight-nBoundY)/nImageHeight*nMagnifiedHeight-nLensHeight/2); 129 // Left and right edges 130 if (nRatioX>0) nRatioX = 0; 131 else if (nRatioX<nBoundRight) nRatioX = nBoundRight; 132 // Top and bottom edges 133 if (nRatioY>0) nRatioY = 0; 134 else if (nRatioY<nBoundBottom) nRatioY = nBoundBottom; 135 } 136 sBgPos = nRatioX + 'px ' + nRatioY + 'px'; 137 } 138 // Now the lens moves with the mouse. The logic is to deduct half of the lens's
139 // width and height from the mouse coordinates to place it with its center at the 140 // mouse coordinates. If you hover on the image now, you should see the magnifying 141 // lens in action. 142 $lens.css({ 143 'top': Math.round(nY-nLensHeight/2) + oImageOffset['top'] + 'px', 144 'left': Math.round(nX-nLensWidth/2) + oImageOffset['left'] + 'px', 145 'background-position': sBgPos 146 }); 147 } 148 }; 149 150 // Data attributes have precedence over options object 151 if (!isNaN(+oDataAttr['speed'])) oOptions['speed'] = +oDataAttr['speed']; 152 if (!isNaN(+oDataAttr['timeout'])) oOptions['timeout'] = +oDataAttr['timeout']; 153 if (!isNaN(+oDataAttr['finalWidth'])) oOptions['finalWidth'] = +oDataAttr['finalWidth']; 154 if (!isNaN(+oDataAttr['finalHeight'])) oOptions['finalHeight'] = +oDataAttr['finalHeight']; 155 if (!isNaN(+oDataAttr['magnifiedWidth'])) oOptions['magnifiedWidth'] = +oDataAttr['magnifiedWidth']; 156 if (!isNaN(+oDataAttr['magnifiedHeight'])) oOptions['magnifiedHeight'] = +oDataAttr['magnifiedHeight']; 157 if (oDataAttr['limitBounds']==='true') oOptions['limitBounds'] = true; 158 if (typeof window[oDataAttr['afterLoad']]==='function') oOptions.afterLoad = window[oDataAttr['afterLoad']]; 159 160 // Implement touch point bottom offset only on mobile devices 161 if (/\b(Android|BlackBerry|IEMobile|iPad|iPhone|Mobile|Opera Mini)\b/.test(navigator.userAgent)) { 162 if (!isNaN(+oDataAttr['touchBottomOffset'])) oOptions['touchBottomOffset'] = +oDataAttr['touchBottomOffset']; 163 } else { 164 oOptions['touchBottomOffset'] = 0; 165 } 166 167 // Save any inline styles for resetting 168 $image.data('originalStyle', $image.attr('style')); 169 170 // Activate magnification: 171 // 1. Try to get large image dimensions 172 // 2. Proceed only if able to get large image dimensions OK 173 174 // [1] Calculate the native (magnified) image dimensions. The zoomed version is only shown 175 // after the native dimensions are available. To get the actual dimensions we have to create 176 // this image object. 177 var elZoomImage = new Image(); 178 $(elZoomImage).on({ 179 'load': function() { 180 // [2] Got image dimensions OK. 181 182 // Fix overlap bug at the edges during magnification 183 $image.css('display', 'block'); 184 // Create container div if necessary 185 if (!$image.parent('.magnify').length) { 186 $image.wrap('<div class="magnify"></div>'); 187 } 188 $container = $image.parent('.magnify'); 189 // Create the magnifying lens div if necessary 190 if ($image.prev('.magnify-lens').length) { 191 $container.children('.magnify-lens').css('background-image', 'url(\'' + sZoomSrc + '\')'); 192 } else { 193 $image.before('<div class="magnify-lens loading" style="background:url(\'' + sZoomSrc + '\') 0 0 no-repeat"></div>'); 194 } 195 $lens = $container.children('.magnify-lens'); 196 // Remove the "Loading..." text 197 $lens.removeClass('loading'); 198 // Cache dimensions and offsets for improved performance 199 // NOTE: This code is inside the load() function, which is important. The width and 200 // height of the object would return 0 if accessed before the image is fully loaded. 201 nImageWidth = oOptions['finalWidth'] || $image.width(); 202 nImageHeight = oOptions['finalHeight'] || $image.height(); 203 nMagnifiedWidth = oOptions['magnifiedWidth'] || elZoomImage.width; 204 nMagnifiedHeight = oOptions['magnifiedHeight'] || elZoomImage.height; 205 nLensWidth = $lens.width(); 206 nLensHeight = $lens.height(); 207 oContainerOffset = getOffset(); // Required by refresh() 208 // Set zoom boundaries 209 if (oOptions['limitBounds']) { 210 nBoundX = (nLensWidth/2) / (nMagnifiedWidth/nImageWidth); 211 nBoundY = (nLensHeight/2) / (nMagnifiedHeight/nImageHeight); 212 } 213 // Enforce non-native large image size? 214 if (nMagnifiedWidth!==elZoomImage.width || nMagnifiedHeight!==elZoomImage.height) { 215 $lens.css('background-size', nMagnifiedWidth + 'px ' + nMagnifiedHeight + 'px'); 216 } 217 // Store zoom dimensions for mobile plugin 218 $image.data('zoomSize', { 219 'width': nMagnifiedWidth, 220 'height': nMagnifiedHeight 221 }); 222 // Store mobile close event for mobile plugin 223 $container.data('mobileCloseEvent', oDataAttr['mobileCloseEvent'] || oOptions['mobileCloseEvent']); 224 // Clean up 225 elZoomImage = null; 226 // Execute callback 227 oOptions.afterLoad(); 228 // Simulate a lens move to update positioning if magnifiedWidth/magnifiedHeight is 229 // updated while the lens is visible 230 if ($lens.is(':visible')) moveLens(); 231 // Handle mouse movements 232 $container.off().on({ 233 'mousemove touchmove': moveLens, 234 'mouseenter': function() {
235 // Need to update offsets here to support accordions 236 oContainerOffset = getOffset(); 237 }, 238 'mouseleave': hideLens 239 }); 240 241 // Prevent magnifying lens from getting "stuck" 242 if (oOptions['timeout']>=0) { 243 $container.on('touchend', function() { 244 setTimeout(hideLens, oOptions['timeout']); 245 }); 246 } 247 // Ensure lens is closed when tapping outside of it 248 $('body').not($container).on('touchstart', hideLens); 249 250 // Support image map click-throughs while zooming 251 var sUsemap = $image.attr('usemap'); 252 if (sUsemap) { 253 var $map = $('map[name=' + sUsemap.slice(1) + ']'); 254 // Image map needs to be on the same DOM level as image source 255 $image.after($map); 256 $container.click(function(e) { 257 // Trigger click on image below lens at current cursor position 258 if (e.clientX || e.clientY) { 259 $lens.hide(); 260 var elPoint = document.elementFromPoint( 261 e.clientX || e.originalEvent.touches[0].clientX, 262 e.clientY || e.originalEvent.touches[0].clientY 263 ); 264 if (elPoint.nodeName==='AREA') { 265 elPoint.click(); 266 } else { 267 // Workaround for buggy implementation of elementFromPoint() 268 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1227469 269 $('area', $map).each(function() { 270 var a = $(this).attr('coords').split(','); 271 if (nX>=a[0] && nX<=a[2] && nY>=a[1] && nY<=a[3]) { 272 this.click(); 273 return false; 274 } 275 }); 276 } 277 } 278 }); 279 } 280 281 if ($anchor.length) { 282 // Make parent anchor inline-block to have correct dimensions 283 $anchor.css('display', 'inline-block'); 284 // Disable parent anchor if it's sourcing the large image 285 if ($anchor.attr('href') && !(oDataAttr['src'] || oOptions['src'])) { 286 $anchor.click(function(e) { 287 e.preventDefault(); 288 }); 289 } 290 } 291 292 }, 293 'error': function() { 294 // Clean up 295 elZoomImage = null; 296 } 297 }); 298 299 elZoomImage.src = sZoomSrc; 300 }, // END init() 301 302 // Simple debounce 303 nTimer = 0, 304 refresh = function() { 305 clearTimeout(nTimer); 306 nTimer = setTimeout(function() { 307 $that.destroy(); 308 $that.magnify(oOptions); 309 }, 100); 310 }; 311 312 /** 313 * Public Methods 314 */ 315 316 // Turn off zoom and reset to original state 317 this.destroy = function() { 318 this.each(function() { 319 var $this = $(this), 320 $lens = $this.prev('div.magnify-lens'), 321 sStyle = $this.data('originalStyle'); 322 if ($this.parent('div.magnify').length && $lens.length) { 323 if (sStyle) $this.attr('style', sStyle); 324 else $this.removeAttr('style'); 325 $this.unwrap(); 326 $lens.remove(); 327 } 328 }); 329 // Unregister event handler 330 $(window).off('resize', refresh); 331 return $that; 332 } 333 334 // Handle window resizing 335 $(window).resize(refresh); 336 337 return this.each(function() { 338 // Initiate magnification powers 339 init(this); 340 }); 341 342 }; 343 }(jQuery));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.