next-csrf
Version:
CSRF mitigation library for Next.js
171 lines (139 loc) • 5.8 kB
JavaScript
(/* istanbul ignore next */ function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['module', 'exports', '../util/array-find-index', '../get/parents', '../util/context-to-element', '../util/get-frame-element'], factory);
} else if (typeof exports !== "undefined") {
factory(module, exports, require('../util/array-find-index'), require('../get/parents'), require('../util/context-to-element'), require('../util/get-frame-element'));
} else {
var mod = {
exports: {}
};
factory(mod, mod.exports, global.arrayFindIndex, global.parents, global.contextToElement, global.getFrameElement);
global.visible = mod.exports;
}
})(this, function (module, exports, _arrayFindIndex, _parents, _contextToElement, _getFrameElement) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _arrayFindIndex2 = _interopRequireDefault(_arrayFindIndex);
var _parents2 = _interopRequireDefault(_parents);
var _contextToElement2 = _interopRequireDefault(_contextToElement);
var _getFrameElement2 = _interopRequireDefault(_getFrameElement);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
// https://www.w3.org/TR/html5/rendering.html#being-rendered
// <area> is not rendered, but we *consider* it visible to simplfiy this function's usage
// determine if an element is rendered
// NOTE: that does not mean an element is visible in the viewport, see util/visible-area
var notRenderedElementsPattern = /^(area)$/;
function computedStyle(element, property) {
return window.getComputedStyle(element, null).getPropertyValue(property);
}
function notDisplayed(_path) {
return _path.some(function (element) {
// display:none is not visible (optimized away at layout)
return computedStyle(element, 'display') === 'none';
});
}
function notVisible(_path) {
// https://github.com/jquery/jquery-ui/blob/master/ui/core.js#L109-L114
// NOTE: a nested element can reverse visibility:hidden|collapse by explicitly setting visibility:visible
// NOTE: visibility can be ["", "visible", "hidden", "collapse"]
var hidden = (0, _arrayFindIndex2.default)(_path, function (element) {
var visibility = computedStyle(element, 'visibility');
return visibility === 'hidden' || visibility === 'collapse';
});
if (hidden === -1) {
// there is no hidden element
return false;
}
var visible = (0, _arrayFindIndex2.default)(_path, function (element) {
return computedStyle(element, 'visibility') === 'visible';
});
if (visible === -1) {
// there is no visible element (but a hidden element)
return true;
}
if (hidden < visible) {
// there is a hidden element and it's closer than the first visible element
return true;
}
// there may be a hidden element, but the closest element is visible
return false;
}
function collapsedParent(_path) {
var offset = 1;
if (_path[0].nodeName.toLowerCase() === 'summary') {
offset = 2;
}
return _path.slice(offset).some(function (element) {
// "content children" of a closed details element are not visible
return element.nodeName.toLowerCase() === 'details' && element.open === false;
});
}
function isVisibleRules() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
context = _ref.context,
_ref$except = _ref.except,
except = _ref$except === undefined ? {
notRendered: false,
cssDisplay: false,
cssVisibility: false,
detailsElement: false,
browsingContext: false
} : _ref$except;
var element = (0, _contextToElement2.default)({
label: 'is/visible',
resolveDocument: true,
context: context
});
var nodeName = element.nodeName.toLowerCase();
if (!except.notRendered && notRenderedElementsPattern.test(nodeName)) {
return true;
}
var _path = (0, _parents2.default)({ context: element });
// in Internet Explorer <audio> has a default display: none, where others have display: inline
// but IE allows focusing <audio style="display:none">, but not <div display:none><audio>
// this is irrelevant to other browsers, as the controls attribute is required to make <audio> focusable
var isAudioWithoutControls = nodeName === 'audio' && !element.hasAttribute('controls');
if (!except.cssDisplay && notDisplayed(isAudioWithoutControls ? _path.slice(1) : _path)) {
return false;
}
if (!except.cssVisibility && notVisible(_path)) {
return false;
}
if (!except.detailsElement && collapsedParent(_path)) {
return false;
}
if (!except.browsingContext) {
// elements within a browsing context are affected by the
// browsing context host element's visibility and tabindex
var frameElement = (0, _getFrameElement2.default)(element);
var _isVisible = isVisibleRules.except(except);
if (frameElement && !_isVisible(frameElement)) {
return false;
}
}
return true;
}
// bind exceptions to an iterator callback
isVisibleRules.except = function () {
var except = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var isVisible = function isVisible(context) {
return isVisibleRules({
context: context,
except: except
});
};
isVisible.rules = isVisibleRules;
return isVisible;
};
// provide isVisible(context) as default iterator callback
var isVisible = isVisibleRules.except({});
exports.default = isVisible;
module.exports = exports['default'];
});
//# sourceMappingURL=visible.js.map