'use strict'
|
var attrs: string[] = ['top', 'left', 'right', 'bottom']
|
var inited: boolean
|
var elementComputedStyle = {}
|
var support: string
|
|
function getSupport() {
|
if(!('CSS' in window) || typeof CSS.supports != 'function') {
|
support = ''
|
} else if(CSS.supports('top: env(safe-area-inset-top)')) {
|
support = 'env'
|
} else if(CSS.supports('top: constant(safe-area-inset-top)')) {
|
support = 'constant'
|
} else {
|
support = ''
|
}
|
return support
|
}
|
|
function init() {
|
support = typeof support === 'string' ? support : getSupport()
|
if(!support) {
|
attrs.forEach((attr: string) => {
|
elementComputedStyle[attr] = 0
|
})
|
return
|
}
|
|
function setStyle(el: HTMLElement, style) {
|
var elStyle: CSSStyleDeclaration = el.style
|
Object.keys(style).forEach(key => {
|
var val: string = style[key]
|
elStyle[key] = val
|
})
|
}
|
|
var cbs: Function[] = []
|
function parentReady(callback?: Function) {
|
if(callback) {
|
cbs.push(callback)
|
} else {
|
cbs.forEach(cb => {
|
cb()
|
})
|
}
|
}
|
|
var passiveEvents: any = false
|
try {
|
var opts = Object.defineProperty({}, 'passive', {
|
get: function() {
|
passiveEvents = { passive: true }
|
}
|
})
|
window.addEventListener('test', null, opts)
|
} catch(e) {
|
|
}
|
|
function addChild(parent: HTMLElement, attr: string) {
|
var a1: HTMLElement = document.createElement('div')
|
var a2: HTMLElement = document.createElement('div')
|
var a1Children: HTMLElement = document.createElement('div')
|
var a2Children: HTMLElement = document.createElement('div')
|
var W: number = 100
|
var MAX: number = 10000
|
var aStyle = {
|
position: 'absolute',
|
width: W + 'px',
|
height: '200px',
|
boxSizing: 'border-box',
|
overflow: 'hidden',
|
paddingBottom: `${support}(safe-area-inset-${attr})`
|
}
|
setStyle(a1, aStyle)
|
setStyle(a2, aStyle)
|
setStyle(a1Children, {
|
transition: '0s',
|
animation: 'none',
|
width: '400px',
|
height: '400px'
|
})
|
setStyle(a2Children, {
|
transition: '0s',
|
animation: 'none',
|
width: '250%',
|
height: '250%'
|
})
|
a1.appendChild(a1Children)
|
a2.appendChild(a2Children)
|
parent.appendChild(a1)
|
parent.appendChild(a2)
|
|
parentReady(() => {
|
a1.scrollTop = a2.scrollTop = MAX
|
var a1LastScrollTop: number = a1.scrollTop
|
var a2LastScrollTop: number = a2.scrollTop
|
function onScroll() {
|
if(this.scrollTop === (this === a1 ? a1LastScrollTop : a2LastScrollTop)) {
|
return
|
}
|
a1.scrollTop = a2.scrollTop = MAX
|
a1LastScrollTop = a1.scrollTop
|
a2LastScrollTop = a2.scrollTop
|
attrChange(attr)
|
}
|
a1.addEventListener('scroll', onScroll, passiveEvents)
|
a2.addEventListener('scroll', onScroll, passiveEvents)
|
})
|
|
var computedStyle: CSSStyleDeclaration = getComputedStyle(a1)
|
Object.defineProperty(elementComputedStyle, attr, {
|
configurable: true,
|
get() {
|
return parseFloat(computedStyle.paddingBottom)
|
}
|
})
|
}
|
|
var parentDiv: HTMLElement = document.createElement('div')
|
setStyle(parentDiv, {
|
position: 'absolute',
|
left: '0',
|
top: '0',
|
width: '0',
|
height: '0',
|
zIndex: '-1',
|
overflow: 'hidden',
|
visibility: 'hidden',
|
})
|
attrs.forEach(key => {
|
addChild(parentDiv, key)
|
})
|
document.body.appendChild(parentDiv)
|
parentReady()
|
inited = true
|
}
|
|
function getAttr(attr: string): number {
|
if(!inited) {
|
init()
|
}
|
return elementComputedStyle[attr]
|
}
|
|
var changeAttrs: string[] = []
|
function attrChange(attr: string) {
|
if(!changeAttrs.length) {
|
setTimeout(() => {
|
var style = {}
|
changeAttrs.forEach(attr => {
|
style[attr] = elementComputedStyle[attr]
|
})
|
changeAttrs.length = 0
|
callbacks.forEach(callback => {
|
callback(style)
|
})
|
}, 0)
|
}
|
changeAttrs.push(attr)
|
}
|
|
var callbacks: Function[] = []
|
function onChange(callback: Function) {
|
if(!getSupport()) {
|
return
|
}
|
if(!inited) {
|
init()
|
}
|
if(typeof callback === 'function') {
|
callbacks.push(callback)
|
}
|
}
|
|
function offChange(callback: Function) {
|
var index = callbacks.indexOf(callback)
|
if(index >= 0){
|
callbacks.splice(index, 1)
|
}
|
}
|
|
var safeAreaInsets = {
|
get support(): boolean {
|
return (typeof support === 'string' ? support : getSupport()).length != 0
|
},
|
get top(): number {
|
return getAttr('top')
|
},
|
get left(): number {
|
return getAttr('left')
|
},
|
get right(): number {
|
return getAttr('right')
|
},
|
get bottom(): number {
|
return getAttr('bottom')
|
},
|
onChange,
|
offChange
|
}
|
|
export = safeAreaInsets
|