PageSourceSearch

https://navaderma.com/wp-content/themes/noda/assets/js/before-after.js?ver=7.1.2

js navaderma.com collected 2026-10-07 01:21:26 UTC 24,242 bytes, 752 lines download raw bytes

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.