UNPKG

next-csrf

Version:

CSRF mitigation library for Next.js

154 lines (132 loc) 5.36 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = function (element) { // dimensions of the element itself var _element = element.getBoundingClientRect(); // dimensions of the viewport var _viewport = getViewportRect(); // we need the area to know how much of the element can be displayed at the most _viewport.area = _viewport.width * _viewport.height; var _area = _viewport; // dimensions of the intersection of all scrollable parents var _container = getScrollableParentRect(element); if (_container) { if (!_container.width || !_container.height) { // scrollable containers without dimensions are invisible, // meaning that the element is not visible at all return 0; } // dimension the element can currently be rendered in _area = getIntersectingRect(_container, _viewport); _area.area = _container.area; } // dimension of the element currently rendered in identified space var _visible = getIntersectingRect(_element, _area); if (!_visible.width || !_visible.height) { // element is not shown within the identified area return 0; } // compare the element's currently visible size to the size it // could take up at the most, being either the element's actual // size, or the space theroetically made available if all // scrollable parents are aligned properly var area = _element.width * _element.height; var maxArea = Math.min(area, _area.area); // Firefox may return sub-pixel bounding client rect var visibleArea = Math.round(_visible.width) * Math.round(_visible.height) / maxArea; // Edge might not reach 0.5 exactly var factor = 10000; var roundedVisibleArea = Math.round(visibleArea * factor) / factor; // clamp the value at 1 return Math.min(roundedVisibleArea, 1); }; var _parents = require('../get/parents'); var _parents2 = _interopRequireDefault(_parents); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function getIntersectingRect(one, two) { // identify the rectangle that _element and _container overlap in var top = Math.max(one.top, two.top); var left = Math.max(one.left, two.left); // make sure bottom can't be above top, right can't be before left var right = Math.max(Math.min(one.right, two.right), left); var bottom = Math.max(Math.min(one.bottom, two.bottom), top); // return something resembling ClientRect return { top: top, right: right, bottom: bottom, left: left, width: right - left, height: bottom - top }; } function getViewportRect() { var width = window.innerWidth || document.documentElement.clientWidth; var height = window.innerHeight || document.documentElement.clientHeight; // return something resembling ClientRect return { top: 0, right: width, bottom: height, left: 0, width: width, height: height }; } function getInnerBoundingClientRect(element) { // convenience for the .reduce() in getScrollableParentRect() var rect = element.getBoundingClientRect(); // remove the width of the scrollbar because that // area is not really considered visible // NOTE: assuming scrollbar is always to the right and bottom var scrollbarWidth = element.offsetWidth - element.clientWidth; var scrollbarHeight = element.offsetHeight - element.clientHeight; // cannot mutate rect because it has readonly properties var _rect = { top: rect.top, left: rect.left, right: rect.right - scrollbarWidth, bottom: rect.bottom - scrollbarHeight, width: rect.width - scrollbarWidth, height: rect.height - scrollbarHeight, area: 0 }; _rect.area = _rect.width * _rect.height; return _rect; } function isOverflowingElement(element) { var style = window.getComputedStyle(element, null); var value = 'visible'; return style.getPropertyValue('overflow-x') !== value && style.getPropertyValue('overflow-y') !== value; } function isScrollableElement(element) { // an element not scrollable if it doesn't crop its content if (!isOverflowingElement(element)) { return false; } // an element is scrollable when it is smaller than its content return element.offsetHeight < element.scrollHeight || element.offsetWidth < element.scrollWidth; } function getScrollableParentRect(element) { // get largest possible space constrained by scrolling containers // find scrollable parents var scrollingContainers = (0, _parents2.default)({ context: element }).slice(1).filter(isScrollableElement); if (!scrollingContainers.length) { // no containers, no joy return null; } // identify the currently visible intersection of all scrolling container parents return scrollingContainers.reduce(function (previous, current) { var rect = getInnerBoundingClientRect(current); var intersection = getIntersectingRect(rect, previous); // identify the smallest scrolling container so we know how much space // our element can fill at the most - note that this is NOT the area // of the intersection, intersection is just abused as a vehicle intersection.area = Math.min(rect.area, previous.area); return intersection; }, getInnerBoundingClientRect(scrollingContainers[0])); } module.exports = exports['default']; //# sourceMappingURL=visible-area.js.map