2017-07-29 05:07:42 +03:00
|
|
|
/* @flow */
|
|
|
|
'use strict';
|
|
|
|
import type ImageLoader from './ImageLoader';
|
|
|
|
import type Logger from './Logger';
|
|
|
|
import StackingContext from './StackingContext';
|
|
|
|
import NodeContainer from './NodeContainer';
|
|
|
|
import TextContainer from './TextContainer';
|
|
|
|
|
|
|
|
export const NodeParser = (
|
|
|
|
node: HTMLElement,
|
|
|
|
imageLoader: ImageLoader,
|
|
|
|
logger: Logger
|
|
|
|
): StackingContext => {
|
|
|
|
const container = new NodeContainer(node, null, imageLoader);
|
|
|
|
const stack = new StackingContext(container, null, true);
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
logger.log(`Starting node parsing`);
|
|
|
|
}
|
|
|
|
|
|
|
|
parseNodeTree(node, container, stack, imageLoader);
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
logger.log(`Finished parsing node tree`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return stack;
|
|
|
|
};
|
|
|
|
|
|
|
|
const IGNORED_NODE_NAMES = ['SCRIPT', 'HEAD', 'TITLE', 'OBJECT', 'BR', 'OPTION'];
|
|
|
|
|
|
|
|
const parseNodeTree = (
|
|
|
|
node: HTMLElement,
|
|
|
|
parent: NodeContainer,
|
|
|
|
stack: StackingContext,
|
|
|
|
imageLoader: ImageLoader
|
|
|
|
): void => {
|
2017-08-01 18:27:12 +03:00
|
|
|
for (let childNode = node.firstChild, nextNode; childNode; childNode = nextNode) {
|
|
|
|
nextNode = childNode.nextSibling;
|
2017-08-01 15:54:18 +03:00
|
|
|
if (childNode.nodeType === Node.TEXT_NODE) {
|
|
|
|
//$FlowFixMe
|
2017-07-29 05:07:42 +03:00
|
|
|
if (childNode.data.trim().length > 0) {
|
2017-08-01 15:54:18 +03:00
|
|
|
//$FlowFixMe
|
2017-07-29 05:07:42 +03:00
|
|
|
parent.textNodes.push(new TextContainer(childNode, parent));
|
|
|
|
}
|
2017-08-01 15:54:18 +03:00
|
|
|
} else if (childNode.nodeType === Node.ELEMENT_NODE) {
|
2017-07-29 05:07:42 +03:00
|
|
|
if (IGNORED_NODE_NAMES.indexOf(childNode.nodeName) === -1) {
|
2017-08-01 15:54:18 +03:00
|
|
|
const childElement = flowRefineToHTMLElement(childNode);
|
|
|
|
const container = new NodeContainer(childElement, parent, imageLoader);
|
2017-07-29 05:07:42 +03:00
|
|
|
if (container.isVisible()) {
|
|
|
|
const treatAsRealStackingContext = createsRealStackingContext(
|
|
|
|
container,
|
2017-08-01 15:54:18 +03:00
|
|
|
childElement
|
2017-07-29 05:07:42 +03:00
|
|
|
);
|
|
|
|
if (treatAsRealStackingContext || createsStackingContext(container)) {
|
|
|
|
// for treatAsRealStackingContext:false, any positioned descendants and descendants
|
|
|
|
// which actually create a new stacking context should be considered part of the parent stacking context
|
|
|
|
const parentStack =
|
|
|
|
treatAsRealStackingContext || container.isPositioned()
|
|
|
|
? stack.getRealParentStackingContext()
|
|
|
|
: stack;
|
|
|
|
const childStack = new StackingContext(
|
|
|
|
container,
|
|
|
|
parentStack,
|
|
|
|
treatAsRealStackingContext
|
|
|
|
);
|
|
|
|
parentStack.contexts.push(childStack);
|
2017-08-01 15:54:18 +03:00
|
|
|
parseNodeTree(childElement, container, childStack, imageLoader);
|
2017-07-29 05:07:42 +03:00
|
|
|
} else {
|
|
|
|
stack.children.push(container);
|
2017-08-01 15:54:18 +03:00
|
|
|
parseNodeTree(childElement, container, stack, imageLoader);
|
2017-07-29 05:07:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-01 18:27:12 +03:00
|
|
|
}
|
2017-07-29 05:07:42 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const createsRealStackingContext = (container: NodeContainer, node: HTMLElement): boolean => {
|
|
|
|
return (
|
|
|
|
container.isRootElement() ||
|
|
|
|
container.isPositionedWithZIndex() ||
|
|
|
|
container.style.opacity < 1 ||
|
|
|
|
container.isTransformed() ||
|
|
|
|
isBodyWithTransparentRoot(container, node)
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const createsStackingContext = (container: NodeContainer): boolean => {
|
|
|
|
return container.isPositioned() || container.isFloating();
|
|
|
|
};
|
|
|
|
|
|
|
|
const isBodyWithTransparentRoot = (container: NodeContainer, node: HTMLElement): boolean => {
|
|
|
|
return (
|
|
|
|
node.nodeName === 'BODY' &&
|
|
|
|
container.parent instanceof NodeContainer &&
|
|
|
|
container.parent.style.background.backgroundColor.isTransparent()
|
|
|
|
);
|
|
|
|
};
|
2017-08-01 15:54:18 +03:00
|
|
|
|
|
|
|
//$FlowFixMe
|
|
|
|
const flowRefineToHTMLElement = (node: Node): HTMLElement => node;
|