1(function () { 2/* ------------------------------------------------------------- 3/ 4/ frontend.js 5/ 6/ Core gallery functionality. 7/ Currently requires jQuery. 8/ 9/ -------------------------------------------------------------*/ 10 11// Use theme lightbox for built-in galleries 12var lightbox = true; 13 14const MULTI_FILTER_TYPE_BUTTONS = 'buttons'; 15const MULTI_FILTER_TYPE_CHECKBOXES = 'checkboxes'; 16 17/* ------------------------------------------------------------- 18/ 19/ load events 20/ 21/ -------------------------------------------------------------*/ 22 23window.addEventListener( 'load', function() { 24 // Galleries 25 galleryModalHandler(); 26 initGalleryFilter(); 27 initShowMore(); 28 initSubGallery(); 29 autoOpenGallery(); 30 initBeforeAfterSlider(); 31} ); 32 33function autoOpenGallery() { 34 jQuery( '.sub-gallery-select' ).each( function() { 35 const select = jQuery( this ); 36 if ( !select.length ) return; 37 38 const first = select.children().first(); 39 if ( select.attr( 'sub-gallery-auto-scroll' ) === 'true' ) { 40 first.addClass( 'skip-scroll' ); 41 } 42 first.click(); 43 } ); 44} 45 46/* ------------------------------------------------------------- 47/ 48/ galleries: modal box 49/ 50/ -------------------------------------------------------------*/ 51 52function galleryModalHandler() { 53 54 if ( !lightbox ) return; 55 56 var modal = jQuery( '.site-gallery-modal:not(.filter-modal)' ); 57 var modalContent = jQuery( '.site-gallery-modal:not(.filter-modal) .modal-content' ); 58 var galleryItem = jQuery( '.site-gallery.grid:not(.filter-modal) .gallery-item' ); 59 var controlNext = jQuery( '.site-gallery-modal:not(.filter-modal) .control-next' ); 60 var controlPrev = jQuery( '.site-gallery-modal:not(.filter-modal) .control-prev' ); 61 var controlMagnify = jQuery( '.site-gallery-modal:not(.filter-modal) .control-magnify' ); 62 var scrollBtn = jQuery( '.site-rtt' ); 63 var current 64 var currentGallery 65 var currentMagnify 66 var next 67 var prev 68 var body = document.querySelector( 'body' ); 69 70 if ( !modal.length ) return; 71 72 function showModal() { 73 body.classList.add( 'model-opened' ); 74 let enableMagnify = currentGallery.attr( 'data-magnify' ) === 'true' && 75 window.outerWidth > 768; 76 modal.get( 0 ).classList.toggle( 'magnify', enableMagnify ); 77 modal.fadeIn(); 78 scrollBtn.removeClass( 'visible' ); 79 jQuery( '#wpadminbar' ).hide(); 80 } 81 82 function hideModal() { 83 body.classList.remove( 'model-opened' ); 84 jQuery( '#wpadminbar' ).show(); 85 modal.fadeOut( function() { 86 clearModal(); 87 } ); 88 } 89 90 function clearModal() { 91 modalContent.empty(); 92 current = ''; 93 currentGallery = ''; 94 clearMagnify(); 95 next = ''; 96 prev = ''; 97 } 98 99 function initMagnify() { 100 if ( !modal.hasClass( 'magnify' ) ) return; 101 if ( modalContent.find( '.is-video' ).length ) { 102 controlMagnify.attr( 'disabled', 'disabled' ); 103 if ( controlMagnify.hasClass( 'active' ) ) { 104 controlMagnify.attr( 'data-was-active', '1' ); 105 } 106 controlMagnify.removeClass( 'active' ); 107 return; 108 } 109 110 controlMagnify.removeAttr( 'disabled' ); 111 112 if ( controlMagnify.attr( 'data-was-active' ) ) { 113 controlMagnify.addClass( 'active' ); 114 controlMagnify.removeAttr( 'data-was-active' ); 115 } 116 modal.toggleClass( 'magnify-active', controlMagnify.hasClass( 'active' ) ); 117 let image = modalContent.find( 'img' ).get( 0 ); 118 image.onload = () => { 119 let mag = new Magnify( modalContent.find( 'img' ).get( 0 ), 2 ); 120 mag.toggle( controlMagnify.hasClass( 'active' ) ); 121 currentMagnify = mag; 122 currentMagnify.update(); 123 }; 124 } 125 126 function clearMagnify() { 127 if ( !currentMagnify ) return; 128 currentMagnify.destroy(); 129 currentMagnify = ''; 130 modal.removeClass( 'magnify-active' ); 131 } 132 133 function populateModal( html ) { 134 if ( !html ) html = current; 135 const index = jQuery( html ).index(); 136 const el = jQuery( html ).clone().appendTo( modalContent ); 137 el.attr( 'data-index', index ); 138 const extras = el.find( 'p' ); 139 extras.hide(); 140 el.find( 'img' ).on( 'load', function() { 141 extras.fadeIn(); 142 } ); 143 el.find( 'video' ).on( 'loadeddata', function() { 144 extras.fadeIn(); 145 } ); 146 clearMagnify(); 147 initMagnify(); 148 playVideo(); 149 } 150 151 function queueNext( html ) { 152 current = html; 153 prev = jQuery( html ).prevAll( '.gallery-item' ).not( '.gallery-item-hidden' ).first(); 154 next = jQuery( html ).nextAll( '.gallery-item' ).not( '.gallery-item-hidden' ).first(); 155 156 attrConditional( controlPrev, 'disabled', 'disabled', !prev.length ); 157 attrConditional( controlNext, 'disabled', 'disabled', !next.length ); 158 159 if ( next && next.length && next.hasClass( 'show-more-hidden' ) ) { 160 currentGallery.trigger( 'show-more' ); 161 } 162 } 163 164 function updateModal( done ) { 165 modalContent.fadeOut( 'fast' ).promise().done( function() { 166 modalContent.empty(); 167 clearMagnify(); 168 populateModal(); 169 modalContent.fadeIn( 'fast' ).promise().done( function() { 170 if ( typeof done === 'function' ) done(); 171 } ); 172 }); 173 } 174 175 function modalForward() { 176 if ( !next || !next.length ) return; 177 queueNext( next ); 178 updateModal( function() { 179 jQuery( document ).trigger( 'gallery-next' ); 180 } ); 181 } 182 183 function modalBack() { 184 if ( !prev || !prev.length ) return; 185 queueNext( prev ); 186 updateModal( function() { 187 jQuery( document ).trigger( 'gallery-prev' ); 188 } ); 189 } 190 191 function modalMagnify() { 192 if ( !currentMagnify ) return; 193 194 modal.toggleClass( 'magnify-active', controlMagnify.hasClass( 'active' ) ); 195 currentMagnify.toggle( controlMagnify.hasClass( 'active' ) ); 196 } 197 198 function playVideo() { 199 let video = modalContent.find( 'video' ); 200 if ( !video.length ) return; 201 video.get( 0 ).play(); 202 } 203 204 // Gallery item click: open modal 205 galleryItem.click( function() { 206 currentGallery = jQuery( this ).closest( '.site-gallery' ); 207 queueNext( this ); 208 updateModal( function() { 209 jQuery( document ).trigger( 'gallery-opened' ); 210 } ); 211 showModal(); 212 } ) 213 214 // Escape key: close modal 215 jQuery( document ).on( 'keydown', function(e) { 216 if (e.key == 'Escape') { 217 hideModal(); 218 } 219 } ) 220 221 // Close btn click: close modal 222 jQuery( '.site-gallery-modal .control-close' ).click( function() { 223 hideModal(); 224 } ); 225 226 // Next btn click: next gallery item 227 jQuery( '.site-gallery-modal .control-next' ).click( function() { 228 modalForward(); 229 } ); 230 231 // Prev btn click: previous gallery item 232 jQuery( '.site-gallery-modal .control-prev' ).click( function() { 233 modalBack(); 234 } ); 235 236 // Magnify btn click: toggle magnify 237 jQuery( '.site-gallery-modal .control-magnify' ).click( function() { 238 if ( jQuery( this ).attr( 'disabled' ) ) return; 239 jQuery( this ).toggleClass( 'active' ); 240 modalMagnify(); 241 } ); 242 243 // Touch swipe: next/prev gallery item 244 245 let possibleSwipe = false;
246 let touchstartX = 0 247 let touchendX = 0 248 249 function checkDirection() { 250 if ( touchendX < touchstartX ) modalForward(); 251 if ( touchendX > touchstartX ) modalBack(); 252 } 253 254 document.addEventListener( 'touchstart', e => { 255 possibleSwipe = e.touches.length == 1 256 touchstartX = e.changedTouches[ 0 ].screenX 257 } ); 258 259 document.addEventListener( 'touchend', e => { 260 if ( ! possibleSwipe ) return; 261 touchendX = e.changedTouches[ 0 ].screenX 262 checkDirection() 263 } ); 264 265 // Modal click outside .modal-flex: close modal 266 jQuery( '.site-gallery-modal:not(.filter-modal)' ).click( function( event ) { 267 const self = jQuery( this ); 268 var target = event.target; 269 var controls = self.get( 0 ).querySelector('.modal-controls'); 270 var content = self.get( 0 ).querySelector('.modal-content'); 271 var form = self.get( 0 ).querySelector('.modal-form'); 272 if ( 273 controls.contains( target ) || 274 content.contains( target ) || 275 form.contains( target) 276 ) { 277 return; 278 } 279 hideModal(); 280 }); 281 282} 283 284function initGalleryFilter() { 285 const galleries = jQuery( '.site-gallery' ); 286 if ( !galleries.length ) return; 287 288 galleries.each( function() { 289 galleryFilterHandler( jQuery( this ) ); 290 } ); 291} 292 293function galleryFilterHandler( gallery ) { 294 const parent = gallery.parent(); 295 const galleryId = gallery.attr( 'id' ); 296 const isSubGallery = parent.hasClass( 'sub-gallery' ); 297 const globalTags = []; 298 let selectedTags = []; 299 const multiFilter = gallery.attr( 'data-multi-filter' ) === 'true'; 300 const filterType = gallery.attr( 'data-filter-type' ); 301 const disableFilters = gallery.attr( 'data-disable-filters' ) === 'true'; 302 if ( disableFilters ) return; 303 const tagsOuter = jQuery( '<div>' ) 304 .addClass( 'filter-tags' ) 305 .insertBefore( gallery ); 306 const noResults = jQuery( '<p>' ) 307 .addClass( 'filter-no-results' ) 308 .text( 'No results found. Please select different filters.' ) 309 .insertAfter( tagsOuter ); 310 let noResultsTID = 0; 311 if ( multiFilter ) { 312 tagsOuter.addClass( `multi-filter-${filterType}` ); 313 } 314 315 gallery.find( '.gallery-item' ).each( function () { 316 const item = jQuery( this ); 317 const tagsAttr = item.attr( 'data-tags' ); 318 const tags = tagsAttr ? tagsAttr.split( ',' ) : []; 319 const tagsElements = item.find( '.tags .tag' ); 320 321 item.data( 'data-tags', tags ); 322 tagsElements.each( function() { 323 const tagEl = jQuery( this ); 324 const html = tagEl.html(); 325 326 if ( globalTags.includes( html ) ) return true; 327 328 globalTags.push( html ); 329 } ); 330 } ); 331 332 if ( !globalTags.length ) return; 333 334 globalTags.sort(); 335 if ( filterType === MULTI_FILTER_TYPE_CHECKBOXES ) { 336 jQuery( '<a>' ) 337 .attr( 'href', '#' ) 338 .html( 'Filter' ) 339 .addClass( 'open-filters' ) 340 .prependTo( tagsOuter ); 341 } 342 else { 343 globalTags.forEach( function( tag ) { 344 jQuery( '<a>' ) 345 .attr( 'href', '#' + tag ) 346 .attr( 'data-tag', tag ) 347 .html( tag ) 348 .addClass( 'filter-tag' ) 349 .appendTo( tagsOuter ); 350 } ); 351 let label = multiFilter ? 'Clear Filters' : 'Show All'; 352 const showAll = jQuery( '<a>' ) 353 .attr( 'href', '#all' ) 354 .attr( 'data-tag', label ) 355 .html( label ) 356 .addClass( 'filter-tag filter-all' ) 357 .prependTo( tagsOuter ); 358 if ( !multiFilter ) { 359 showAll.addClass( 'selected' ); 360 } 361 } 362 363 function handlePostTagsUpdate() { 364 noResults.hide(); 365 clearTimeout( noResultsTID ); 366 noResultsTID = setTimeout( () => { 367 if ( gallery.find( '.gallery-item:not(.gallery-item-hidden)' ).length > 0 ) return; 368 noResults.fadeIn(); 369 }, 500 ); 370 371 showMoreHandler( gallery, true ); 372 }; 373 374 function update( tag, noAnimate ) { 375 376 let speed = noAnimate ? 0 : 300; 377 if ( tag === null || ( Array.isArray( tag ) && tag.length === 0 ) ) { 378 gallery.find( '.gallery-item' ).removeClass( 'gallery-item-hidden' ).fadeIn( speed ); 379 handlePostTagsUpdate(); 380 return; 381 } 382 383 if ( multiFilter ) { 384 if ( !Array.isArray( tag ) ) { 385 tag = [ tag ]; 386 } 387 388 gallery.find( '.gallery-item' ).addClass( 'gallery-item-hidden' ).fadeOut( speed, function() { 389 const item = jQuery( this ); 390 let hasTags = true; 391 for ( const t of tag ) { 392 if ( !item.data( 'data-tags' ).includesLower( t ) ) { 393 hasTags = false;
394 break; 395 } 396 } 397 398 if ( hasTags ) { 399 item.removeClass( 'gallery-item-hidden' ).fadeIn( speed ); 400 } 401 402 handlePostTagsUpdate(); 403 } ); 404 } 405 else { 406 if ( Array.isArray( tag ) ) { 407 tag = tag[ 0 ]; 408 } 409 gallery.find( '.gallery-item' ).fadeOut( speed, function() { 410 const item = jQuery( this ); 411 if ( item.data( 'data-tags' ).includesLower( tag ) ) { 412 item.removeClass( 'gallery-item-hidden' ).fadeIn( speed ); 413 } 414 else { 415 item.addClass( 'gallery-item-hidden' ).fadeOut( speed ); 416 } 417 418 handlePostTagsUpdate(); 419 } ); 420 } 421 } 422 423 function removeSelectedTag() { 424 parent.find( '.filter-tag.selected' ).removeClass( 'selected' ); 425 } 426 427 // Modal 428 var modal = jQuery( '.site-gallery-modal.filter-modal' ); 429 var body = document.querySelector( 'body' ); 430 var scrollBtn = jQuery( '.site-rtt' ); 431 432 function showModal() { 433 body.classList.add( 'model-opened' ); 434 populateModalFilterTags(); 435 modal.attr( 'current-gallery', gallery.attr( 'id' ) ); 436 modal.fadeIn(); 437 scrollBtn.removeClass( 'visible' ); 438 } 439 440 function hideModal() { 441 const tags = []; 442 jQuery( '.site-gallery-modal.filter-modal input[type="checkbox"]:checked' ).each( function() { 443 tags.push( jQuery( this ).val() ); 444 } ); 445 update( tags ); 446 selectedTags = tags; 447 body.classList.remove( 'model-opened' ); 448 modal.fadeOut(); 449 jQuery( document ).trigger( 'gallery-closed' ); 450 } 451 452 function populateModalFilterTags() { 453 jQuery( '.filter-modal .modal-content .inner' ).empty(); 454 for ( const tag of globalTags ) { 455 let checked = ''; 456 if ( selectedTags.includes( tag ) ) { 457 checked = ' checked'; 458 } 459 jQuery( ` 460 <label> 461 <input type="checkbox" value="${tag}"${checked}/> 462 <span>${tag}</span> 463 </label> 464 ` ).appendTo( jQuery( '.filter-modal .modal-content .inner' ) ); 465 } 466 } 467 468 // Escape key: close modal 469 jQuery(document).on( 'keydown', function(e) { 470 if ( e.key == 'Escape' ) { 471 hideModal(); 472 } 473 }) 474 475 // Close btn click: close modal 476 jQuery( '.site-gallery-modal.filter-modal .close' ).click( function( e ) { 477 e.preventDefault(); 478 const currentGallery = modal.attr( 'current-gallery' ); 479 if ( currentGallery !== galleryId ) return; 480 hideModal(); 481 }); 482 483 // Modal click outside .modal-flex: close modal 484 jQuery( '.site-gallery-modal.filter-modal' ).click( function(event) { 485 var target = event.target; 486 var content = document.querySelector( '.site-gallery-modal.filter-modal .modal-content' ); 487 var inner = document.querySelector( '.site-gallery-modal.filter-modal .modal-content .inner' ); 488 if ( content.contains( target ) || inner.contains( target ) ) return; 489 hideModal(); 490 }); 491 492 jQuery( '.site-gallery-modal.filter-modal' ).on( 'change', 'input[type="checkbox"]', function( e ) { 493 const currentGallery = modal.attr( 'current-gallery' ); 494 if ( currentGallery !== galleryId || multiFilter ) return; 495 const selected = jQuery( this ); 496 modal.find( 'input[type="checkbox"]' ).prop( 'checked', false ); 497 selected.prop( 'checked', true ); 498 } ); 499 500 parent.on( 'click', '.filter-tag.filter-all', function( e ) { 501 e.preventDefault(); 502 const tag = jQuery( this ); 503 removeSelectedTag(); 504 if ( !multiFilter ) { 505 tag.addClass( 'selected' ); 506 } 507 update( null ); 508 } ); 509 510 parent.on( 'click', '.filter-tag:not(.filter-all)', function( e ) { 511 e.preventDefault(); 512 const tag = jQuery( this ); 513 if ( multiFilter ) { 514 parent.find( '.filter-tag-all.selected' ).removeClass( 'selected' ); 515 tag.toggleClass( 'selected' ); 516 const selectedTags = parent.find( '.filter-tag.selected' ).map( function() { 517 return jQuery( this ).attr( 'data-tag' ); 518 } ).get(); 519 update( selectedTags ); 520 } 521 else { 522 const selected = tag.attr( 'data-tag' ); 523 removeSelectedTag(); 524 tag.addClass( 'selected' ); 525 update( selected ); 526 } 527 } ); 528 529 if ( isSubGallery ) { 530 parent.on( 'showing', function() { 531 jQuery( '.site-gallery-modal .sub-gallery-title' ).html( gallery.attr( 'data-label' ) ); 532 // move parent to top of siblings 533 const firstGallery = jQuery( '.sub-gallery' ).first(); 534 if ( parent !== firstGallery ) { 535 parent.insertBefore( firstGallery ); 536 } 537 } ); 538
539 parent.on( 'hiding', function() { 540 update( null, true ); 541 } ); 542 } 543 544 tagsOuter.find( '.open-filters' ).on( 'click', function( e ) { 545 e.preventDefault(); 546 showModal(); 547 } ); 548 549 jQuery( document ).on( 'click', '.clear-filters', function( e ) { 550 e.preventDefault(); 551 jQuery( '.site-gallery-modal.filter-modal input[type="checkbox"] ' ).prop( 'checked', false ); 552 } ); 553} 554 555function initSubGallery() { 556 jQuery( document ).on( 'click', '.sub-gallery-select > div', function( e ) { 557 e.preventDefault(); 558 const self = jQuery( this ); 559 const label = self.find( '.label .inner-label' ).text(); 560 const dir = self.attr( 'data-sub-gallery' ); 561 const hasAutoScroll = self.parent().attr( 'sub-gallery-auto-scroll' ) == 'true'; 562 jQuery( '.sub-gallery' ).trigger( 'hiding' ).hide( 0, function() { 563 jQuery( '.sub-gallery[data-sub-gallery="' + dir + '"]' ) 564 .trigger( 'showing' ) 565 .show( 0 ) 566 .find( '.filter-tag.filter-all' ).click(); 567 } ); 568 if ( hasAutoScroll ) { 569 if ( self.hasClass( 'skip-scroll' ) ) { 570 self.removeClass( 'skip-scroll' ); 571 } 572 else { 573 const header = jQuery( '.site-header-menu-row' ); 574 const offset = header.css( 'position' ) === 'sticky' ? header.outerHeight( true ) : 200; 575 const spacing = 10; 576 jQuery( 'html, body' ).animate( { 577 scrollTop: jQuery( '.selected-gallery-name' ).offset().top - offset - spacing 578 }, 100 ); 579 } 580 } 581 jQuery( '.selected-gallery-name' ).text( label ); 582 jQuery( '.gallery-select-prompt' ).remove(); 583 self.addClass( 'selected' ).siblings().removeClass( 'selected' ); 584 } ); 585} 586 587function initShowMore() { 588 const galleries = jQuery( '.site-gallery' ); 589 if ( !galleries.length ) return; 590 591 galleries.each( function() { 592 showMoreHandler( jQuery( this ) ); 593 } ); 594} 595 596function showMoreHandler( gallery, resetShowMoreItems ) { 597 const MAX_INITIAL = parseInt( l10GalleryArgs.maxInitialImages ); 598 const PAGINATION = parseInt( l10GalleryArgs.paginationCount ); 599 const QUERY_SELECTOR = '.gallery-item.show-more-hidden:not(.gallery-item-hidden)'; 600 const HIDE_CLASS = 'show-more-hidden'; 601 const container = gallery.parent(); 602 let observer; 603 let nextHidden; 604 605 if ( resetShowMoreItems ) { 606 // do this on the next tick 607 gallery.find( '.gallery-item:not(.gallery-item-hidden)' ) 608 .removeClass( HIDE_CLASS ) 609 .slice( l10GalleryArgs.maxInitialImages ) 610 .addClass( HIDE_CLASS ); 611 } 612 613 const items = gallery.find( QUERY_SELECTOR ); 614 container.find( '.show-more-btn' ).remove(); 615 if ( items.length < MAX_INITIAL ) return; 616 617 nextHidden = items.slice( MAX_INITIAL ); 618 nextHidden.addClass( HIDE_CLASS ); 619 620 const showMore = jQuery( ` 621 <div class="site-btn show-more-btn"> 622 <a href="javascript:void(0)" class="show-more">Show More</a> 623 </div> 624 ` ).appendTo( container ); 625 626 showMore.on( 'click', function( e ) { 627 e.preventDefault(); 628 const hidden = gallery.find( QUERY_SELECTOR ); 629 if ( !hidden.length ) { 630 return; 631 } 632 const next = hidden.slice( 0, PAGINATION ); 633 next.removeClass( HIDE_CLASS ); 634 if ( !gallery.find( '.show-more-hidden' ).length && !showMore.hasClass( 'end-reached' ) ) { 635 showMore.addClass( 'end-reached' ).find( 'a' ).html( 'End Reached' ); 636 observer.disconnect(); 637 } 638 } ); 639 640 gallery.off( 'show-more' ).on( 'show-more', function() { 641 showMore.click(); 642 } ); 643 644 observer = new IntersectionObserver( function( entries ) { 645 if ( !entries[ 0 ].isIntersecting ) return; 646 showMore.click(); 647 }, { threshold: 0 } ); 648 649 observer.observe( showMore[ 0 ] ); 650} 651 652function initBeforeAfterSlider() { 653 const sliders = jQuery( '.sub-gallery .before-after-slider .slider' ); 654 sliders.on( 'mouseup', function() { 655 this.blur(); 656 } ); 657 sliders.on( 'input change', function( e ) { 658 const element = jQuery( this ).parents('.before-after-slider'); 659 const pos = e.target.value; 660 element.find( '.before' ).css( 661 { clipPath: 'polygon(0 0, ' + pos + '% 0, ' + pos + '% 100%, 0% 100%)' } 662 ); 663 element.find( '.slider-button' ).css( 664 { 665 left: 'calc(' + pos + '%)', 666 } 667 ); 668 }); 669} 670 671Array.prototype.includesLower = function( value ) { 672 const lower = value.toLowerCase(); 673 return this.some( element => { 674 return element.toLowerCase() === lower; 675 } ); 676}; 677 678function attrConditional( el, attr, value, condition ) { 679 if ( condition ) { 680 return el.attr( attr, value ); 681 } 682 else { 683 return el.removeAttr( attr ); 684 } 685}; 686 687class Magnify { 688 constructor ( img, zoom ) { 689 let self = this; 690 let glass, w, h, borderWidth; 691 /* Create magnifier glass: */ 692 glass = document.createElement( 'DIV' ); 693 glass.setAttribute( 'class', 'magnifier-glass' ); 694 695 /* Insert magnifier glass: */ 696 img.parentElement.insertBefore( glass, img ); 697 698 /* Set background properties for the magnifier glass: */ 699 glass.style.backgroundImage = 'url("' + img.src + '")'; 700 glass.style.backgroundRepeat = 'no-repeat'; 701 borderWidth = parseFloat( getComputedStyle( img ).borderWidth ); 702 w = glass.offsetWidth / 2; 703 h = glass.offsetHeight / 2; 704 705 /* Execute a function when someone moves the magnifier glass over the image: */ 706 glass.addEventListener( 'mousemove', moveMagnifier ); 707 img.addEventListener( 'mousemove', moveMagnifier ); 708 709 img.parentElement.addEventListener( 'mouseenter', onEnter ); 710 img.parentElement.addEventListener( 'mouseleave', onLeave ); 711 712 /*and also for touch screens:*/ 713 glass.addEventListener( 'touchmove', moveMagnifier ); 714 img.addEventListener( 'touchmove', moveMagnifier ); 715 716 // If the size of the image changes, we need to update the background size 717 window.addEventListener( 'resize', onResize ); 718 719 function moveMagnifier ( e ) { 720 let pos, x, y; 721 /* Prevent any other actions that may occur when moving over the image */ 722 e.preventDefault(); 723 /* Get the cursor's x and y positions: */ 724 pos = getCursorPos( e ); 725 x = pos.x; 726 y = pos.y; 727 let marginLeft = parseFloat( getComputedStyle( img ).marginLeft ); 728 /* Prevent the magnifier glass from being positioned outside the image: */ 729 if ( x > img.width - ( w / zoom ) ) { x = img.width - ( w / zoom ); } 730 if ( x < w / zoom ) { x = w / zoom; } 731 if ( y >
731 img.height - ( h / zoom ) ) { y = img.height - ( h / zoom ); } 732 if ( y < h / zoom ) { y = h / zoom; } 733 /* Set the position of the magnifier glass: */ 734 glass.style.left = ( x - w + marginLeft ) + 'px'; 735 glass.style.top = ( y - h ) + 'px'; 736 /* Display what the magnifier glass "sees": */ 737 glass.style.backgroundPosition = '-' + ( ( x * zoom ) - w + borderWidth ) + 'px -' + ( ( y * zoom ) - h + borderWidth ) + 'px'; 738 } 739 740 function onEnter() { 741 img.parentElement.classList.add( 'magnify-hover' ); 742 self.update(); 743 } 744 745 function onLeave() { 746 img.parentElement.classList.remove( 'magnify-hover' ); 747 } 748 749 function getCursorPos ( e ) { 750 let a, x = 0, y = 0; 751 e = e || window.event; 752 /* Get the x and y positions of the image: */ 753 a = img.getBoundingClientRect(); 754 /* Calculate the cursor's x and y coordinates, relative to the image: */ 755 x = e.pageX - a.left; 756 y = e.pageY - a.top; 757 /* Consider any page scrolling: */ 758 x = x - window.pageXOffset; 759 y = y - window.pageYOffset; 760 return { x: x, y: y }; 761 } 762 763 function onResize() { 764 glass.style.backgroundSize = ( img.width * zoom ) + 'px ' + ( img.height * zoom ) + 'px'; 765 } 766 767 this.toggle = function ( show ) { 768 glass.style.display = show ? 'block' : 'none'; 769 770 if ( show ) { 771 this.update(); 772 } 773 }; 774 775 this.update = function () { 776 onResize(); 777 }; 778 779 this.destroy = function () { 780 glass.remove(); 781 img.removeEventListener( 'mousemove', moveMagnifier ); 782 img.removeEventListener( 'touchmove', moveMagnifier ); 783 img.parentElement.removeEventListener( 'mouseenter', onEnter ); 784 img.parentElement.removeEventListener( 'mouseleave', onLeave ); 785 window.removeEventListener( 'resize', onResize ); 786 }; 787 788 return this; 789 } 790} 791 792})();
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.