vendor: 18,503 bytes, lines 1-575
1/* juxtapose - v1.2.0 - 2017-12-18 2 * Copyright (c) 2017 Alex Duner and Northwestern University Knight Lab 3 */ 4(function (document, window) { 5 6 var juxtapose = { 7 sliders: [], 8 OPTIMIZATION_ACCEPTED: 1, 9 OPTIMIZATION_WAS_CONSTRAINED: 2 10 }; 11 12 var flickr_key = "d90fc2d1f4acc584e08b8eaea5bf4d6c"; 13 var FLICKR_SIZE_PREFERENCES = ['Large', 'Medium']; 14 15 function Graphic(properties, slider) { 16 var self = this; 17 this.image = new Image(); 18 19 this.loaded = false; 20 this.image.onload = function() { 21 self.loaded = true; 22 slider._onLoaded(); 23 }; 24 25 this.image.src = properties.src; 26 this.image.alt = properties.alt || ''; 27 this.label = properties.label || false; 28 this.credit = properties.credit || false; 29 } 30 31 function FlickrGraphic(properties, slider) { 32 var self = this; 33 this.image = new Image(); 34 35 this.loaded = false; 36 this.image.onload = function() { 37 self.loaded = true; 38 slider._onLoaded(); 39 }; 40 41 this.flickrID = this.getFlickrID(properties.src); 42 this.callFlickrAPI(this.flickrID, self); 43 44 this.label = properties.label || false; 45 this.credit = properties.credit || false; 46 } 47 48 FlickrGraphic.prototype = { 49 getFlickrID: function(url) { 50 if (url.match(/flic.kr\/.+/i)) { 51 var encoded = url.split('/').slice(-1)[0]; 52 return base58Decode(encoded); 53 } 54 var idx = url.indexOf("flickr.com/photos/"); 55 var pos = idx + "flickr.com/photos/".length; 56 var photo_info = url.substr(pos); 57 if (photo_info.indexOf('/') == -1) return null; 58 if (photo_info.indexOf('/') === 0) photo_info = photo_info.substr(1); 59 id = photo_info.split("/")[1]; 60 return id; 61 }, 62 63 callFlickrAPI: function(id, self) { 64 var url = 'https://api.flickr.com/services/rest/?method=flickr.photos.getSizes' + 65 '&api_key=' + flickr_key + 66 '&photo_id=' + id + '&format=json&nojsoncallback=1'; 67 68 var request = new XMLHttpRequest(); 69 request.open('GET', url, true); 70 request.onload = function() { 71 if (request.status >= 200 && request.status < 400){ 72 data = JSON.parse(request.responseText); 73 var flickr_url = self.bestFlickrUrl(data.sizes.size); 74 self.setFlickrImage(flickr_url); 75 } else { 76 console.error("There was an error getting the picture from Flickr"); 77 } 78 }; 79 request.onerror = function() { 80 console.error("There was an error getting the picture from Flickr"); 81 }; 82 request.send(); 83 }, 84 85 setFlickrImage: function(src) { 86 this.image.src = src; 87 }, 88 89 bestFlickrUrl: function(ary) { 90 var dict = {}; 91 for (var i = 0; i < ary.length; i++) { 92 dict[ary[i].label] = ary[i].source; 93 } 94 for (var j = 0; j < FLICKR_SIZE_PREFERENCES.length; j++) { 95 if (FLICKR_SIZE_PREFERENCES[j] in dict) { 96 return dict[FLICKR_SIZE_PREFERENCES[j]]; 97 } 98 } 99 return ary[0].source; 100 } 101 }; 102 103 function getNaturalDimensions(DOMelement) { 104 if (DOMelement.naturalWidth && DOMelement.naturalHeight) { 105 return {width: DOMelement.naturalWidth, height: DOMelement.naturalHeight}; 106 } 107 // http://www.jacklmoore.com/notes/naturalwidth-and-naturalheight-in-ie/ 108 var img = new Image(); 109 img.src = DOMelement.src; 110 return {width: img.width, height: img.height}; 111 } 112 113 function getImageDimensions(img) { 114 var dimensions = { 115 width: getNaturalDimensions(img).width, 116 height: getNaturalDimensions(img).height, 117 aspect: function() { return (this.width / this.height); } 118 }; 119 return dimensions; 120 } 121 122 function addClass(element, c) { 123 if (element.classList) { 124 element.classList.add(c); 125 } else { 126 element.className += " " + c; 127 } 128 } 129 130 function removeClass(element, c) { 131 element.className = element.className.replace(/(\S+)\s*/g, function (w, match) { 132 if (match === c) { 133 return ''; 134 } 135 return w; 136 }).replace(/^\s+/, ''); 137 } 138 139 function setText(element, text) { 140 if (document.body.textContent) { 141 element.textContent = text; 142 } else { 143 element.innerText = text; 144 } 145 } 146 147 function getComputedWidthAndHeight(element) { 148 if (window.getComputedStyle) { 149 return { 150 width: parseInt(getComputedStyle(element).width, 10), 151 height: parseInt(getComputedStyle(element).height, 10) 152 }; 153 } else { 154 w = element.getBoundingClientRect().right - element.getBoundingClientRect().left; 155 h = element.getBoundingClientRect().bottom - element.getBoundingClientRect().top; 156 return { 157 width: parseInt(w, 10) || 0, 158 height: parseInt(h, 10) || 0 159 }; 160 } 161 } 162 163 function viewport() { 164 var e = window, a = 'inner'; 165 if ( !( 'innerWidth' in window ) ) { 166 a = 'client'; 167 e = document.documentElement || document.body; 168 } 169 return { width : e[ a+'Width' ] , height : e[ a+'Height' ] } 170 } 171 172 function getPageX(e) { 173 var pageX; 174 if (e.pageX) { 175 pageX = e.pageX; 176 } else if (e.touches) { 177 pageX = e.touches[0].pageX; 178 } else { 179 pageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 180 } 181 return pageX; 182 } 183 184 function getPageY(e) { 185 var pageY; 186 if (e.pageY) { 187 pageY = e.pageY; 188 } else if (e.touches) { 189 pageY = e.touches[0].pageY; 190 } else { 191 pageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 192 } 193 return pageY; 194 } 195 196 function checkFlickr(url) { 197 if (url.match(/flic.kr\/.+/i)) { 198 return true; 199 } 200 var idx = url.indexOf("flickr.com/photos/"); 201 if (idx == -1) { 202 return false; 203 } else { 204 return true; 205 } 206 } 207 208 function base58Decode(encoded) { 209 var alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ', 210 base = alphabet.length; 211 if (typeof encoded !== 'string') { 212 throw '"base58Decode" only accepts strings.'; 213 } 214 var decoded = 0; 215 while (encoded) { 216 var alphabetPosition = alphabet.indexOf(encoded[0]); 217 if (alphabetPosition < 0) { 218 throw '"base58Decode" can\'t find "' + encoded[0] + '" in the alphabet: "' + alphabet + '"'; 219 } 220 var powerOf = encoded.length - 1; 221 decoded += alphabetPosition * (Math.pow(base, powerOf)); 222 encoded = encoded.substring(1); 223 } 224 return decoded.toString(); 225 } 226 227 function getLeftPercent(slider, input) { 228 if (typeof(input) === "string" || typeof(input) === "number") { 229 leftPercent = parseInt(input, 10); 230 } else { 231 var sliderRect = slider.getBoundingClientRect(); 232 var offset = { 233 top: sliderRect.top + document.body.scrollTop + document.documentElement.scrollTop, 234 left: sliderRect.left + document.body.scrollLeft + document.documentElement.scrollLeft 235 }; 236 var width = slider.offsetWidth; 237 var pageX = getPageX(input); 238 var relativeX = pageX - offset.left; 239 leftPercent = (relativeX / width) * 100; 240 } 241 return leftPercent; 242 } 243 244 function getTopPercent(slider, input) { 245 if (typeof(input) === "string" || typeof(input) === "number") { 246 topPercent = parseInt(input, 10); 247 } else { 248 var sliderRect = slider.getBoundingClientRect(); 249 var offset = { 250 top: sliderRect.top + document.body.scrollTop + document.documentElement.scrollTop, 251 left: sliderRect.left + document.body.scrollLeft + document.documentElement.scrollLeft 252 }; 253 var width = slider.offsetHeight; 254 var pageY = getPageY(input); 255 var relativeY = pageY - offset.top; 256 topPercent = (relativeY / width) * 100; 257 } 258 return topPercent; 259 } 260 261 // values of BOOLEAN_OPTIONS are ignored. just used for 'in' test on keys 262 var BOOLEAN_OPTIONS = {'animate': true, 'showLabels': true, 'showCredits': true, 'makeResponsive': true }; 263 function interpret_boolean(x) { 264 if (typeof(x) != 'string') { 265 return Boolean(x); 266 } 267 return !(x === 'false' || x === ''); 268 } 269 270 function JXSlider(selector, images, options) { 271 272 this.selector = selector; 273 274 var i; 275 this.options = { // new options must have default values set here. 276 animate: true, 277 showLabels: true, 278 showCredits: true, 279 makeResponsive: true, 280 startingPosition: "50%", 281 mode: 'horizontal', 282 callback: null // pass a callback function if you like 283 }; 284 285 for (i in this.options) { 286 if(i in options) { 287 if (i in BOOLEAN_OPTIONS) { 288 this.options[i] = interpret_boolean(options[i]); 289 } else { 290 this.options[i] = options[i]; 291 } 292 } 293 } 294 295 if (images.length == 2) { 296 297 if(checkFlickr(images[0].src)) { 298 this.imgBefore = new FlickrGraphic(images[0], this); 299 } else { 300 this.imgBefore = new Graphic(images[0], this); 301 } 302 303 if(checkFlickr(images[1].src)) { 304 this.imgAfter = new FlickrGraphic(images[1], this); 305 } else { 306 this.imgAfter = new Graphic(images[1], this); 307 } 308 309 } else { 310 console.warn("The images parameter takes two Image objects."); 311 } 312 313 if (this.imgBefore.credit || this.imgAfter.credit) { 314 this.options.showCredits = true; 315 } else { 316 this.options.showCredits = false; 317 } 318 } 319 320 JXSlider.prototype = { 321 322 updateSlider: function(input, animate) { 323 var leftPercent, rightPercent; 324 325 if (this.options.mode === "vertical") { 326 leftPercent = getTopPercent(this.slider, input); 327 } else { 328 leftPercent = getLeftPercent(this.slider, input); 329 } 330 331 leftPercent = leftPercent.toFixed(2) + "%"; 332 leftPercentNum = parseFloat(leftPercent); 333 rightPercent = (100 - leftPercentNum) + "%"; 334 335 if (leftPercentNum > 0 && leftPercentNum < 100) { 336 removeClass(this.handle, 'transition'); 337 removeClass(this.rightImage, 'transition'); 338 removeClass(this.leftImage, 'transition'); 339 340 if (this.options.animate && animate) { 341 addClass(this.handle, 'transition'); 342 addClass(this.leftImage, 'transition'); 343 addClass(this.rightImage, 'transition'); 344 } 345 346 if (this.options.mode === "vertical") { 347 this.handle.style.top = leftPercent; 348 this.leftImage.style.height = leftPercent; 349 this.rightImage.style.height = rightPercent; 350 } else { 351 this.handle.style.left = leftPercent; 352 this.leftImage.style.width = leftPercent; 353 this.rightImage.style.width = rightPercent; 354 } 355 this.sliderPosition = leftPercent; 356 } 357 }, 358 359 getPosition: function() { 360 return this.sliderPosition; 361 }, 362 363 displayLabel: function(element, labelText) { 364 label = document.createElement("div"); 365 label.className = 'jx-label'; 366 label.setAttribute('tabindex', 0); //put the controller in the natural tab order of the document 367 368 setText(label, labelText); 369 element.appendChild(label); 370 }, 371 372 displayCredits: function() { 373 credit = document.createElement("div"); 374 credit.className = "jx-credit"; 375 376 text = "<em>Photo Credits:</em>"; 377 if (this.imgBefore.credit) { text += " <em>Before</em> " + this.imgBefore.credit; } 378 if (this.imgAfter.credit) { text += " <em>After</em> " + this.imgAfter.credit; } 379 380 credit.innerHTML = text; 381 382 this.wrapper.appendChild(credit); 383 }, 384 385 setStartingPosition: function(s) { 386 this.options.startingPosition = s; 387 }, 388 389 checkImages: function() { 390 if (getImageDimensions(this.imgBefore.image).aspect() == 391 getImageDimensions(this.imgAfter.image).aspect()) { 392 return true; 393 } else { 394 return false; 395 } 396 }, 397 398 calculateDims: function(width, height){ 399 var ratio = getImageDimensions(this.imgBefore.image).aspect(); 400 if (width) { 401 height = width / ratio; 402 } else if (height) { 403 width = height * ratio; 404 } 405 return { 406 width: width, 407 height: height, 408 ratio: ratio 409 }; 410 }, 411 412 responsivizeIframe: function(dims){ 413 //Check the slider dimensions against the iframe (window) dimensions 414 if (dims.height < window.innerHeight){ 415 //If the aspect ratio is greater than 1, imgs are landscape, so letterbox top and bottom 416 if (dims.ratio >= 1){ 417 this.wrapper.style.paddingTop = parseInt((window.innerHeight - dims.height) / 2) + "px"; 418 } 419 } else if (dims.height > window.innerHeight) { 420 /* If the image is too tall for the window, which happens at 100% width on large screens, 421 * force dimension recalculation based on height instead of width */ 422 dims = this.calculateDims(0, window.innerHeight); 423 this.wrapper.style.paddingLeft = parseInt((window.innerWidth - dims.width) / 2) + "px"; 424 } 425 if (this.options.showCredits) { 426 // accommodate the credits box within the iframe 427 dims.height -= 13; 428 } 429 return dims; 430 }, 431 432 setWrapperDimensions: function() { 433 var wrapperWidth = getComputedWidthAndHeight(this.wrapper).width; 434 var wrapperHeight = getComputedWidthAndHeight(this.wrapper).height; 435 var dims = this.calculateDims(wrapperWidth, wrapperHeight); 436 // if window is in iframe, make sure images don't overflow boundaries 437 if (window.location !== window.parent.location && !this.options.makeResponsive) { 438 dims = this.responsivizeIframe(dims); 439 } 440 441 this.wrapper.style.height = parseInt(dims.height) + "px"; 442 this.wrapper.style.width = parseInt(dims.width) + "px"; 443 }, 444 445 optimizeWrapper: function(maxWidth){ 446 var result = juxtapose.OPTIMIZATION_ACCEPTED; 447 if ((this.imgBefore.image.naturalWidth >= maxWidth) && (this.imgAfter.image.naturalWidth >= maxWidth)) { 448 this.wrapper.style.width = maxWidth + "px"; 449 result = juxtapose.OPTIMIZATION_WAS_CONSTRAINED; 450 } else if (this.imgAfter.image.naturalWidth < maxWidth) { 451 this.wrapper.style.width = this.imgAfter.image.naturalWidth + "px"; 452 } else { 453 this.wrapper.style.width = this.imgBefore.image.naturalWidth + "px"; 454 } 455 this.setWrapperDimensions(); 456 return result; 457 }, 458 459 _onLoaded: function() { 460 461 if (this.imgBefore && this.imgBefore.loaded === true && 462 this.imgAfter && this.imgAfter.loaded === true) { 463 464 this.wrapper = document.querySelector(this.selector); 465 addClass(this.wrapper, 'juxtapose'); 466 467 this.wrapper.style.width = getNaturalDimensions(this.imgBefore.image).width; 468 this.setWrapperDimensions(); 469 470 this.slider = document.createElement("div"); 471 this.slider.className = 'jx-slider'; 472 this.wrapper.appendChild(this.slider); 473 474 if (this.options.mode != "horizontal") { 475 addClass(this.slider, this.options.mode); 476 } 477 478 this.handle = document.createElement("div"); 479 this.handle.className = 'jx-handle'; 480 481 this.rightImage = document.createElement("div"); 482 this.rightImage.className = 'jx-image jx-right'; 483 this.rightImage.appendChild(this.imgAfter.image); 484 485 486 this.leftImage = document.createElement("div"); 487 this.leftImage.className = 'jx-image jx-left'; 488 this.leftImage.appendChild(this.imgBefore.image); 489 490 this.labCredit = document.createElement("a"); 491 this.labCredit.setAttribute('href', 'http://juxtapose.knightlab.com'); 492 this.labCredit.setAttribute('target', '_blank'); 493 this.labLogo = document.createElement("div"); 494 this.labLogo.className = 'knightlab-logo'; 495 this.labCredit.appendChild(this.labLogo); 496 this.projectName = document.createElement("span"); 497 this.projectName.className = 'juxtapose-name'; 498 setText(this.projectName, 'JuxtaposeJS'); 499 this.labCredit.appendChild(this.projectName); 500 501 this.slider.appendChild(this.handle); 502 this.slider.appendChild(this.leftImage); 503 this.slider.appendChild(this.rightImage); 504 //this.slider.appendChild(this.labCredit); 505 506 this.leftArrow = document.createElement("div"); 507 this.rightArrow = document.createElement("div"); 508 this.control = document.createElement("div"); 509 this.controller = document.createElement("div"); 510 511 this.leftArrow.className = 'jx-arrow jx-left'; 512 this.rightArrow.className = 'jx-arrow jx-right'; 513 this.control.className = 'jx-control'; 514 this.controller.className = 'jx-controller'; 515 516 this.controller.setAttribute('tabindex', 0); //put the controller in the natural tab order of the document 517 this.controller.setAttribute('role', 'slider'); 518 this.controller.setAttribute('aria-valuenow', 50); 519 this.controller.setAttribute('aria-valuemin', 0); 520 this.controller.setAttribute('aria-valuemax', 100); 521 522 this.handle.appendChild(this.leftArrow); 523 this.handle.appendChild(this.control); 524 this.handle.appendChild(this.rightArrow); 525 this.control.appendChild(this.controller); 526 527 this._init(); 528 } 529 }, 530 531 _init: function() { 532 533 if (this.checkImages() === false) { 534 console.warn(this, "Check that the two images have the same aspect ratio for the slider to work correctly."); 535 } 536 537 this.updateSlider(this.options.startingPosition, false); 538 539 if (this.options.showLabels === true) { 540 if (this.imgBefore.label) { this.displayLabel(this.leftImage, this.imgBefore.label); } 541 if (this.imgAfter.label) { this.displayLabel(this.rightImage, this.imgAfter.label); } 542 } 543 544 if (this.options.showCredits === true) { 545 this.displayCredits(); 546 } 547 548 var self = this; 549 window.addEventListener("resize", function() { 550 self.setWrapperDimensions(); 551 }); 552 553 554 // Set up Javascript Events 555 // On mousedown, call updateSlider then set animate to false 556 // (if animate is true, adds css transition when updating). 557 558 this.slider.addEventListener("mousedown", function(e) { 559 e = e || window.event; 560 e.preventDefault(); 561 self.updateSlider(e, true); 562 animate = true; 563 564 this.addEventListener("mousemove", function(e) { 565 e = e || window.event; 566 e.preventDefault(); 567 if (animate) { self.updateSlider(e, false); } 568 }); 569 570 this.addEventListener('mouseup', function(e) { 571 e = e || window.event; 572 e.preventDefault(); 573 e.stopPropagation(); 574 this.removeEventListener('mouseup', arguments.callee); 575 animate = false;
576 }); 577 }); 578 579 this.slider.addEventListener("touchstart", function(e) { 580 e = e || window.event; 581 e.preventDefault(); 582 e.stopPropagation(); 583 self.updateSlider(e, true); 584 585 this.addEventListener("touchmove", function(e) { 586 e = e || window.event; 587 e.preventDefault(); 588 e.stopPropagation(); 589 self.updateSlider(event, false); 590 }); 591 592 }); 593 594 /* keyboard accessibility */ 595 596 this.handle.addEventListener("keydown", function (e) { 597 e = e || window.event; 598 var key = e.which || e.keyCode; 599 var ariaValue = parseFloat(this.style.left); 600 601 //move jx-controller left 602 if (key == 37) { 603 ariaValue = ariaValue - 1; 604 var leftStart = parseFloat(this.style.left) - 1; 605 self.updateSlider(leftStart, false); 606 self.controller.setAttribute('aria-valuenow', ariaValue); 607 } 608 609 //move jx-controller right 610 if (key == 39) { 611 ariaValue = ariaValue + 1; 612 var rightStart = parseFloat(this.style.left) + 1; 613 self.updateSlider(rightStart, false); 614 self.controller.setAttribute('aria-valuenow', ariaValue); 615 } 616 }); 617 618 //toggle right-hand image visibility 619 this.leftImage.addEventListener("keydown", function (event) { 620 var key = event.which || event.keyCode; 621 if ((key == 13) || (key ==32)) { 622 self.updateSlider("90%", true); 623 self.controller.setAttribute('aria-valuenow', 90); 624 } 625 }); 626 627 //toggle left-hand image visibility 628 this.rightImage.addEventListener("keydown", function (event) { 629 var key = event.which || event.keyCode; 630 if ((key == 13) || (key ==32)) { 631 self.updateSlider("10%", true); 632 self.controller.setAttribute('aria-valuenow', 10); 633 } 634 }); 635 636 juxtapose.sliders.push(this); 637 638 if (this.options.callback && typeof(this.options.callback) == 'function') { 639 this.options.callback(this); 640 } 641 } 642 643 }; 644 645 /* 646 Given an element that is configured with the proper data elements, make a slider out of it. 647 Normally this will just be used by scanPage. 648 */ 649 juxtapose.makeSlider = function (element, idx) { 650 if (typeof idx == 'undefined') { 651 idx = juxtapose.sliders.length; // not super threadsafe... 652 } 653 654 var w = element; 655 656 var images = w.querySelectorAll('img'); 657 658 var options = {}; 659 // don't set empty string into options, that's a false false. 660 if (w.getAttribute('data-animate')) { 661 options.animate = w.getAttribute('data-animate'); 662 } 663 if (w.getAttribute('data-showlabels')) { 664 options.showLabels = w.getAttribute('data-showlabels'); 665 } 666 if (w.getAttribute('data-showcredits')) { 667 options.showCredits = w.getAttribute('data-showcredits'); 668 } 669 if (w.getAttribute('data-startingposition')) { 670 options.startingPosition = w.getAttribute('data-startingposition'); 671 } 672 if (w.getAttribute('data-mode')) { 673 options.mode = w.getAttribute('data-mode'); 674 } 675 if (w.getAttribute('data-makeresponsive')) { 676 options.mode = w.getAttribute('data-makeresponsive'); 677 } 678 679 specificClass = 'juxtapose-' + idx; 680 addClass(element, specificClass); 681 682 selector = '.' + specificClass; 683 684 if (w.innerHTML) { 685 w.innerHTML = ''; 686 } else { 687 w.innerText = ''; 688 } 689 690 slider = new juxtapose.JXSlider( 691 selector, 692 [ 693 { 694 src: images[0].src, 695 label: images[0].getAttribute('data-label'), 696 credit: images[0].getAttribute('data-credit'), 697 alt: images[0].alt 698 }, 699 { 700 src: images[1].src, 701 label: images[1].getAttribute('data-label'), 702 credit: images[1].getAttribute('data-credit'), 703 alt: images[1].alt 704 } 705 ], 706 options 707 ); 708 }; 709 710 //Enable HTML Implementation 711 juxtapose.scanPage = function() { 712 var elements = document.querySelectorAll('.juxtapose'); 713 for (var i = 0; i < elements.length; i++) { 714 juxtapose.makeSlider(elements[i], i); 715 } 716 }; 717 718 juxtapose.JXSlider = JXSlider; 719 window.juxtapose = juxtapose; 720 721 juxtapose.scanPage(); 722 723}(document, window)); 724// addEventListener polyfill / jonathantneal 725!window.addEventListener && (function (WindowPrototype, DocumentPrototype, ElementPrototype, addEventListener, removeEventListener, dispatchEvent, registry) { 726 WindowPrototype[addEventListener] = DocumentPrototype[addEventListener] = ElementPrototype[addEventListener] = function (type, listener) { 727 var target = this; 728 729 registry.unshift([target, type, listener, function (event) { 730 event.currentTarget = target; 731 event.preventDefault = function () { event.returnValue = false }; 732 event.stopPropagation = function () { event.cancelBubble = true }; 733 event.target = event.srcElement || target; 734 735 listener.call(target, event); 736 }]); 737 738 this.attachEvent("on" + type, registry[0][3]); 739 }; 740 741 WindowPrototype[removeEventListener] = DocumentPrototype[removeEventListener] = ElementPrototype[removeEventListener] = function (type, listener) { 742 for (var index = 0, register; register = registry[index]; ++index) { 743 if (register[0] == this && register[1] == type && register[2] == listener) { 744 return this.detachEvent("on" + type, registry.splice(index, 1)[0][3]); 745 } 746 } 747 }; 748 749 WindowPrototype[dispatchEvent] = DocumentPrototype[dispatchEvent] = ElementPrototype[dispatchEvent] = function (eventObject) { 750 return this.fireEvent("on" + eventObject.type, eventObject); 751 }; 752})(Window.prototype, HTMLDocument.prototype, Element.prototype, "addEventListener", "removeEventListener", "dispatchEvent", []);
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.