FEATURE: polyfill intersection observer for IE11 / iOS Safari

FEATURE: polyfill intersection observer for IE11 / iOS Safari

This feature is used for defer loading of images and in future for post cloaking

This gives us a polyfill so we can safely use the feature in problem browsers

The polyfill supports “polling” but it does not appear we need it yet.

If we discover anything odd here, consider setting poll interval per:

var io = new IntersectionObserver(callback);
io.POLL_INTERVAL = 100; // Time in milliseconds.

Keeping the mutation observer cause we often mutate the DOM

From 0ca61242b8be4e6645d288765891d954f42e72d2 Mon Sep 17 00:00:00 2001
From: Sam <sam.saffron@gmail.com>
Date: Wed, 12 Dec 2018 15:36:08 +1100
Subject: [PATCH] FEATURE: polyfill intersection observer for IE11 / iOS Safari

This feature is used for defer loading of images and in future for post cloaking

This gives us a polyfill so we can safely use the feature in problem browsers

The polyfill supports "polling" but it does not appear we need it yet.

If we discover anything odd here, consider setting poll interval per:

https://github.com/w3c/IntersectionObserver/tree/master/polyfill

`‍``
var io = new IntersectionObserver(callback);
io.POLL_INTERVAL = 100; // Time in milliseconds.
`‍``

Keeping the mutation observer cause we often mutate the DOM

diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js
index 66e8359..bcf1f0d 100644
--- a/app/assets/javascripts/vendor.js
+++ b/app/assets/javascripts/vendor.js
@@ -32,3 +32,4 @@
 //= require virtual-dom-amd
 //= require highlight.js
 //= require htmlparser.js
+//= require intersection-observer
diff --git a/lib/tasks/javascript.rake b/lib/tasks/javascript.rake
index 9de2f4e..28108df 100644
--- a/lib/tasks/javascript.rake
+++ b/lib/tasks/javascript.rake
@@ -56,7 +56,7 @@ task 'javascript:update' do
     }, {
       source: 'jquery.cookie/jquery.cookie.js'
     }, {
-      source: 'jQuery/dist/jquery.js'
+      source: 'jquery/dist/jquery.js'
     }, {
       source: 'jquery-tags-input/src/jquery.tagsinput.js'
     }, {
@@ -65,6 +65,9 @@ task 'javascript:update' do
       source: 'mousetrap/mousetrap.js'
     }, {
       source: 'resumablejs/resumable.js'
+    }, {
+      # TODO: drop when we eventually drop IE11, this will land in iOS in version 13
+      source: 'intersection-observer/intersection-observer.js'
     }
   ]
 
diff --git a/package.json b/package.json
index c4252f0..c0b084f 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
     "favcount": "https://github.com/chrishunt/favcount",
     "handlebars": "4.0.5",
     "htmlparser": "https://github.com/tautologistics/node-htmlparser",
+    "intersection-observer": "^0.5.1",
     "jquery": "3.3.1",
     "jquery-color": "1.0.0",
     "jquery-resize": "https://github.com/cowboy/jquery-resize/",
