This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
bytedo
/
anot
Archived
1
0
Fork 0

移除部分兼容处理;移除表单验证指令

master
宇天 2020-08-17 19:04:17 +08:00
parent 8b21f0a800
commit 099378aedd
12 changed files with 273 additions and 298 deletions

View File

@ -8,13 +8,13 @@ import './text'
import './css' import './css'
import './expr' import './expr'
import './attr.modern' import './attr'
import './html' import './html'
import './if' import './if'
import './on' import './on'
import './for' import './for'
import './class.hover.active' import './class.hover.active'
import './duplex/modern' import './duplex/index'
import './rules' // import './rules'
import './validate' // import './validate'

View File

@ -1,10 +1,6 @@
import { Anot, escapeRegExp } from '../seed/core' import { Anot, escapeRegExp } from '../seed/core'
import { $$skipArray } from '../vmodel/reserved' import { $$skipArray } from '../vmodel/reserved'
/*
https://github.com/hufyhang/orderBy/blob/master/index.js
*/
export function orderBy(array, by, decend) { export function orderBy(array, by, decend) {
var type = Anot.type(array) var type = Anot.type(array)
if (type !== 'array' && type !== 'object') throw 'orderBy只能处理对象或数组' if (type !== 'array' && type !== 'object') throw 'orderBy只能处理对象或数组'
@ -88,14 +84,7 @@ export function filterBy(array, search, ...args) {
var target = isArray ? [] : {} var target = isArray ? [] : {}
__repeat(array, isArray, function(key) { __repeat(array, isArray, function(key) {
var val = array[key] var val = array[key]
if ( if (criteria.apply({ key }, [val, key].concat(args))) {
criteria.apply(
{
key: key
},
[val, key].concat(args)
)
) {
if (isArray) { if (isArray) {
target.push(val) target.push(val)
} else { } else {
@ -121,7 +110,9 @@ export function selectBy(data, array, defaults) {
export function limitBy(input, limit, begin) { export function limitBy(input, limit, begin) {
var type = Anot.type(input) var type = Anot.type(input)
if (type !== 'array' && type !== 'object') throw 'limitBy只能处理对象或数组' if (type !== 'array' && type !== 'object') {
throw 'limitBy只能处理对象或数组'
}
//必须是数值 //必须是数值
if (typeof limit !== 'number') { if (typeof limit !== 'number') {
return input return input

View File

@ -3,7 +3,7 @@ import { fromDOM } from '../vtree/fromDOM'
import { fromString } from '../vtree/fromString' import { fromString } from '../vtree/fromString'
import { VFragment } from '../vdom/VFragment' import { VFragment } from '../vdom/VFragment'
import { Directive } from './Directive' import { Directive } from './directive'
import { orphanTag } from '../vtree/orphanTag' import { orphanTag } from '../vtree/orphanTag'
import { parseAttributes, eventMap } from '../parser/attributes' import { parseAttributes, eventMap } from '../parser/attributes'
@ -11,6 +11,8 @@ import { parseInterpolate } from '../parser/interpolate'
import { startWith, groupTree, dumpTree, getRange } from './share' import { startWith, groupTree, dumpTree, getRange } from './share'
var viewID
/** /**
* 生成一个渲染器,并作为它第一个遇到的ms-controller对应的VM的$render属性 * 生成一个渲染器,并作为它第一个遇到的ms-controller对应的VM的$render属性
* @param {String|DOM} node * @param {String|DOM} node
@ -25,17 +27,18 @@ Anot.scan = function(node, vm, beforeReady) {
/** /**
* Anot.scan 的内部实现 * Anot.scan 的内部实现
*/ */
function Render(node, vm, beforeReady) {
this.root = node //如果传入的字符串,确保只有一个标签作为根节点
this.vm = vm
this.beforeReady = beforeReady
this.bindings = [] //收集待加工的绑定属性
this.callbacks = []
this.directives = []
this.init()
}
Render.prototype = { class Render {
constructor(node, vm, beforeReady) {
this.root = node //如果传入的字符串,确保只有一个标签作为根节点
this.vm = vm
this.beforeReady = beforeReady
this.bindings = [] //收集待加工的绑定属性
this.callbacks = []
this.directives = []
this.init()
}
/** /**
* 开始扫描指定区域 * 开始扫描指定区域
* 收集绑定属性 * 收集绑定属性
@ -50,15 +53,13 @@ Render.prototype = {
} else if (typeof this.root === 'string') { } else if (typeof this.root === 'string') {
vnodes = fromString(this.root) //转换虚拟DOM vnodes = fromString(this.root) //转换虚拟DOM
} else { } else {
return console.warn( return console.warn('Anot.scan()第1个参数,只能是节点对象或节点字符串')
'Anot.scan first argument must element or HTML string'
)
} }
this.root = vnodes[0] this.root = vnodes[0]
this.vnodes = vnodes this.vnodes = vnodes
this.scanChildren(vnodes, this.vm, true) this.scanChildren(vnodes, this.vm, true)
}, }
scanChildren(children, scope, isRoot) { scanChildren(children, scope, isRoot) {
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
@ -81,7 +82,7 @@ Render.prototype = {
if (isRoot) { if (isRoot) {
this.complete() this.complete()
} }
}, }
/** /**
* 从文本节点获取指令 * 从文本节点获取指令
@ -99,7 +100,7 @@ Render.prototype = {
} }
]) ])
} }
}, }
/** /**
* 从注释节点获取指令 * 从注释节点获取指令
@ -112,7 +113,7 @@ Render.prototype = {
if (startWith(vdom.nodeValue, 'ms-for:')) { if (startWith(vdom.nodeValue, 'ms-for:')) {
this.getForBinding(vdom, scope, parentChildren) this.getForBinding(vdom, scope, parentChildren)
} }
}, }
/** /**
* 从元素节点的nodeName与属性中获取指令 * 从元素节点的nodeName与属性中获取指令
@ -223,7 +224,7 @@ Render.prototype = {
) { ) {
this.scanChildren(children, scope, false) this.scanChildren(children, scope, false)
} }
}, }
/** /**
* 将绑定属性转换为指令 * 将绑定属性转换为指令
@ -245,7 +246,7 @@ Render.prototype = {
fn() fn()
} }
this.optimizeDirectives() this.optimizeDirectives()
}, }
/** /**
* 将收集到的绑定属性进行深加工,最后转换指令 * 将收集到的绑定属性进行深加工,最后转换指令
@ -276,7 +277,7 @@ Render.prototype = {
this.directives.push(directive) this.directives.push(directive)
} }
} }
}, }
/** /**
* 修改指令的update与callback方法,让它们以后执行时更加高效 * 修改指令的update与callback方法,让它们以后执行时更加高效
@ -288,12 +289,13 @@ Render.prototype = {
el.update = newUpdate el.update = newUpdate
el._isScheduled = false el._isScheduled = false
} }
}, }
update: function() {
update() {
for (var i = 0, el; (el = this.directives[i++]); ) { for (var i = 0, el; (el = this.directives[i++]); ) {
el.update() el.update()
} }
}, }
/** /**
* 销毁所有指令 * 销毁所有指令
@ -308,7 +310,7 @@ Render.prototype = {
for (let i in this) { for (let i in this) {
if (i !== 'dispose') delete this[i] if (i !== 'dispose') delete this[i]
} }
}, }
/** /**
* 将循环区域转换为for指令 * 将循环区域转换为for指令
@ -341,7 +343,7 @@ Render.prototype = {
parentChildren parentChildren
} }
]) ])
}, }
/** /**
* 在带ms-for元素节点旁添加两个注释节点,组成循环区域 * 在带ms-for元素节点旁添加两个注释节点,组成循环区域
@ -370,7 +372,6 @@ Render.prototype = {
this.getForBinding(begin, scope, parentChildren, props['data-for-rendered']) this.getForBinding(begin, scope, parentChildren, props['data-for-rendered'])
} }
} }
var viewID
function newUpdate() { function newUpdate() {
var oldVal = this.beforeUpdate() var oldVal = this.beforeUpdate()

View File

@ -8,104 +8,98 @@
removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added
*/ */
export function Cache(maxLength) {
export class Cache {
constructor(maxLength) {
// 标识当前缓存数组的大小 // 标识当前缓存数组的大小
this.size = 0 this.size = 0
// 标识缓存数组能达到的最大长度 // 标识缓存数组能达到的最大长度
this.limit = maxLength this.limit = maxLength
// head最不常用的项tail最常用的项全部初始化为undefined // head最不常用的项tail最常用的项全部初始化为undefined
this.head = this.tail = void 0 this.head = this.tail = void 0
this._keymap = {} this._keymap = {}
} }
Cache.prototype = { put(key, value) {
var entry = { key, value }
put(key, value) { this._keymap[key] = entry
var entry = { if (this.tail) {
key: key, // 如果存在tail缓存数组的长度不为0将tail指向新的 entry
value: value this.tail.newer = entry
} entry.older = this.tail
this._keymap[key] = entry } else {
if (this.tail) { // 如果缓存数组的长度为0将head指向新的entry
// 如果存在tail缓存数组的长度不为0将tail指向新的 entry this.head = entry
this.tail.newer = entry
entry.older = this.tail
} else {
// 如果缓存数组的长度为0将head指向新的entry
this.head = entry
}
this.tail = entry
// 如果缓存数组达到上限,则先删除 head 指向的缓存对象
/* istanbul ignore if */
if (this.size === this.limit) {
this.shift()
} else {
this.size++
}
return value
},
shift() {
/* istanbul ignore next */
var entry = this.head
/* istanbul ignore if */
if (entry) {
// 删除 head ,并改变指向
this.head = this.head.newer
// 同步更新 _keymap 里面的属性值
this.head.older =
entry.newer =
entry.older =
this._keymap[entry.key] =
void 0
delete this._keymap[entry.key] //#1029
// 同步更新 缓存数组的长度
this.size--
}
},
get(key) {
var entry = this._keymap[key]
// 如果查找不到含有`key`这个属性的缓存对象
if (entry === void 0)
return
// 如果查找到的缓存对象已经是 tail (最近使用过的)
/* istanbul ignore if */
if (entry === this.tail) {
return entry.value
}
// HEAD--------------TAIL
// <.older .newer>
// <--- add direction --
// A B C <D> E
if (entry.newer) {
// 处理 newer 指向
if (entry === this.head) {
// 如果查找到的缓存对象是 head (最近最少使用过的)
// 则将 head 指向原 head 的 newer 所指向的缓存对象
this.head = entry.newer
}
// 将所查找的缓存对象的下一级的 older 指向所查找的缓存对象的older所指向的值
// 例如A B C D E
// 如果查找到的是D那么将E指向C不再指向D
entry.newer.older = entry.older // C <-- E.
}
if (entry.older) {
// 处理 older 指向
// 如果查找到的是D那么C指向E不再指向D
entry.older.newer = entry.newer // C. --> E
}
// 处理所查找到的对象的 newer 以及 older 指向
entry.newer = void 0 // D --x
// older指向之前使用过的变量即D指向E
entry.older = this.tail // D. --> E
if (this.tail) {
// 将E的newer指向D
this.tail.newer = entry // E. <-- D
}
// 改变 tail 为D
this.tail = entry
return entry.value
} }
} this.tail = entry
// 如果缓存数组达到上限,则先删除 head 指向的缓存对象
/* istanbul ignore if */
if (this.size === this.limit) {
this.shift()
} else {
this.size++
}
return value
}
shift() {
/* istanbul ignore next */
var entry = this.head
/* istanbul ignore if */
if (entry) {
// 删除 head ,并改变指向
this.head = this.head.newer
// 同步更新 _keymap 里面的属性值
this.head.older = entry.newer = entry.older = this._keymap[
entry.key
] = void 0
delete this._keymap[entry.key] //#1029
// 同步更新 缓存数组的长度
this.size--
}
}
get(key) {
var entry = this._keymap[key]
// 如果查找不到含有`key`这个属性的缓存对象
if (entry === void 0) return
// 如果查找到的缓存对象已经是 tail (最近使用过的)
/* istanbul ignore if */
if (entry === this.tail) {
return entry.value
}
// HEAD--------------TAIL
// <.older .newer>
// <--- add direction --
// A B C <D> E
if (entry.newer) {
// 处理 newer 指向
if (entry === this.head) {
// 如果查找到的缓存对象是 head (最近最少使用过的)
// 则将 head 指向原 head 的 newer 所指向的缓存对象
this.head = entry.newer
}
// 将所查找的缓存对象的下一级的 older 指向所查找的缓存对象的older所指向的值
// 例如A B C D E
// 如果查找到的是D那么将E指向C不再指向D
entry.newer.older = entry.older // C <-- E.
}
if (entry.older) {
// 处理 older 指向
// 如果查找到的是D那么C指向E不再指向D
entry.older.newer = entry.newer // C. --> E
}
// 处理所查找到的对象的 newer 以及 older 指向
entry.newer = void 0 // D --x
// older指向之前使用过的变量即D指向E
entry.older = this.tail // D. --> E
if (this.tail) {
// 将E的newer指向D
this.tail.newer = entry // E. <-- D
}
// 改变 tail 为D
this.tail = entry
return entry.value
}
}

View File

@ -24,8 +24,8 @@ export function oneObject(array, val) {
if (typeof array === 'string') { if (typeof array === 'string') {
array = array.match(rword) || [] array = array.match(rword) || []
} }
var result = {}, var result = {}
value = val !== void 0 ? val : 1 var value = val !== void 0 ? val : 1
for (var i = 0, n = array.length; i < n; i++) { for (var i = 0, n = array.length; i < n; i++) {
result[array[i]] = value result[array[i]] = value
} }
@ -114,9 +114,10 @@ export var validators = {}
export var cssHooks = {} export var cssHooks = {}
window.Anot = Anot window.Anot = Anot
Anot.platform = platform
/* istanbul ignore next */
export function createFragment() { export function createFragment() {
/* istanbul ignore next */
return document.createDocumentFragment() return document.createDocumentFragment()
} }

View File

@ -6,22 +6,20 @@ var rarraylike = /(Array|List|Collection|Map|Arguments|Set)\]$/
// Anot.type // Anot.type
var class2type = {} var class2type = {}
'Boolean Number String Function Array Date RegExp Object Error'.replace( 'Boolean,Number,String,Function,Array,Date,RegExp,Object,Error,AsyncFunction,Promise,Generator,GeneratorFunction'
Anot.rword, .split(',')
function(name) { .forEach(function(name) {
class2type['[object ' + name + ']'] = name.toLowerCase() class2type['[object ' + name + ']'] = name.toLowerCase()
} })
)
Anot.type = function(obj) { Anot.type = function(obj) {
//取得目标的类型 if (obj) {
if (obj == null) { // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准可以将正则字面量当作函数使用因此typeof在判定正则时会返回function
return String(obj) return typeof obj === 'object' || typeof obj === 'function'
? class2type[inspect.call(obj)] || 'object'
: typeof obj
} }
// 早期的webkit内核浏览器实现了已废弃的ecma262v4标准可以将正则字面量当作函数使用因此typeof在判定正则时会返回function return String(obj)
return typeof obj === 'object' || typeof obj === 'function'
? class2type[inspect.call(obj)] || 'object'
: typeof obj
} }
Anot.isFunction = function(fn) { Anot.isFunction = function(fn) {
@ -123,8 +121,8 @@ export function isArrayLike(obj) {
} }
Anot.each = function(obj, fn) { Anot.each = function(obj, fn) {
//排除null, undefined
if (obj) { if (obj) {
//排除null, undefined
var i = 0 var i = 0
if (isArrayLike(obj)) { if (isArrayLike(obj)) {
for (var n = obj.length; i < n; i++) { for (var n = obj.length; i < n; i++) {

View File

@ -5,32 +5,34 @@ import {
propagateChanged propagateChanged
} from './transaction' } from './transaction'
import { Anot, platform } from '../seed/core' import { Anot, platform } from '../seed/core'
/** /**
* *
与Computed等共享UUID 与Computed等共享UUID
*/ */
export let obid = 1 export let obid = 1
export function Mutation(expr, value, vm) {
//构造函数
this.expr = expr
if (value) {
var childVm = platform.createProxy(value, this)
if (childVm) {
value = childVm
}
}
this.value = value
this.vm = vm
try {
vm.$mutations[expr] = this
} catch (ignoreIE) {}
this.uuid = ++obid
this.updateVersion()
this.mapIDs = {}
this.observers = []
}
Mutation.prototype = { export class Mutation {
constructor(expr, value, vm) {
//构造函数
this.expr = expr
if (value) {
var childVm = platform.createProxy(value, this)
if (childVm) {
value = childVm
}
}
this.value = value
this.vm = vm
try {
vm.$mutations[expr] = this
} catch (ignoreIE) {}
this.uuid = ++obid
this.updateVersion()
this.mapIDs = {}
this.observers = []
}
get() { get() {
if (Anot.trackingAction) { if (Anot.trackingAction) {
this.collect() //被收集 this.collect() //被收集
@ -52,22 +54,7 @@ Mutation.prototype = {
} }
} }
return this.value return this.value
}, }
collect() {
// Anot.track(name, '被收集')
reportObserved(this)
},
updateVersion() {
this.version = Math.random() + Math.random()
},
notify() {
transactionStart()
propagateChanged(this)
transactionEnd()
},
set(newValue) { set(newValue) {
var oldValue = this.value var oldValue = this.value
@ -87,4 +74,18 @@ Mutation.prototype = {
this.notify() this.notify()
} }
} }
collect() {
// Anot.track(name, '被收集')
reportObserved(this)
}
updateVersion() {
this.version = Math.random() + Math.random()
}
notify() {
transactionStart()
propagateChanged(this)
transactionEnd()
}
} }

View File

@ -3,76 +3,76 @@ import { voidTag } from './voidTag'
import { makeOrphan } from './makeOrphan' import { makeOrphan } from './makeOrphan'
export function fromDOM(dom) { export function fromDOM(dom) {
return [from(dom)] return [_from(dom)]
} }
export function from(node) { export function _from(node) {
var type = node.nodeName.toLowerCase() var type = node.nodeName.toLowerCase()
switch (type) { switch (type) {
case '#text': case '#text':
case '#comment': case '#comment':
return { return {
nodeName: type, nodeName: type,
dom: node, dom: node,
nodeValue: node.nodeValue nodeValue: node.nodeValue
} }
default: default:
var props = markProps(node, node.attributes || []) var props = markProps(node, node.attributes || [])
var vnode = { var vnode = {
nodeName: type, nodeName: type,
dom: node, dom: node,
isVoidTag: !!voidTag[type], isVoidTag: !!voidTag[type],
props: props props: props
} }
if(type === 'option'){ if (type === 'option') {
//即便你设置了option.selected = true, //即便你设置了option.selected = true,
//option.attributes也找不到selected属性 //option.attributes也找不到selected属性
props.selected = node.selected props.selected = node.selected
} }
if (orphanTag[type] || type === 'option') { if (orphanTag[type] || type === 'option') {
makeOrphan(vnode, type, node.text || node.innerHTML) makeOrphan(vnode, type, node.text || node.innerHTML)
if (node.childNodes.length === 1) { if (node.childNodes.length === 1) {
vnode.children[0].dom = node.firstChild vnode.children[0].dom = node.firstChild
} }
} else if (!vnode.isVoidTag) { } else if (!vnode.isVoidTag) {
vnode.children = [] vnode.children = []
for (var i = 0, el; el = node.childNodes[i++];) { for (var i = 0, el; (el = node.childNodes[i++]); ) {
var child = from(el) var child = _from(el)
if (/\S/.test(child.nodeValue)) { if (/\S/.test(child.nodeValue)) {
vnode.children.push(child) vnode.children.push(child)
} }
} }
} }
return vnode return vnode
} }
} }
var rformElement = /input|textarea|select/i var rformElement = /input|textarea|select/i
function markProps(node, attrs) { function markProps(node, attrs) {
var ret = {} var ret = {}
for (var i = 0, n = attrs.length; i < n; i++) { for (var i = 0, n = attrs.length; i < n; i++) {
var attr = attrs[i] var attr = attrs[i]
if (attr.specified) { if (attr.specified) {
//IE6-9不会将属性名变小写,比如它会将用户的contenteditable变成contentEditable //IE6-9不会将属性名变小写,比如它会将用户的contenteditable变成contentEditable
ret[attr.name.toLowerCase()] = attr.value ret[attr.name.toLowerCase()] = attr.value
}
} }
if (rformElement.test(node.nodeName)) { }
ret.type = node.type if (rformElement.test(node.nodeName)) {
var a = node.getAttributeNode('value') ret.type = node.type
if (a && /\S/.test(a.value)) { //IE6,7中无法取得checkbox,radio的value var a = node.getAttributeNode('value')
ret.value = a.value if (a && /\S/.test(a.value)) {
} //IE6,7中无法取得checkbox,radio的value
ret.value = a.value
} }
var style = node.style.cssText }
if (style) { var style = node.style.cssText
ret.style = style if (style) {
} ret.style = style
//类名 = 去重(静态类名+动态类名+ hover类名? + active类名) }
if (ret.type === 'select-one') { //类名 = 去重(静态类名+动态类名+ hover类名? + active类名)
ret.selectedIndex = node.selectedIndex if (ret.type === 'select-one') {
} ret.selectedIndex = node.selectedIndex
return ret }
} return ret
}

View File

@ -1,8 +1,8 @@
export var orphanTag = { export var orphanTag = {
script: 1, script: 1,
style: 1, style: 1,
textarea: 1, textarea: 1,
xmp: 1, xmp: 1,
noscript: 1, noscript: 1,
template: 1 template: 1
} }

View File

@ -1,5 +1,29 @@
import { Anot, oneObject } from '../seed/core' import { Anot, oneObject } from '../seed/core'
var pNestChild = oneObject('div,ul,ol,dl,table,h1,h2,h3,h4,h5,h6,form,fieldset')
var tNestChild = makeObject('tr,style,script')
var nestObject = {
p: pNestChild,
select: makeObject('option,optgroup,#text'),
optgroup: makeObject('option,#text'),
option: makeObject('#text'),
tr: makeObject('th,td,style,script'),
tbody: tNestChild,
tfoot: tNestChild,
thead: tNestChild,
colgroup: makeObject('col'),
// table: oneObject('caption,colgroup,tbody,thead,tfoot,style,script,template,#document-fragment'),
head: makeObject(
'base,basefont,bgsound,link,style,script,meta,title,noscript,noframes'
),
html: oneObject('head,body')
}
function makeObject(str) {
return oneObject(str + ',template,#document-fragment,#comment')
}
export function validateDOMNesting(parent, child) { export function validateDOMNesting(parent, child) {
var parentTag = parent.nodeName var parentTag = parent.nodeName
var tag = child.nodeName var tag = child.nodeName
@ -8,16 +32,16 @@ export function validateDOMNesting(parent, child) {
if (parentTag === 'p') { if (parentTag === 'p') {
if (pNestChild[tag]) { if (pNestChild[tag]) {
console.warn( console.warn(
'P element can not add these childlren:\n' + Object.keys(pNestChild) 'P标签节点不允许以下类型的子节点:\n' + Object.keys(pNestChild)
) )
return false return false
} }
} else if (!parentChild[tag]) { } else if (!parentChild[tag]) {
console.warn( console.warn(
parentTag.toUpperCase() + parentTag.toUpperCase() +
'element only add these children:\n' + '标签节点只能增加以下子节点:\n' +
Object.keys(parentChild) + Object.keys(parentChild) +
'\nbut you add ' + '\n当前子节点为:' +
tag.toUpperCase() + tag.toUpperCase() +
' !!' ' !!'
) )
@ -26,38 +50,3 @@ export function validateDOMNesting(parent, child) {
} }
return true return true
} }
function makeObject(str) {
return oneObject(str + ',template,#document-fragment,#comment')
}
var pNestChild = oneObject('div,ul,ol,dl,table,h1,h2,h3,h4,h5,h6,form,fieldset')
var tNestChild = makeObject('tr,style,script')
var nestObject = {
p: pNestChild,
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
select: makeObject('option,optgroup,#text'),
optgroup: makeObject('option,#text'),
option: makeObject('#text'),
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
// No special behavior since these rules fall back to "in body" mode for
// all except special table nodes which cause bad parsing behavior anyway.
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr
tr: makeObject('th,td,style,script'),
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody
tbody: tNestChild,
tfoot: tNestChild,
thead: tNestChild,
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup
colgroup: makeObject('col'),
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable
// table: oneObject('caption,colgroup,tbody,thead,tfoot,style,script,template,#document-fragment'),
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead
head: makeObject(
'base,basefont,bgsound,link,style,script,meta,title,noscript,noframes'
),
// https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
html: oneObject('head,body')
}