PageSourceSearch

https://muhammad-huzaifaa.github.io/ObjectCompose/juxtapose/js/juxtapose.js

js muhammad-huzaifaa.github.io collected 2026-10-03 09:38:46 UTC 28,035 bytes, 757 lines download raw bytes

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.