diff --git a/vendor/assets/javascripts/intersection-observer.js b/vendor/assets/javascripts/intersection-observer.js
new file mode 100644
index 0000000..7a07965
--- /dev/null
+++ b/vendor/assets/javascripts/intersection-observer.js
@@ -0,0 +1,726 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE.
+ *
+ *  https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ */
+
+(function(window, document) {
+'use strict';
+
+
+// Exits early if all IntersectionObserver and IntersectionObserverEntry
+// features are natively supported.
+if ('IntersectionObserver' in window &&
+    'IntersectionObserverEntry' in window &&
+    'intersectionRatio' in window.IntersectionObserverEntry.prototype) {
+
+  // Minimal polyfill for Edge 15's lack of `isIntersecting`
+  // See: https://github.com/w3c/IntersectionObserver/issues/211
+  if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
+    Object.defineProperty(window.IntersectionObserverEntry.prototype,
+      'isIntersecting', {
+      get: function () {
+        return this.intersectionRatio > 0;
+      }
+    });
+  }
+  return;
+}
+
+
+/**
+ * An IntersectionObserver registry. This registry exists to hold a strong
+ * reference to IntersectionObserver instances currently observing a target
+ * element. Without this registry, instances without another reference may be
+ * garbage collected.
+ */
+var registry = [];
+
+
+/**
+ * Creates the global IntersectionObserverEntry constructor.
+ * https://w3c.github.io/IntersectionObserver/#intersection-observer-entry
+ * @param {Object} entry A dictionary of instance properties.
+ * @constructor
+ */
+function IntersectionObserverEntry(entry) {
+  this.time = entry.time;
+  this.target = entry.target;
+  this.rootBounds = entry.rootBounds;
+  this.boundingClientRect = entry.boundingClientRect;
+  this.intersectionRect = entry.intersectionRect || getEmptyRect();
+  this.isIntersecting = !!entry.intersectionRect;
+
+  // Calculates the intersection ratio.
+  var targetRect = this.boundingClientRect;
+  var targetArea = targetRect.width * targetRect.height;
+  var intersectionRect = this.intersectionRect;
+  var intersectionArea = intersectionRect.width * intersectionRect.height;
+
+  // Sets intersection ratio.
+  if (targetArea) {
+    // Round the intersection ratio to avoid floating point math issues:
+    // https://github.com/w3c/IntersectionObserver/issues/324
+    this.intersectionRatio = Number((intersectionArea / targetArea).toFixed(4));
+  } else {
+    // If area is zero and is intersecting, sets to 1, otherwise to 0
+    this.intersectionRatio = this.isIntersecting ? 1 : 0;
+  }
+}
+
+
+/**
+ * Creates the global IntersectionObserver constructor.
+ * https://w3c.github.io/IntersectionObserver/#intersection-observer-interface
+ * @param {Function} callback The function to be invoked after intersection
+ *     changes have queued. The function is not invoked if the queue has
+ *     been emptied by calling the `takeRecords` method.
+ * @param {Object=} opt_options Optional configuration options.
+ * @constructor
+ */
+function IntersectionObserver(callback, opt_options) {
+
+  var options = opt_options || {};
+
+  if (typeof callback != 'function') {
+    throw new Error('callback must be a function');
+  }
+
+  if (options.root && options.root.nodeType != 1) {
+    throw new Error('root must be an Element');
+  }
+
+  // Binds and throttles `this._checkForIntersections`.
+  this._checkForIntersections = throttle(
+      this._checkForIntersections.bind(this), this.THROTTLE_TIMEOUT);
+
+  // Private properties.
+  this._callback = callback;
+  this._observationTargets = [];
+  this._queuedEntries = [];
+  this._rootMarginValues = this._parseRootMargin(options.rootMargin);
+
+  // Public properties.
+  this.thresholds = this._initThresholds(options.threshold);
+  this.root = options.root || null;
+  this.rootMargin = this._rootMarginValues.map(function(margin) {
+    return margin.value + margin.unit;
+  }).join(' ');
+}
+
+
+/**
+ * The minimum interval within which the document will be checked for
+ * intersection changes.
+ */
+IntersectionObserver.prototype.THROTTLE_TIMEOUT = 100;
+
+
+/**
+ * The frequency in which the polyfill polls for intersection changes.
+ * this can be updated on a per instance basis and must be set prior to
+ * calling `observe` on the first target.
+ */
+IntersectionObserver.prototype.POLL_INTERVAL = null;
+
+/**
+ * Use a mutation observer on the root element
+ * to detect intersection changes.
+ */
+IntersectionObserver.prototype.USE_MUTATION_OBSERVER = true;
+
+
+/**
+ * Starts observing a target element for intersection changes based on
+ * the thresholds values.
+ * @param {Element} target The DOM element to observe.
+ */
+IntersectionObserver.prototype.observe = function(target) {
+  var isTargetAlreadyObserved = this._observationTargets.some(function(item) {
+    return item.element == target;
+  });
+
+  if (isTargetAlreadyObserved) {
+    return;
+  }
+
+  if (!(target && target.nodeType == 1)) {
+    throw new Error('target must be an Element');
+  }
+
+  this._registerInstance();
+  this._observationTargets.push({element: target, entry: null});
+  this._monitorIntersections();
+  this._checkForIntersections();
+};
+
+
+/**
+ * Stops observing a target element for intersection changes.
+ * @param {Element} target The DOM element to observe.
+ */
+IntersectionObserver.prototype.unobserve = function(target) {
+  this._observationTargets =
+      this._observationTargets.filter(function(item) {
+
+    return item.element != target;
+  });
+  if (!this._observationTargets.length) {
+ 

GitHub

1 Like