Skip to content

Commit

Permalink
✨ Implements variable substitution macro for html element opacity. (a…
Browse files Browse the repository at this point in the history
…mpproject#16479)

* Create variable substitution macro that returns the opacity value of an element.

* fix typo

* Add Unit Tests

* Update unit tests

* rename variable to prevent linter errors

* move opacity to a new file

* removed opacity code from src

* remove dead code

* remove dead code

* Added license to opacity

* Added license to opacity test

* Moved public function to top of file

* Change getOpacity to getMinOpacity

* Change getRootOpacity to getRootMinOpacity

* Sort imports alphabetically

* change parents to ancestors in comment
  • Loading branch information
joel-regus authored and Enriqe committed Nov 28, 2018
1 parent 9ffa7a1 commit 3455ad2
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 0 deletions.
112 changes: 112 additions & 0 deletions extensions/amp-analytics/0.1/opacity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* Copyright 2018 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 {computedStyle} from '../../../src/style';


/**
* Returns the min opacity found amongst the element and its ancestors
* @param {!Element|null} el
* @return {number} minimum opacity value
*/
export function getMinOpacity(el) {
const parentNodeTree = getElementNodeTree(el.parentElement);
parentNodeTree.push(el);
let minOpacityFound = 1;
let opacity;

for (let i = 0; i < parentNodeTree.length; i++) {
const node = parentNodeTree[i];
opacity = getElementOpacity(node);

if (opacity < minOpacityFound) { minOpacityFound = opacity; }

if (minOpacityFound === 0) { return minOpacityFound; }

}

return minOpacityFound;

}

/**
* Returns the Opacity value of the element.
* @param {!Element} el
* @return {number}
*/
function getElementOpacity(el) {
const win = window;
const fullyVisibleValue = 1;
const fullyHiddenValue = 0;

if (!el) { return fullyVisibleValue; }
const {visibility, opacity} = computedStyle(win, el);

if (visibility === 'hidden') {
return fullyHiddenValue;

}
const opacityValue = (opacity === '')
? fullyVisibleValue
: parseFloat(opacity);

if (isNaN(opacityValue)) { return fullyVisibleValue; }

return opacityValue;

}

/**
* Returns the node tree of the current element starting from
* the document root
* @param {!Element|null} el
* @return {Array} node list of the element's node tree
*/
function getElementNodeTree(el) {
const nodeList = [];
if (!el) { return nodeList; }

const CAP = 50;
const DOCUMENT_NODE_TYPE = 9;
const ELEMENT_WITH_PARENT_TYPE = 1;
let parent;
let element = el;
nodeList.push(element);

for (let i = 0; i < CAP; i++) {

parent = element.parentNode || element.parentElement;

if (parent && parent.nodeType == ELEMENT_WITH_PARENT_TYPE) {
element = parent;
nodeList.push(element);

} else if (parent && parent.nodeType == DOCUMENT_NODE_TYPE) {
parent = element.ownerDocument.defaultView.frameElement;

if (parent && parent.nodeType == ELEMENT_WITH_PARENT_TYPE) {
element = parent;
nodeList.push(element);

} else { break; }

} else { break; }

}

return nodeList;

}
81 changes: 81 additions & 0 deletions extensions/amp-analytics/0.1/test/test-opacity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Copyright 2018 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 {getMinOpacity} from '../opacity';

describes.realWin('getMinOpacity', {amp: true}, env => {
let win;
let ampElement;
let doc;
let parent;
let style;

beforeEach(() => {
win = env.win;
doc = win.document;
style = doc.createElement('style');
style.setAttribute('amp-custom','');
style.innerHTML = `
#img {
opacity: 0.5;
}
#parent {
height: 100px;
width: 100px;
}`;
doc.head.appendChild(style);

ampElement = doc.createElement('amp-img');
ampElement.id = 'img';
ampElement.setAttribute('width', '100');
ampElement.setAttribute('height', '100');

parent = doc.createElement('div');
parent.id = 'parent';

parent.appendChild(ampElement);
doc.body.appendChild(parent);

expect(getMinOpacity(ampElement)).to.equal(0.5);
});

it('amp element opacity value change', () => {
ampElement.style.opacity = 1;
expect(getMinOpacity(ampElement)).to.equal(1);

ampElement.style.opacity = 0.5;
expect(getMinOpacity(ampElement)).to.equal(0.5);

ampElement.style.opacity = 0;
expect(getMinOpacity(ampElement)).to.equal(0);

ampElement.style.opacity = 1;
ampElement.style.visibility = 'hidden';
expect(getMinOpacity(ampElement)).to.equal(0);
});

it('amp element\'s parent opacity value lower than amp element', () => {
parent.style.opacity = 0;
expect(getMinOpacity(ampElement)).to.equal(0);

parent.style.opacity = 1;
// since ampElement is 0.5
expect(getMinOpacity(ampElement)).to.equal(0.5);

parent.style.visibility = 'hidden';
expect(getMinOpacity(ampElement)).to.equal(0);
});
});
6 changes: 6 additions & 0 deletions extensions/amp-analytics/0.1/test/test-visibility-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ describes.fakeWin('VisibilityManagerForDoc', {amp: true}, env => {

// Root model starts invisible.
expect(root.getRootVisibility()).to.equal(0);
expect(root.getRootMinOpacity()).to.equal(1);
});

it('should initialize correctly foregrounded', () => {
Expand All @@ -127,6 +128,7 @@ describes.fakeWin('VisibilityManagerForDoc', {amp: true}, env => {
// Root model starts invisible.
expect(root.parent).to.be.null;
expect(root.getRootVisibility()).to.equal(1);
expect(root.getRootMinOpacity()).to.equal(1);
});

it('should resolve root layout box', () => {
Expand Down Expand Up @@ -431,6 +433,7 @@ describes.fakeWin('VisibilityManagerForDoc', {amp: true}, env => {
expect(state.backgrounded).to.equal(0);
expect(state.backgroundedAtStart).to.equal(0);
expect(state.totalTime).to.equal(12);
expect(state.opacity).to.equal(1);

expect(state.elementX).to.equal(11);
expect(state.elementY).to.equal(21);
Expand Down Expand Up @@ -461,6 +464,7 @@ describes.fakeWin('VisibilityManagerForDoc', {amp: true}, env => {
expect(disposed).to.be.calledOnce;
expect(root.models_).to.have.length(0);

expect(state.opacity).to.equal(1);
expect(state.totalVisibleTime).to.equal(0);
expect(state.firstSeenTime).to.equal(22);
expect(state.backgrounded).to.equal(0);
Expand Down Expand Up @@ -987,6 +991,7 @@ describes.realWin('VisibilityManager integrated', {amp: true}, env => {
ampElement.id = 'abc';
ampElement.setAttribute('width', '100');
ampElement.setAttribute('height', '100');
ampElement.style.opacity = 0.5;
doc.body.appendChild(ampElement);
return new Promise(resolve => {
if (resources.getResourceForElementOptional(ampElement)) {
Expand Down Expand Up @@ -1067,6 +1072,7 @@ describes.realWin('VisibilityManager integrated', {amp: true}, env => {
loadTimeVisibility: 25,
maxVisiblePercentage: 25,
minVisiblePercentage: 25,
opacity: 0.5,
totalVisibleTime: 5,
maxContinuousVisibleTime: 5,
intersectionRatio: 0.25,
Expand Down
25 changes: 25 additions & 0 deletions extensions/amp-analytics/0.1/visibility-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import {Services} from '../../../src/services';
import {VisibilityModel} from './visibility-model';
import {dev, user} from '../../../src/log';
import {getMinOpacity} from './opacity';
import {getMode} from '../../../src/mode';
import {isArray, isFiniteNumber} from '../../../src/types';
import {layoutRectLtwh} from '../../../src/layout-rect';
Expand Down Expand Up @@ -166,6 +167,14 @@ export class VisibilityManager {
*/
isBackgroundedAtStart() {}

/**
* Returns the root's, root's parent's and root's children's
* lowest opacity value
* @return {number}
* @abstract
*/
getRootMinOpacity() {}

/**
* Returns the root's layout rect.
* @return {!../../../src/layout-rect.LayoutRectDef}}
Expand Down Expand Up @@ -338,6 +347,7 @@ export class VisibilityManager {
// Optionally, element-level state.
let layoutBox;
if (opt_element) {
state['opacity'] = getMinOpacity(opt_element);
const resource =
this.resources_.getResourceForElementOptional(opt_element);
layoutBox =
Expand All @@ -352,6 +362,7 @@ export class VisibilityManager {
});

} else {
state['opacity'] = this.getRootMinOpacity();
layoutBox = this.getRootLayoutBox();
}
model.maybeDispose();
Expand Down Expand Up @@ -498,6 +509,14 @@ export class VisibilityManagerForDoc extends VisibilityManager {
return this.backgroundedAtStart_;
}

/** @override */
getRootMinOpacity() {
const root = this.ampdoc.getRootNode();
const rootElement = dev().assertElement(
root.documentElement || root.body || root);
return getMinOpacity(rootElement);
}

/** @override */
getRootLayoutBox() {
// This code is the same for "in-a-box" and standalone doc.
Expand Down Expand Up @@ -707,6 +726,12 @@ export class VisibilityManagerForEmbed extends VisibilityManager {
return this.backgroundedAtStart_;
}

/** @override */
getRootMinOpacity() {
const rootElement = dev().assertElement(this.embed.iframe);
return getMinOpacity(rootElement);
}

/**
* Gets the layout box of the embedded document. Note that this may be
* smaller than the size allocated by the host. In that case, the document
Expand Down

0 comments on commit 3455ad2

Please sign in to comment.