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