This repository has been archived on 2023-08-29. You can view files and clone it, but cannot push or open issues/pull-requests.
yutent
/
anot.js
Archived
1
0
Fork 0
master
宇天 2019-01-28 15:22:05 +08:00
parent 49b7a26e62
commit 76ced919e0
6 changed files with 120 additions and 448 deletions

View File

@ -1,6 +1,7 @@
## Anot.js
## Anot.js 2.x
> Anot 是Anot not only templateEngine的缩写。 它是一款迷你,易用、高性能的前端MVVM框架, fork于avalon。进行了大量的重构,精简部分冗余的API, 同时针对组件拓展进行了优化。
>> 2.x版本为 全新版本, 只兼容支持type="module"的浏览器。
```bash
# 开发模式
@ -12,13 +13,9 @@ npm start
# 打包
npm run prod
```
执行完, 会打包为6个版本, 分别是
- anot.js 普通版(可用于webpack)
- anot-touch.js 普通带触摸版(可用于webpack)
- anot.shim.js 自带AMD加载版
- anot-touch.shim.js 自带AMD加载带触摸版
- anot.next.js 未来版(需要支持es6 module的现代浏览器)
- anot-touch.next.js 带触摸的未来版(需要支持es6 module的现代浏览器)
执行完, 会打包为2个版本, 分别是
- anot.js 普通版(需要支持es6 module的现代浏览器)
- anot-touch.js 带触摸的版本(需要支持es6 module的现代浏览器)
### 文档:

View File

