forked from ampproject/amphtml
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove measure APIs from amp-auto-lightbox (ampproject#33663)
* Remove measure APIs from amp-auto-lightbox * fix presubmits * moved measurer to src/utils * lints
- Loading branch information
1 parent
cc62615
commit 8ce8ea7
Showing
5 changed files
with
235 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/** | ||
* Copyright 2020 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
import {Deferred} from './promise'; | ||
import {createViewportObserver} from '../viewport-observer'; | ||
import {toWin} from '../types'; | ||
|
||
/** | ||
* @fileoverview | ||
* This utility is similar to the `src/utils/intersection`, but it doesn't | ||
* require the `rootBounds` and thus can use a simpler version of the | ||
* intersection observer that's supported natively on more platforms. | ||
* | ||
* TODO(#33678): Dedupe intersection measurement utils once the native | ||
* support is better. | ||
*/ | ||
|
||
/** @type {WeakMap<!Element, Deferred>} */ | ||
let intersectionDeferreds; | ||
|
||
/** @type {WeakMap<!Window, IntersectionObserver>} */ | ||
let intersectionObservers; | ||
|
||
/** | ||
* @param {!Window} win | ||
* @return {!IntersectionObserve} | ||
*/ | ||
function getInOb(win) { | ||
if (!intersectionDeferreds) { | ||
intersectionDeferreds = new WeakMap(); | ||
intersectionObservers = new WeakMap(); | ||
} | ||
|
||
if (!intersectionObservers.has(win)) { | ||
const observer = createViewportObserver( | ||
(entries) => { | ||
const seen = new Set(); | ||
for (let i = entries.length - 1; i >= 0; i--) { | ||
const {target} = entries[i]; | ||
if (seen.has(target)) { | ||
continue; | ||
} | ||
seen.add(target); | ||
|
||
observer.unobserve(target); | ||
intersectionDeferreds.get(target).resolve(entries[i]); | ||
intersectionDeferreds.delete(target); | ||
} | ||
}, | ||
win, | ||
{needsRootBounds: false} | ||
); | ||
intersectionObservers.set(win, observer); | ||
return observer; | ||
} | ||
return intersectionObservers.get(win); | ||
} | ||
|
||
/** | ||
* Returns a promise that resolves with the intersection entry for the given element. | ||
* | ||
* If multiple measures for the same element occur very quickly, they will | ||
* dedupe to the same promise. | ||
* | ||
* @param {!Element} el | ||
* @return {!Promise<IntersectionObserverEntry>} | ||
*/ | ||
export function measureIntersectionNoRoot(el) { | ||
if (intersectionDeferreds && intersectionDeferreds.has(el)) { | ||
return intersectionDeferreds.get(el).promise; | ||
} | ||
|
||
const inOb = getInOb(toWin(el.ownerDocument.defaultView)); | ||
inOb.observe(el); | ||
|
||
const deferred = new Deferred(); | ||
intersectionDeferreds.set(el, deferred); | ||
return deferred.promise; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/** | ||
* Copyright 2020 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {measureIntersectionNoRoot} from '../../../src/utils/intersection-no-root'; | ||
|
||
describes.fakeWin('utils/intersection', {}, (env) => { | ||
function getInObConstructorStub() { | ||
const ctor = (cb) => { | ||
if (ctor.callback) { | ||
throw new Error('Only a single InOb instance allowed per Window.'); | ||
} | ||
const observedEls = new Set(); | ||
ctor.callback = (entries) => { | ||
if (entries.some((x) => !observedEls.has(x.target))) { | ||
throw new Error( | ||
'Attempted to fire intersection for unobserved element.' | ||
); | ||
} | ||
cb(entries); | ||
}; | ||
return { | ||
observe: (e) => observedEls.add(e), | ||
unobserve: (e) => observedEls.delete(e), | ||
disconnect: () => observedEls.clear(), | ||
}; | ||
}; | ||
return ctor; | ||
} | ||
|
||
function fireIntersections(entries) { | ||
if (entries.length == 0) { | ||
return; | ||
} | ||
const win = entries[0].target.ownerDocument.defaultView; | ||
win.IntersectionObserver.callback(entries); | ||
} | ||
|
||
let el; | ||
beforeEach(() => { | ||
env.win.IntersectionObserver = getInObConstructorStub(); | ||
el = env.win.document.createElement('p'); | ||
env.win.document.body.appendChild(el); | ||
}); | ||
|
||
it('should measure intersection for an element', async () => { | ||
const intersection = measureIntersectionNoRoot(el); | ||
fireIntersections([{x: 100, target: el}]); | ||
expect(await intersection).eql({x: 100, target: el}); | ||
}); | ||
|
||
it('should dedupe multiple measures', async () => { | ||
const measure1 = measureIntersectionNoRoot(el); | ||
const measure2 = measureIntersectionNoRoot(el); | ||
expect(measure1).equal(measure2); | ||
}); | ||
|
||
it('should not dedupe multiple measures with entries in between', async () => { | ||
const measure1 = measureIntersectionNoRoot(el); | ||
fireIntersections([{x: 100, target: el}]); | ||
const measure2 = measureIntersectionNoRoot(el); | ||
|
||
expect(measure1).not.equal(measure2); | ||
}); | ||
|
||
it('should only use the latest entry', async () => { | ||
const intersection = measureIntersectionNoRoot(el); | ||
const firstEntry = {x: 0, target: el}; | ||
const secondEntry = {x: 100, target: el}; | ||
|
||
fireIntersections([firstEntry, secondEntry]); | ||
expect(await intersection).equal(secondEntry); | ||
}); | ||
|
||
it('should measure multiple elements', async () => { | ||
const el2 = env.win.document.createElement('p'); | ||
env.win.document.body.appendChild(el2); | ||
|
||
const intersection1 = measureIntersectionNoRoot(el); | ||
const intersection2 = measureIntersectionNoRoot(el2); | ||
|
||
const firstEntry = {x: 0, target: el}; | ||
const secondEntry = {x: 2, target: el2}; | ||
|
||
fireIntersections([secondEntry]); | ||
fireIntersections([firstEntry]); | ||
|
||
expect(await intersection1).equal(firstEntry); | ||
expect(await intersection2).equal(secondEntry); | ||
}); | ||
|
||
it('should support measuring elements from multiple windows', async () => { | ||
const el1 = { | ||
ownerDocument: { | ||
defaultView: {IntersectionObserver: getInObConstructorStub()}, | ||
}, | ||
}; | ||
const el2 = { | ||
ownerDocument: { | ||
defaultView: {IntersectionObserver: getInObConstructorStub()}, | ||
}, | ||
}; | ||
|
||
const intersection1 = measureIntersectionNoRoot(el1); | ||
const intersection2 = measureIntersectionNoRoot(el2); | ||
const firstEntry = {target: el1}; | ||
const secondEntry = {target: el2}; | ||
fireIntersections([firstEntry]); | ||
fireIntersections([secondEntry]); | ||
|
||
expect(await intersection1).equal(firstEntry); | ||
expect(await intersection2).equal(secondEntry); | ||
}); | ||
}); |