2019-03-21 09:57:27 +01:00
|
|
|
var recordedErrors = new Map()
|
|
|
|
|
|
|
|
export default class Errors {
|
|
|
|
static countErrors() {
|
|
|
|
let total = 0
|
2019-07-18 12:26:39 +02:00
|
|
|
for (let error of recordedErrors.keys()) {
|
2019-03-21 09:57:27 +01:00
|
|
|
total += recordedErrors.get(error).size
|
|
|
|
}
|
|
|
|
return total
|
|
|
|
}
|
|
|
|
|
|
|
|
static setStyle(element, styles) {
|
2019-07-18 12:26:39 +02:00
|
|
|
for (let key in styles) {
|
2019-03-21 09:57:27 +01:00
|
|
|
element.style[key] = styles[key]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static appendError(error, source) {
|
|
|
|
if (recordedErrors.has(error)) {
|
|
|
|
let sources = recordedErrors.get(error)
|
|
|
|
sources.add(source)
|
2019-07-18 12:26:39 +02:00
|
|
|
} else {
|
2019-03-21 09:57:27 +01:00
|
|
|
recordedErrors.set(error, new Set([source]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static showErrors() {
|
|
|
|
if (this.countErrors() == 0) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
let errors = document.getElementById('runtime-errors')
|
|
|
|
if (errors == null) {
|
|
|
|
errors = document.createElement('div')
|
|
|
|
errors.setAttribute('id', 'runtime-errors')
|
|
|
|
this.setStyle(document.body, {
|
|
|
|
border: '2px solid red'
|
|
|
|
})
|
2019-07-18 12:26:39 +02:00
|
|
|
this.setStyle(errors, {
|
|
|
|
position: 'absolute',
|
2019-03-21 09:57:27 +01:00
|
|
|
top: '0px',
|
|
|
|
padding: '8px',
|
|
|
|
width: '100%',
|
|
|
|
background: 'red',
|
2019-07-18 12:26:39 +02:00
|
|
|
color: 'white'
|
|
|
|
})
|
2019-03-21 09:57:27 +01:00
|
|
|
document.body.appendChild(errors)
|
|
|
|
let counter = document.createElement('div')
|
|
|
|
counter.setAttribute('id', 'runtime-errors-counter')
|
2019-07-18 12:26:39 +02:00
|
|
|
this.setStyle(counter, {
|
|
|
|
borderRadius: '50%',
|
2019-03-21 09:57:27 +01:00
|
|
|
width: '32px',
|
|
|
|
height: '32px',
|
|
|
|
background: 'white',
|
|
|
|
color: 'red',
|
|
|
|
fontSize: '18px',
|
|
|
|
textAlign: 'center',
|
|
|
|
lineHeight: '32px',
|
2019-07-18 12:26:39 +02:00
|
|
|
verticalAlign: 'middle'
|
|
|
|
})
|
2019-03-21 09:57:27 +01:00
|
|
|
counter.innerHTML = '1'
|
|
|
|
errors.appendChild(counter)
|
|
|
|
|
|
|
|
let header = document.createElement('div')
|
2019-07-18 12:26:39 +02:00
|
|
|
this.setStyle(header, {
|
|
|
|
position: 'absolute',
|
2019-03-21 09:57:27 +01:00
|
|
|
top: '6px',
|
|
|
|
left: '48px',
|
|
|
|
height: '44px',
|
2019-07-18 12:26:39 +02:00
|
|
|
fontSize: '32px'
|
|
|
|
})
|
2019-03-21 09:57:27 +01:00
|
|
|
header.innerHTML = 'Runtime Errors'
|
|
|
|
errors.appendChild(header)
|
|
|
|
errors.addEventListener('click', this.toggleErrors.bind(this))
|
|
|
|
}
|
|
|
|
let counter = document.getElementById('runtime-errors-counter')
|
|
|
|
counter.innerHTML = this.countErrors()
|
|
|
|
}
|
|
|
|
|
|
|
|
static expandErrors() {
|
|
|
|
let errors = document.getElementById('runtime-errors')
|
2019-07-18 12:26:39 +02:00
|
|
|
for (let error of recordedErrors.keys()) {
|
|
|
|
for (var source of recordedErrors.get(error)) {
|
|
|
|
if (typeof source == 'undefined') {
|
2019-03-21 09:57:27 +01:00
|
|
|
source = 'See console for details'
|
|
|
|
return
|
|
|
|
}
|
|
|
|
let info = document.createElement('div')
|
|
|
|
info.className = 'info'
|
|
|
|
info.style.wordWrap = 'break-word'
|
|
|
|
info.innerHTML = error + `<br/><small>${source}</small>`
|
|
|
|
errors.appendChild(info)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static toggleErrors() {
|
|
|
|
let errors = document.getElementById('runtime-errors')
|
|
|
|
let infos = errors.querySelectorAll('.info')
|
|
|
|
if (infos.length > 0) {
|
2019-07-18 12:26:39 +02:00
|
|
|
infos.forEach(info => errors.removeChild(info))
|
|
|
|
} else {
|
2019-03-21 09:57:27 +01:00
|
|
|
this.expandErrors()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static removeError(event) {
|
|
|
|
console.log('removeError', event)
|
|
|
|
if (recordedErrors.has(event.error)) {
|
|
|
|
let sources = recordedErrors.get(event.error)
|
|
|
|
sources.delete(event.source)
|
|
|
|
console.log('sources', sources)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static registerGlobalErrorHandler() {
|
|
|
|
// Register more informative error handler
|
2019-07-18 12:26:39 +02:00
|
|
|
window.addEventListener(
|
|
|
|
'error',
|
|
|
|
event => {
|
2021-02-24 16:13:50 +01:00
|
|
|
if (typeof event.error == 'undefined') {
|
2020-02-25 15:19:01 +01:00
|
|
|
// This sometimes happens in Edge. Since we have no error
|
|
|
|
// position, we cannot do much beside an info log.
|
2021-02-24 16:13:50 +01:00
|
|
|
console.info('Catched undefined error', event)
|
2020-02-25 15:19:01 +01:00
|
|
|
return
|
|
|
|
}
|
2019-07-18 12:26:39 +02:00
|
|
|
this.appendError(event.error, event.filename)
|
|
|
|
},
|
|
|
|
true
|
|
|
|
)
|
2019-03-21 09:57:27 +01:00
|
|
|
|
2019-07-18 12:26:39 +02:00
|
|
|
document.addEventListener('DOMContentLoaded', event => {
|
2019-03-21 09:57:27 +01:00
|
|
|
this.showErrors()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
static registerFrameAwaitErrors() {
|
|
|
|
let iframes = document.getElementsByTagName('iframe')
|
2019-07-18 12:26:39 +02:00
|
|
|
for (let i = 0; i < iframes.length; i++) {
|
2019-03-21 09:57:27 +01:00
|
|
|
let target = iframes[i]
|
2019-07-18 12:26:39 +02:00
|
|
|
target.iframeTimeout = setTimeout(() => {
|
|
|
|
this.appendError('Cannot load iframe', target.src)
|
|
|
|
}, frameErrorTimeout)
|
2019-03-21 09:57:27 +01:00
|
|
|
target.onload = () => {
|
|
|
|
clearTimeout(target.iframeTimeout)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Errors.registerGlobalErrorHandler()
|