@ -1,20 +1,20 @@
/*********************************************************************
* 全局变量及方法 *
**********************************************************************/
var bindingID = 1024
var IEVersion = 0
let bindingID = 1024
let IEVersion = 0
if (window.VBArray) {
IEVersion = document.documentMode || (window.XMLHttpRequest ? 7 : 6)
}
var expose = generateID()
let expose = generateID()
//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function
var DOC = window.document
var head = DOC.head //HEAD元素
let DOC = window.document
let head = DOC.head //HEAD元素
head.insertAdjacentHTML(
'afterBegin',
'<anot skip class="anot-hide"><style id="anot-style">.anot-hide{ display: none!important } slot{visibility:hidden;}</style></anot>'
)
var ifGroup = head.firstChild
let ifGroup = head.firstChild
function log() {
// http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log
@ -34,22 +34,22 @@ function createMap() {
return Object.create(null)
}
var subscribers = '$' + expose
let subscribers = '$' + expose
var nullObject = {} //作用类似于noop只用于代码防御千万不要在它上面添加属性
var rword = /[^, ]+/g //切割字符串为一个个小块以空格或豆号分开它们结合replace实现字符串的forEach
var rw20g = /\w+/g
var rsvg = /^\[object SVG\w*Element\]$/
var oproto = Object.prototype
var ohasOwn = oproto.hasOwnProperty
var serialize = oproto.toString
var ap = Array.prototype
var aslice = ap.slice
var W3C = window.dispatchEvent
var root = DOC.documentElement
var anotFragment = DOC.createDocumentFragment()
var cinerator = DOC.createElement('div')
var class2type = {
let nullObject = {} //作用类似于noop只用于代码防御千万不要在它上面添加属性
let rword = /[^, ]+/g //切割字符串为一个个小块以空格或豆号分开它们结合replace实现字符串的forEach
let rw20g = /\w+/g
let rsvg = /^\[object SVG\w*Element\]$/
let oproto = Object.prototype
let ohasOwn = oproto.hasOwnProperty
let serialize = oproto.toString
let ap = Array.prototype
let aslice = ap.slice
let W3C = window.dispatchEvent
let root = DOC.documentElement
let anotFragment = DOC.createDocumentFragment()
let cinerator = DOC.createElement('div')
let class2type = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
@ -74,9 +74,9 @@ function oneObject(array, val) {
if (typeof array === 'string') {
array = array.match(rword) || []
}
var result = {},
let result = {},
value = val !== void 0 ? val : 1
for (var i = 0, n = array.length; i < n; i++) {
for (let i = 0, n = array.length; i < n; i++) {
result[array[i]] = value
}
return result

View File

@ -3,352 +3,24 @@
// ===============================
// ========== Promise ============
// ===============================
;(function(nativePromise) {
function _yes(val) {
return val
}
function _no(err) {
throw err
}
function done(callback) {
return this.then(callback, _no)
}
function fail(callback) {
return this.then(_yes, callback)
}
function defer() {
var obj = {}
obj.promise = new _Promise(function(yes, no) {
obj.resolve = yes
obj.reject = no
if (!Promise.defer) {
Promise.defer = function() {
let obj = {}
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve
obj.reject = reject
})
return obj
}
//成功的回调
function _resolve(obj, val) {
if (obj._state !== 'pending') {
return
}
if (val && typeof val.then === 'function') {
var method = val instanceof _Promise ? '_then' : 'then'
val[method](
function(v) {
_transmit(obj, v, true)
},
function(v) {
_transmit(obj, v, false)
}
)
} else {
_transmit(obj, val, true)
}
}
//失败的回调
function _reject(obj, val) {
if (obj._state !== 'pending') {
return
}
_transmit(obj, val, false)
}
// 改变Promise的_fired值并保持用户传参触发所有回调
function _transmit(obj, val, isResolved) {
obj._fired = true
obj._val = val
obj._state = isResolved ? 'fulfilled' : 'rejected'
fireCallback(obj, function() {
for (var i in obj.callback) {
obj._fire(obj.callback[i].yes, obj.callback[i].no)
}
})
}
function fireCallback(obj, callback) {
var isAsync = false
if (typeof obj.async === 'boolean') {
isAsync = obj.async
} else {
isAsync = obj.async = true
}
if (isAsync) {
setTimeout(callback, 0)
} else {
callback()
}
}
function _some(bool, iterable) {
iterable = Array.isArray(iterable) ? iterable : []
var n = 0
var res = []
var end = false
return new _Promise(function(yes, no) {
if (!iterable.length) no(res)
function loop(obj, idx) {
obj.then(
function(val) {
if (!end) {
res[idx] = val
n++
if (bool || n >= iterable.length) {
yes(bool ? val : res)
end = true
}
}
},
function(val) {
end = true
no(val)
}
)
}
for (var i = 0, len = iterable.length; i < len; i++) {
loop(iterable[i], i)
}
})
}
//---------------------------
var _Promise = function(callback) {
this.callback = []
var _this = this
if (typeof this !== 'object') {
throw new TypeError('Promises must be constructed via new')
}
if (typeof callback !== 'function') {
throw new TypeError('Argument must be a function')
}
callback(
function(val) {
_resolve(_this, val)
},
function(val) {
_reject(_this, val)
}
)
}
var self = {
_state: 1,
_fired: 1,
_val: 1,
callback: 1
}
_Promise.prototype = {
constructor: _Promise,
_state: 'pending',
_fired: false,
_fire: function(yes, no) {
if (this._state === 'rejected') {
if (typeof no === 'function') no(this._val)
else throw this._val
} else {
if (typeof yes === 'function') yes(this._val)
}
},
_then: function(yes, no) {
if (this._fired) {
var _this = this
fireCallback(_this, function() {
_this._fire(yes, no)
})
} else {
this.callback.push({ yes: yes, no: no })
}
},
then: function(yes, no) {
yes = typeof yes === 'function' ? yes : _yes
no = typeof no === 'function' ? no : _no
var _this = this
var next = new _Promise(function(resolve, reject) {
_this._then(
function(val) {
try {
val = yes(val)
} catch (err) {
return reject(err)
}
resolve(val)
},
function(val) {
try {
val = no(val)
} catch (err) {
return reject(err)
}
resolve(val)
}
)
})
for (var i in _this) {
if (!self[i]) next[i] = _this[i]
}
return next
},
done: done,
catch: fail,
fail: fail
}
_Promise.all = function(arr) {
return _some(false, arr)
}
_Promise.race = function(arr) {
return _some(true, arr)
}
_Promise.defer = defer
_Promise.resolve = function(val) {
var obj = this.defer()
obj.resolve(val)
return obj.promise
}
_Promise.reject = function(val) {
var obj = this.defer()
obj.reject(val)
return obj.promise
}
if (/native code/.test(nativePromise)) {
nativePromise.prototype.done = done
nativePromise.prototype.fail = fail
if (!nativePromise.defer) {
nativePromise.defer = defer
}
}
window.Promise = nativePromise || _Promise
})(window.Promise)
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
value: function(target, first) {
'use strict'
if (target === undefined || target === null)
throw new TypeError('Can not convert first argument to object')
var to = Object(target)
for (var i = 0, len = arguments.length; i < len; i++) {
var next = arguments[i]
if (next === undefined || next === null) continue
var keys = Object.keys(Object(next))
for (var j = 0, n = keys.length; j < n; j++) {
var key = keys[j]
var desc = Object.getOwnPropertyDescriptor(next, key)
if (desc !== undefined && desc.enumerable) to[key] = next[key]
}
}
return to
}
})
}
if (!Array.from) {
Object.defineProperty(Array, 'from', {
enumerable: false,
value: (function() {
var toStr = Object.prototype.toString
var isCallable = function(fn) {
return (
typeof fn === 'function' || toStr.call(fn) === '[object Function]'
)
}
var toInt = function(val) {
var num = val - 0
if (isNaN(num)) return 0
if (num === 0 || isFinite(num)) return num
return (num > 0 ? 1 : -1) * Math.floor(Math.abs(num))
}
var maxInt = Math.pow(2, 53) - 1
var toLen = function(val) {
var len = toInt(val)
return Math.min(Math.max(len, 0), maxInt)
}
return function(arrLike) {
var _this = this
var items = Object(arrLike)
if (arrLike === null)
throw new TypeError(
'Array.from requires an array-like object - not null or undefined'
)
var mapFn = arguments.length > 1 ? arguments[1] : undefined
var other
if (mapFn !== undefined) {
if (!isCallable(mapFn))
throw new TypeError(
'Array.from: when provided, the second argument must be a function'
)
if (arguments.length > 2) other = arguments[2]
}
var len = toLen(items.length)
var arr = isCallable(_this) ? Object(new _this(len)) : new Array(len)
var k = 0
var kVal
while (k < len) {
kVal = items[k]
if (mapFn)
arr[k] =
other === 'undefined'
? mapFn(kVal, k)
: mapFn.call(other, kVal, k)
else arr[k] = kVal
k++
}
arr.length = len
return arr
}
})()
})
}
// 判断数组是否包含指定元素
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(val) {
for (var i in this) {
if (this[i] === val) return true
}
return false
},
enumerable: false
})
}
//类似于Array 的splice方法
if (!String.prototype.splice) {
Object.defineProperty(String.prototype, 'splice', {
value: function(start, len, fill) {
var length = this.length,
argLen = arguments.length
let length = this.length
let argLen = arguments.length
fill = fill === undefined ? '' : fill
@ -358,8 +30,11 @@ if (!String.prototype.splice) {
//处理负数
if (start < 0) {
if (Math.abs(start) >= length) start = 0
else start = length + start
if (Math.abs(start) >= length) {
start = 0
} else {
start = length + start
}
}
if (argLen === 1) {
@ -367,8 +42,8 @@ if (!String.prototype.splice) {
} else {
len -= 0
var strl = this.slice(0, start),
strr = this.slice(start + len)
let strl = this.slice(0, start)
let strr = this.slice(start + len)
return strl + fill + strr
}
@ -381,10 +56,10 @@ if (!Date.prototype.getFullWeek) {
//获取当天是本年度第几周
Object.defineProperty(Date.prototype, 'getFullWeek', {
value: function() {
var thisYear = this.getFullYear(),
that = new Date(thisYear, 0, 1),
firstDay = that.getDay() || 1,
numsOfToday = (this - that) / 86400000
let thisYear = this.getFullYear()
let that = new Date(thisYear, 0, 1)
let firstDay = that.getDay() || 1
let numsOfToday = (this - that) / 86400000
return Math.ceil((numsOfToday + firstDay) / 7)
},
enumerable: false
@ -393,10 +68,10 @@ if (!Date.prototype.getFullWeek) {
//获取当天是本月第几周
Object.defineProperty(Date.prototype, 'getWeek', {
value: function() {
var today = this.getDate(),
thisMonth = this.getMonth(),
thisYear = this.getFullYear(),
firstDay = new Date(thisYear, thisMonth, 1).getDay()
let today = this.getDate()
let thisMonth = this.getMonth()
let thisYear = this.getFullYear()
let firstDay = new Date(thisYear, thisMonth, 1).getDay()
return Math.ceil((today + firstDay) / 7)
},
enumerable: false
@ -417,8 +92,8 @@ if (!Date.prototype.format) {
Object.defineProperty(Date.prototype, 'format', {
value: function(str) {
str = str || 'Y-m-d H:i:s'
var week = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
dt = {
let week = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
let dt = {
fullyear: this.getFullYear(),
year: this.getYear(),
fullweek: this.getFullWeek(),
@ -429,8 +104,8 @@ if (!Date.prototype.format) {
hours: this.getHours(),
minutes: this.getMinutes(),
seconds: this.getSeconds()
},
re
}
let re
dt.g = dt.hours > 12 ? dt.hours - 12 : dt.hours
@ -452,7 +127,7 @@ if (!Date.prototype.format) {
D: dt.day
}
for (var i in re) {
for (let i in re) {
str = str.replace(new RegExp(i, 'g'), re[i])
}
return str

View File

@ -1,4 +1,4 @@
var Anot = function(el) {
let Anot = function(el) {
//创建jQuery式的无new 实例化结构
return new Anot.init(el)
}
@ -6,25 +6,25 @@ var Anot = function(el) {
/*视浏览器情况采用最快的异步回调*/
Anot.nextTick = new function() {
// jshint ignore:line
var tickImmediate = window.setImmediate
var tickObserver = window.MutationObserver
let tickImmediate = window.setImmediate
let tickObserver = window.MutationObserver
if (tickImmediate) {
return tickImmediate.bind(window)
}
var queue = []
let queue = []
function callback() {
var n = queue.length
for (var i = 0; i < n; i++) {
let n = queue.length
for (let i = 0; i < n; i++) {
queue[i]()
}
queue = queue.slice(n)
}
if (tickObserver) {
var node = document.createTextNode('anot')
let node = document.createTextNode('anot')
new tickObserver(callback).observe(node, { characterData: true }) // jshint ignore:line
var bool = false
let bool = false
return function(fn) {
queue.push(fn)
bool = !bool
@ -103,11 +103,11 @@ Anot.isPlainObject = function(obj) {
)
}
var VMODELS = (Anot.vmodels = {}) //所有vmodel都储存在这里
let VMODELS = (Anot.vmodels = {}) //所有vmodel都储存在这里
Anot.init = function(source) {
if (Anot.isPlainObject(source)) {
var $id = source.$id
var vm = null
let $id = source.$id
let vm = null
if (!$id) {
log('warning: vm必须指定id')
}
@ -116,12 +116,12 @@ Anot.init = function(source) {
VMODELS[$id] = vm
Anot.nextTick(function() {
var $elem = document.querySelector('[anot=' + vm.$id + ']')
let $elem = document.querySelector('[anot=' + vm.$id + ']')
if ($elem) {
if ($elem === DOC.body) {
scanTag($elem, [])
} else {
var _parent = $elem
let _parent = $elem
while ((_parent = _parent.parentNode)) {
if (_parent.__VM__) {
break
@ -141,7 +141,7 @@ Anot.fn = Anot.prototype = Anot.init.prototype
//与jQuery.extend方法可用于浅拷贝深拷贝
Anot.mix = Anot.fn.mix = function() {
var options,
let options,
name,
src,
copy,
@ -208,7 +208,7 @@ function cacheStore(tpye, key, val) {
}
if (this.type(key) === 'object') {
for (var i in key) {
for (let i in key) {
window[tpye].setItem(i, key[i])
}
return
@ -233,7 +233,7 @@ function cacheStore(tpye, key, val) {
/*判定是否类数组如节点集合纯数组arguments与拥有非负整数的length属性的纯JS对象*/
function isArrayLike(obj) {
if (obj && typeof obj === 'object') {
var n = obj.length,
let n = obj.length,
str = serialize.call(obj)
if (/(Array|List|Collection|Map|Arguments)\]$/.test(str)) {
return true
@ -275,7 +275,7 @@ Anot.mix({
end = start || 0
start = 0
}
var index = -1,
let index = -1,
length = Math.max(0, Math.ceil((end - start) / step)),
result = new Array(length)
while (++index < length) {
@ -288,11 +288,11 @@ Anot.mix({
eventHooks: {},
/*绑定事件*/
bind: function(el, type, fn, phase) {
var hooks = Anot.eventHooks
let hooks = Anot.eventHooks
type = type.split(',')
Anot.each(type, function(i, t) {
t = t.trim()
var hook = hooks[t]
let hook = hooks[t]
if (typeof hook === 'object') {
type = hook.type || type
phase = hook.phase || !!phase
@ -304,12 +304,12 @@ Anot.mix({
},
/*卸载事件*/
unbind: function(el, type, fn, phase) {
var hooks = Anot.eventHooks
let hooks = Anot.eventHooks
type = type.split(',')
fn = fn || noop
Anot.each(type, function(i, t) {
t = t.trim()
var hook = hooks[t]
let hook = hooks[t]
if (typeof hook === 'object') {
type = hook.type || type
phase = hook.phase || !!phase
@ -322,7 +322,7 @@ Anot.mix({
if (node instanceof Anot) {
node = node[0]
}
var prop = /[_-]/.test(name) ? camelize(name) : name,
let prop = /[_-]/.test(name) ? camelize(name) : name,
fn
name = Anot.cssName(prop) || prop
if (value === void 0 || typeof value === 'boolean') {
@ -331,7 +331,7 @@ Anot.mix({
if (name === 'background') {
name = 'backgroundColor'
}
var val = fn(node, name)
let val = fn(node, name)
return value === true ? parseFloat(val) || 0 : val
} else if (value === '') {
//请除样式
@ -352,9 +352,9 @@ Anot.mix({
each: function(obj, fn) {
if (obj) {
//排除null, undefined
var i = 0
let i = 0
if (isArrayLike(obj)) {
for (var n = obj.length; i < n; i++) {
for (let n = obj.length; i < n; i++) {
if (fn(i, obj[i]) === false) break
}
} else {
@ -379,7 +379,7 @@ Anot.mix({
},
/*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/
remove: function(target, item) {
var index = target.indexOf(item)
let index = target.indexOf(item)
if (~index) return Anot.Array.removeAt(target, index)
return false
}
@ -391,12 +391,12 @@ Anot.mix({
* @return
*/
ls: function() {
var args = aslice.call(arguments, 0)
let args = aslice.call(arguments, 0)
args.unshift('localStorage')
return cacheStore.apply(this, args)
},
ss: function() {
var args = aslice.call(arguments, 0)
let args = aslice.call(arguments, 0)
args.unshift('sessionStorage')
return cacheStore.apply(this, args)
},
@ -483,16 +483,16 @@ Anot.mix({
//获取url的参数
search: function(key) {
key += ''
var uri = location.search
let uri = location.search
if (!key || !uri) return null
uri = uri.slice(1)
uri = uri.split('&')
var obj = {}
for (var i = 0, item; (item = uri[i++]); ) {
var tmp = item.split('=')
let obj = {}
for (let i = 0, item; (item = uri[i++]); ) {
let tmp = item.split('=')
tmp[1] = tmp.length < 2 ? null : tmp[1]
tmp[1] = decodeURIComponent(tmp[1])
if (obj.hasOwnProperty(tmp[0])) {
@ -514,7 +514,7 @@ Anot.mix({
return log('该浏览器不支持复制到粘贴板')
}
var ta = DOC.createElement('textarea')
let ta = DOC.createElement('textarea')
ta.textContent = txt
ta.style.position = 'fixed'
ta.style.bottom = '-1000px'
@ -529,10 +529,10 @@ Anot.mix({
}
})
var bindingHandlers = (Anot.bindingHandlers = {})
var bindingExecutors = (Anot.bindingExecutors = {})
let bindingHandlers = (Anot.bindingHandlers = {})
let bindingExecutors = (Anot.bindingExecutors = {})
var directives = (Anot.directives = {})
let directives = (Anot.directives = {})
Anot.directive = function(name, obj) {
bindingHandlers[name] = obj.init = obj.init || noop
bindingExecutors[name] = obj.update = obj.update || noop

View File

@ -1,5 +1,5 @@
// https://github.com/rsms/js-lru
var Cache = new function() {
let Cache = new function() {
// jshint ignore:line
function LRU(maxLength) {
this.size = 0
@ -8,10 +8,10 @@ var Cache = new function() {
this._keymap = {}
}
var p = LRU.prototype
let p = LRU.prototype
p.put = function(key, value) {
var entry = {
let entry = {
key: key,
value: value
}
@ -32,7 +32,7 @@ var Cache = new function() {
}
p.shift = function() {
var entry = this.head
let entry = this.head
if (entry) {
this.head = this.head.newer
this.head.older = entry.newer = entry.older = this._keymap[
@ -42,7 +42,7 @@ var Cache = new function() {
}
}
p.get = function(key) {
var entry = this._keymap[key]
let entry = this._keymap[key]
if (entry === void 0) return
if (entry === this.tail) {
return entry.value

View File

@ -18,18 +18,18 @@ Anot.contains = function(root, el) {
}
if (window.SVGElement) {
var svgns = 'http://www.w3.org/2000/svg'
var svg = DOC.createElementNS(svgns, 'svg')
let svgns = 'http://www.w3.org/2000/svg'
let svg = DOC.createElementNS(svgns, 'svg')
svg.innerHTML = '<circle cx="50" cy="50" r="40" fill="red" />'
if (!rsvg.test(svg.firstChild)) {
// #409
/* jshint ignore:start */
function enumerateNode(node, targetNode) {
if (node && node.childNodes) {
var nodes = node.childNodes
for (var i = 0, el; (el = nodes[i++]); ) {
let nodes = node.childNodes
for (let i = 0, el; (el = nodes[i++]); ) {
if (el.tagName) {
var svg = DOC.createElementNS(svgns, el.tagName.toLowerCase())
let svg = DOC.createElementNS(svgns, el.tagName.toLowerCase())
// copy attrs
ap.forEach.call(el.attributes, function(attr) {
svg.setAttribute(attr.name, attr.value)
@ -51,7 +51,7 @@ if (window.SVGElement) {
return new XMLSerializer().serializeToString(this)
},
set: function(html) {
var tagName = this.tagName.toLowerCase(),
let tagName = this.tagName.toLowerCase(),
par = this.parentNode,
frag = Anot.parseHTML(html)
// 操作的svg直接插入
@ -59,7 +59,7 @@ if (window.SVGElement) {
par.insertBefore(frag, this)
// svg节点的子节点类似
} else {
var newFrag = DOC.createDocumentFragment()
let newFrag = DOC.createDocumentFragment()
enumerateNode(frag, newFrag)
par.insertBefore(newFrag, this)
}
@ -70,18 +70,18 @@ if (window.SVGElement) {
enumerable: true,
configurable: true,
get: function() {
var s = this.outerHTML
var ropen = new RegExp(
let s = this.outerHTML
let ropen = new RegExp(
'<' + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>',
'i'
)
var rclose = new RegExp('</' + this.nodeName + '>$', 'i')
let rclose = new RegExp('</' + this.nodeName + '>$', 'i')
return s.replace(ropen, '').replace(rclose, '')
},
set: function(html) {
if (Anot.clearHTML) {
Anot.clearHTML(this)
var frag = Anot.parseHTML(html)
let frag = Anot.parseHTML(html)
enumerateNode(frag, this)
}
}
@ -92,7 +92,7 @@ if (window.SVGElement) {
//========================= event binding ====================
var eventHooks = Anot.eventHooks
let eventHooks = Anot.eventHooks
//针对firefox, chrome修正mouseenter, mouseleave(chrome30+)
if (!('onmouseenter' in root)) {
@ -106,7 +106,7 @@ if (!('onmouseenter' in root)) {
type: fixType,
fix: function(elem, fn) {
return function(e) {
var t = e.relatedTarget
let t = e.relatedTarget
if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) {
delete e.type
e.type = origType