1/** 2 * jQuery plugin "Lightspeed box". 3 * A responsive lightbox plugin with css transitions, touch support and wait cursor for async loads. 4 * 5 * https://github.com/AlexEmashev/lsb-lightbox 6 * Author: Alexander Emashev 7 * License: The MIT public license. 8 */ 9 jQuery(document).ready(function($){ 10 'use strict'; 11 $.fn.lightspeedBox = function(options) { 12 var defaultSettings = { 13 showImageTitle: true, 14 showImageCount: true, 15 showDownloadButton: true, 16 showPlayButton: true, 17 slideshow: false, 18 playbackTiming: 3500, 19 zIndex: 30, 20 locale: { 21 nextButton: 'Next image', 22 prevButton: 'Previous image', 23 closeButton: 'Close', 24 downloadButton: 'Download image', 25 noImageFound: 'Sorry, no image found.', 26 playButton: 'Play slideshow', 27 pauseButton: 'Stop slideshow' 28 } 29 }; 30 31 /** 32 * Settings of the plugin. 33 */ 34 var settings = $.extend(defaultSettings, options); 35 36 /** 37 * Lightbox element. 38 */ 39 var $lsb; 40 41 /** 42 * Wait cursor. 43 */ 44 var $spinner; 45 46 /** 47 * Image reference in lightbox. 48 */ 49 var $lsbImage; 50 51 /** 52 * Image title from alt tag. 53 */ 54 var $lsbTitle; 55 56 /** 57 * Image count 58 */ 59 var $lsbCount; 60 61 /** 62 * No image found message. 63 */ 64 var $noImageFound; 65 66 /** 67 * Next image button. 68 */ 69 var $next; 70 71 /** 72 * Previous image button. 73 */ 74 var $prev; 75 76 /** 77 * Close button. 78 */ 79 var $close; 80 81 /** 82 * Image download button. 83 */ 84 var $download; 85 86 /** 87 * Play slideshow button. 88 */ 89 var $play; 90 91 /** 92 * Pause button 93 */ 94 var $pause; 95 96 /** 97 * Used for transition effect between slides. 98 */ 99 var transitionTimeout = 0; 100 101 /** 102 * Flag that lsb is closed 103 */ 104 var lsbClosed = true; 105 106 /** 107 * Collection of images to show in lightbox. 108 */ 109 var imageCollection = { 110 /** 111 * Current selected image. 112 */ 113 current: null, 114 /** 115 * Image addresses collection. 116 * {href: 'www.example.com/img.jpg', alt: 'Sample image "1"'} 117 */ 118 images: [], 119 /** 120 * Fills image collection with appropriate images 121 * @param $selectedImg - current image element. 122 */ 123 getImagesInSet: function($selectedImg) { 124 var previews; 125 // Reset previous images. 126 var collectedImages = []; 127 var curImgIndex = 0; 128 129 // Check if element is in group. 130 var group = $selectedImg.attr('data-lsb-group'); 131 132 // Get all images in group 133 if (group) { 134 previews = $('.lsb-preview[data-lsb-group="' + group + '"]'); 135 var selectedImgHref = $selectedImg.attr('href'); 136 // Fill collection with found elements. 137 previews.each(function(i, element) { 138 var elementHref = element.getAttribute('href'); 139 var alt = $(element) 140 .find('img') 141 .attr('alt'); 142 var elementImageDownloadUrl = element.getAttribute( 143 'data-lsb-download-link' 144 ); 145 146 collectedImages.push({ 147 href: elementHref, 148 alt: alt, 149 downloadUrl: elementImageDownloadUrl 150 }); 151 // Calculate image of the collection that should be displayed (the one user clicked). 152 if (elementHref === selectedImgHref) { 153 curImgIndex = i; 154 } 155 }); 156 } else { 157 // If it is a single image 158 collectedImages.push({ 159 href: $selectedImg.attr('href'), 160 alt: $selectedImg.find('img').attr('alt'), 161 downloadUrl: $selectedImg.attr('data-lsb-download-link') 162 }); 163 } 164 165 this.images = collectedImages; 166 this.current = curImgIndex; 167 // Hide next and previous buttons if there is only one image. 168 if (this.images.length === 1) { 169 $prev.css('visibility', 'hidden'); 170 $next.css('visibility', 'hidden'); 171 $play.hide(); 172 } else { 173 $prev.css('visibility', 'visible'); 174 $next.css('visibility', 'visible'); 175 if (settings.showPlayButton) $play.show(); 176 } 177 }, 178 /** 179 * Returns next image reference. 180 */ 181 nextImage: function() { 182 if (this.images.length === 0) { 183 return ''; 184 } 185 186 this.current += 1; 187 if (this.current > this.images.length - 1) { 188 this.current = 0; 189 } 190 191 return this.images[this.current]; 192 }, 193 /** 194 * Returns previous image reference. 195 */ 196 previousImage: function() { 197 if (this.images.length === 0) { 198 return ''; 199 } 200 201 this.current -= 1; 202 if (this.current < 0) { 203 this.current = this.images.length - 1; 204 } 205 206 return this.images[this.current]; 207 }, 208 /** 209 * Returns true if images holds more than just one image. 210 */ 211 canSwitch: function() { 212 return this.images.length > 1; 213 } 214 }; 215
216 ////////////// Functions ///////////// 217 218 /** 219 * Initializes the lightbox. 220 */ 221 (function init() { 222 var waitingIconCircle = ''; 223 224 for (var i = 0; i < 12; i++) { 225 waitingIconCircle += '<div class="waitingicon-circle"></div>'; 226 } 227 228 $('body').append( 229 '<div class="lightspeed-box">' + 230 '<div class="lsb-content">' + 231 '<header class="lsb-header"><div class="lsb-image-count"></div><div class="lsb-image-title"></div></header>' + 232 '<div class="lsb-control-panel">' + 233 '<a class="lsb-control lsb-panel-button lsb-play" title="Slideshow">' + 234 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1408 1792">' + 235 '<path fill="#000" d="M1384 927l-1328 738q-23 13-39.5 3t-16.5-36v-1472q0-26 16.5-36t39.5 3l1328 738q23 13 23 31t-23 31z" />' + 236 '</svg>' + 237 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1408 1792">' + 238 '<path fill="#fff" d="M1384 927l-1328 738q-23 13-39.5 3t-16.5-36v-1472q0-26 16.5-36t39.5 3l1328 738q23 13 23 31t-23 31z" />' + 239 '</svg>' + 240 '</a>' + 241 '<a class="lsb-control lsb-panel-button lsb-pause" title="Stop Slideshow">' + 242 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1536 1792">' + 243 '<path fill="#000" d="M1536 192v1408q0 26-19 45t-45 19h-512q-26 0-45-19t-19-45v-1408q0-26 19-45t45-19h512q26 0 45 19t19 45zM640 192v1408q0 26-19 45t-45 19h-512q-26 0-45-19t-19-45v-1408q0-26 19-45t45-19h512q26 0 45 19t19 45z" />' + 244 '</svg>' + 245 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1536 1792">' + 246 '<path fill="#fff" d="M1536 192v1408q0 26-19 45t-45 19h-512q-26 0-45-19t-19-45v-1408q0-26 19-45t45-19h512q26 0 45 19t19 45zM640 192v1408q0 26-19 45t-45 19h-512q-26 0-45-19t-19-45v-1408q0-26 19-45t45-19h512q26 0 45 19t19 45z" />' + 247 '</svg>' + 248 '</a>' + 249 '<a class="lsb-control lsb-panel-button lsb-download" download title="Download Image">' + 250 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1664 1792">' + 251 '<path fill="#000" d="M1280 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zM1536 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zM1664 1120v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zM1339 551q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z" />' + 252 '</svg>' + 253 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1664 1792">' + 254 '<path fill="#fff" d="M1280 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zM1536 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zM1664 1120v320q0 40-28 68t-68 28h-1472q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zM1339 551q17 41-14 70l-448 448q-18 19-45 19t-45-19l-448-448q-31-29-14-70 17-39 59-39h256v-448q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z" />' + 255 '</svg>' + 256 '</a>' + 257 '</div>' + 258 '<div class="lsb-image-container">' + 259 '<div class="lsb-no-image-found"><div class="no-found-msg">Sorry, no image found.</div></div>' + 260 '<img class="lsb-image lsb-noimage">' + 261 '</div>' + 262 '<div class="waitingicon">' + 263 waitingIconCircle + 264 '</div>' + 265 '<div class="lsb-control lsb-close">' + 266 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1408 1792">' + 267 '<path fill="#000" d="M1298 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z" />' + 268 '</svg>' + 269 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewB
269ox="0 0 1408 1792">' + 270 '<path fill="#fff" d="M1298 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z" />' + 271 '</svg>' + 272 '</div>' + 273 '<div class="lsb-control lsb-prev" title="Previous Image">' + 274 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 640 1792">' + 275 '<path fill="#000" d="M627 544q0 13-10 23l-393 393 393 393q10 10 10 23t-10 23l-50 50q-10 10-23 10t-23-10l-466-466q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l50 50q10 10 10 23z" />' + 276 '</svg>' + 277 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 640 1792">' + 278 '<path fill="#fff" d="M627 544q0 13-10 23l-393 393 393 393q10 10 10 23t-10 23l-50 50q-10 10-23 10t-23-10l-466-466q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l50 50q10 10 10 23z" />' + 279 '</svg>' + 280 '</div>' + 281 '<div class="lsb-control lsb-next" title="Next Image">' + 282 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 640 1792">' + 283 '<path fill="#000" d="M595 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z" />' + 284 '</svg>' + 285 '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 640 1792">' + 286 '<path fill="#fff" d="M595 960q0 13-10 23l-466 466q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l393-393-393-393q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l466 466q10 10 10 23z" />' + 287 '</svg>' + 288 '</div>' + 289 '</div>' + 290 '</div>' 291 ); 292 293 // Lightbox element. 294 $lsb = $('.lightspeed-box'); 295 $lsb.css('z-index', settings.zIndex); 296 $spinner = $('.waitingicon'); 297 $lsbImage = $lsb.find('.lsb-image'); 298 $lsbTitle = $lsb.find('.lsb-image-title'); 299 $lsbCount = $lsb.find('.lsb-image-count'); 300 $noImageFound = $lsb.find('.lsb-no-image-found'); // No image found message. 301 // Next image button. 302 $next = $lsb.find('.lsb-next'); 303 // Previous image button. 304 $prev = $lsb.find('.lsb-prev'); 305 $close = $lsb.find('.lsb-close'); 306 $download = $lsb.find('.lsb-download'); 307 $play = $lsb.find('.lsb-play'); 308 $pause = $lsb.find('.lsb-pause'); 309 310 if (!settings.showDownloadButton) { 311 $download.hide(); 312 } 313 314 if (!settings.showPlayButton) { 315 $play.hide(); 316 } 317 318 $pause.hide(); 319 320 // Set l10n. 321 $next.attr('title', settings.locale.nextButton); 322 $prev.attr('title', settings.locale.prevButton); 323 $close.attr('title', settings.locale.closeButton); 324 $download.attr('title', settings.locale.downloadButton); 325 $play.attr('title', settings.locale.playButton); 326 $pause.attr('title', settings.locale.pauseButton); 327 $noImageFound.find('.no-found-msg').text(settings.locale.noImageFound); 328 329 ///// Add event handlers for elements. ////// 330 331 var mouseOffset = { x: 0, y: 0 }; // Structure to support preview click. 332 var clickThreshold = 20; // Max amount in pixels to detect click. 333 /** 334 * Instead of click on image, detect mousedown and mouseup events. They work with touch either. 335 * This allows to use this lightbox in different sliders. 336 */ 337 $('.lsb-preview').mousedown(function(event) { 338 mouseOffset.x = event.clientX; 339 mouseOffset.y = event.clientY; 340 }); 341 342 $('.lsb-preview').mouseup(function(event) { 343 if ( 344 Math.abs(mouseOffset.x - event.clientX) < clickThreshold && 345 Math.abs(mouseOffset.y - event.clientY) < clickThreshold 346 ) { 347 // Get all images in set 348 imageCollection.getImagesInSet($(this)); 349 350 showLightbox(); 351 switchImage(imageCollection.images[imageCollection.current]); 352 353 if (settings.slideshow) { 354 window.setTimeout(playbackGo, settings.playbackTiming); 355 } 356 } 357 }); 358 359 $('.lsb-preview').click(function(event) { 360 event.preventDefault(); 361 }); 362 363 // Add swipe detection plugin. 364 $lsb.swipeDetector().on('swipeLeft.lsb swipeRight.lsb', function(event) { 365 if (imageCollection.images.length > 1) { 366 if (event.type === 'swipeLeft') { 367 switchImage(imageCollection.nextImage()); 368 } else if (event.type === 'swipeRight') { 369 switchImage(imageCollection.previousImage()); 370 } 371 } 372 }); 373 374 /** 375 * Keyboard support. 376 */ 377 $(document).on('keyup.lightspeed-box', function(event) { 378 var leftKeyCode = 37; 379 var rightKeyCode = 39; 380 var escKeyCode = 27; 381 382 if ($lsb.hasClass('lsb-active')) { 383 // Right button press. Image switching make sence only if there are more than one image in collection. 384 if ( 385 event.which === rightKeyCode && 386 imageCollection.images.length > 1 387 ) { 388 event.stopPropagation(); 389 switchImage(imageCollection.nextImage()); 390 } else if ( 391 event.which === leftKeyCode && 392 imageCollection.images.length > 1 393 ) { 394 // Left button press 395 event.stopPropagation(); 396 switchImage(imageCollection.previousImage()); 397 } else if (event.which === escKeyCode) { 398 // Esc button press 399 closeLightbox(); 400 } 401 } 402 }); 403 404 /** 405 * Next image button click. 406 */ 407 $next.click(function(event) { 408 event.stopPropagation(); 409 settings.slideshow = false;
410 switchImage(imageCollection.nextImage()); 411 }); 412 413 /** 414 * Previous image button click. 415 */ 416 $prev.click(function(event) { 417 event.stopPropagation(); 418 settings.slideshow = false; 419 switchImage(imageCollection.previousImage()); 420 }); 421 422 /** 423 * Play click. 424 */ 425 $play.click(function(event) { 426 event.stopPropagation(); 427 switchPlayback(true); 428 }); 429 430 /** 431 * Pause click. 432 */ 433 $pause.click(function(event) { 434 event.stopPropagation(); 435 switchPlayback(false); 436 }); 437 438 /** 439 * Download button click. 440 */ 441 $download.click(function(event) { 442 event.stopPropagation(); 443 }); 444 445 /** 446 * Click on element, that is displayed when no image found. 447 */ 448 $noImageFound.click(switchOrCloseImage); 449 450 /** 451 * Click on image. 452 */ 453 $lsbImage.click(switchOrCloseImage); 454 455 /** 456 * Click on empty space of lightbox. 457 */ 458 $lsb.click(function(event) { 459 switchPlayback(false); 460 closeLightbox(); 461 }); 462 463 /** 464 * Allow user to click or select image title. 465 */ 466 $lsbTitle.click(function(event) { 467 event.stopPropagation(); 468 }); 469 })(); 470 471 function switchOrCloseImage(event) { 472 if (typeof event !== 'undefined') { 473 event.stopPropagation(); 474 // This means user used any of controls, so play button should be disabled. 475 switchPlayback(false); 476 } 477 478 if (imageCollection.images.length > 1) { 479 switchImage(imageCollection.nextImage()); 480 } else { 481 closeLightbox(); 482 } 483 } 484 485 /** 486 * Switch interface between play and pause. 487 * @param play {Boolean} true to turn on slideshow. 488 */ 489 function switchPlayback(play) { 490 if (play) { 491 settings.slideshow = true; 492 $play.hide(); 493 $pause.show(); 494 window.setTimeout(playbackGo, settings.playbackTiming); 495 } else { 496 settings.slideshow = false; 497 $play.show(); 498 $pause.hide(); 499 } 500 } 501 502 /** 503 * Shows lightbox. 504 */ 505 function showLightbox() { 506 lsbClosed = false; 507 $lsbCount.text(''); 508 $lsb.addClass('lsb-active'); 509 } 510 /** 511 * Hides lightbox. 512 */ 513 function closeLightbox() { 514 $lsb.removeClass('lsb-active'); 515 $lsbImage.removeClass('lsb-image-loaded'); 516 $lsbImage.addClass('lsb-noimage'); 517 lsbClosed = true; 518 } 519 520 function playbackGo() { 521 if (settings.slideshow) switchOrCloseImage(); 522 523 // If lightbox is not closed and playback enabled, set timeout for new slide. 524 if (settings.slideshow && !lsbClosed) { 525 window.setTimeout(playbackGo, settings.playbackTiming); 526 } 527 } 528 529 /** 530 * Switches image to specific. 531 * @param imageObj image to which to switch to reference. 532 */ 533 function switchImage(imageObj) { 534 $lsbImage.addClass('lsb-noimage'); 535 $lsbImage.removeClass('lsb-image-loaded'); 536 $lsbTitle.addClass('lsb-image-notitle'); 537 $noImageFound.hide(); 538 539 // Use timeout to let the image transition effect play. 540 window.setTimeout(function() { 541 loadImage(imageObj); 542 }, transitionTimeout); 543 } 544 545 /** 546 * Loads full size image. 547 */ 548 function loadImage(imageObj) { 549 $spinner.show(); 550 // Show current image number. 551 if (settings.showImageCount && imageCollection.images.length > 1) { 552 $lsbCount.text( 553 imageCollection.current + 1 + '/' + imageCollection.images.length 554 ); 555 } else { 556 $lsbCount.text(''); 557 } 558 559 //Load image. 560 var $img = $('<img />') 561 .attr('src', imageObj.href) 562 .on('load', function() { 563 // Set image. 564 $lsbImage.attr('src', $img.attr('src')); 565 if (settings.showImageTitle) { 566 // Set image title. 567 $lsbTitle.text(imageObj.alt); 568 } 569 // Set download button reference 570 if (imageObj.downloadUrl) { 571 $download.attr('href', imageObj.downloadUrl); 572 } else { 573 $download.attr('href', imageObj.href); 574 } 575 576 displayImage(); 577 }) 578 .on('error', function(error) { 579 $spinner.hide(); 580 // Show at least a title, so user can refer it to define a problem. 581 $lsbTitle.text(imageObj.alt); 582 $noImageFound.show(); 583 console.log('[LSB Error]:', error); 584 }); 585 } 586 587 /** 588 * Shows image when animation has finished. 589 */ 590 function displayImage() { 591 $spinner.hide(); 592 $lsbImage.removeClass('lsb-noimage'); 593 $lsbImage.addClass('lsb-image-loaded'); 594 $lsbTitle.removeClass('lsb-image-notitle'); 595 } 596 }; 597 598 /** 599 * Plugin to detect swipes 600 */ 601 $.fn.swipeDetector = function(options) { 602 // States: 0 - no swipe, 1 - swipe started, 2 - swipe released 603 var swipeState = 0; 604 // Coordinates when swipe started 605 var startX = 0; 606 var startY = 0; 607 // Distance of swipe 608 var pixelOffsetX = 0; 609 var pixelOffsetY = 0; 610 // Target element which should detect swipes. 611 var swipeTarget = this; 612 var defaultSettings = { 613 // Amount of pixels, when swipe don't count. 614 swipeThreshold: 70, 615 // Flag that indicates that plugin should react only on touch events. 616 // Not on mouse events too. 617 useOnlyTouch: true 618 }; 619 620 // Initializer
621 (function init() { 622 options = $.extend(defaultSettings, options); 623 // Support touch and mouse as well. 624 swipeTarget.on('mousedown touchstart', swipeStart); 625 $('html').on('mouseup touchend', swipeEnd); 626 $('html').on('mousemove touchmove', swiping); 627 })(); 628 629 function swipeStart(event) { 630 if (options.useOnlyTouch && !event.originalEvent.touches) return; 631 632 if (event.originalEvent.touches) event = event.originalEvent.touches[0]; 633 634 if (swipeState === 0) { 635 swipeState = 1; 636 startX = event.clientX; 637 startY = event.clientY; 638 } 639 } 640 641 function swipeEnd(event) { 642 if (swipeState === 2) { 643 swipeState = 0; 644 645 if ( 646 Math.abs(pixelOffsetX) > Math.abs(pixelOffsetY) && 647 Math.abs(pixelOffsetX) > options.swipeThreshold 648 ) { 649 // Horizontal Swipe 650 if (pixelOffsetX < 0) { 651 swipeTarget.trigger($.Event('swipeLeft.lsb')); 652 } else { 653 swipeTarget.trigger($.Event('swipeRight.lsb')); 654 } 655 } else if (Math.abs(pixelOffsetY) > options.swipeThreshold) { 656 // Vertical swipe 657 if (pixelOffsetY < 0) { 658 swipeTarget.trigger($.Event('swipeUp.lsb')); 659 } else { 660 swipeTarget.trigger($.Event('swipeDown.lsb')); 661 } 662 } 663 } 664 } 665 666 function swiping(event) { 667 // If swipe don't occuring, do nothing. 668 if (swipeState !== 1) return; 669 670 if (event.originalEvent.touches) { 671 event = event.originalEvent.touches[0]; 672 } 673 674 var swipeOffsetX = event.clientX - startX; 675 var swipeOffsetY = event.clientY - startY; 676 677 if ( 678 Math.abs(swipeOffsetX) > options.swipeThreshold || 679 Math.abs(swipeOffsetY) > options.swipeThreshold 680 ) { 681 swipeState = 2; 682 pixelOffsetX = swipeOffsetX; 683 pixelOffsetY = swipeOffsetY; 684 } 685 } 686 687 return swipeTarget; // Return element available for chaining. 688 }; 689});
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.