@mpxjs/core 2.9.38 → 2.9.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/core/proxy.js +48 -2
- package/src/dynamic/astCache.js +25 -0
- package/src/dynamic/dynamicRenderMixin.js +77 -0
- package/src/dynamic/vnode/context.js +17 -0
- package/src/dynamic/vnode/css-select/cssauron.js +445 -0
- package/src/dynamic/vnode/css-select/index.js +116 -0
- package/src/dynamic/vnode/css-select/through.js +19 -0
- package/src/dynamic/vnode/css-select/tokenizer.js +371 -0
- package/src/dynamic/vnode/interpreter.js +449 -0
- package/src/dynamic/vnode/render.js +289 -0
- package/src/index.js +2 -0
- package/src/platform/builtInMixins/index.js +8 -0
- package/src/platform/builtInMixins/proxyEventMixin.js +12 -3
- package/src/platform/builtInMixins/renderHelperMixin.js +2 -2
- package/src/platform/patch/ali/getDefaultOptions.js +20 -0
- package/src/platform/patch/wx/getDefaultOptions.js +20 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpxjs/core",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.39",
|
|
4
4
|
"description": "mpx runtime core",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"miniprogram",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"main": "src/index.js",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@mpxjs/utils": "^2.9.
|
|
22
|
+
"@mpxjs/utils": "^2.9.39",
|
|
23
23
|
"lodash": "^4.1.1",
|
|
24
24
|
"miniprogram-api-typings": "^3.10.0"
|
|
25
25
|
},
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"url": "https://github.com/didi/mpx/issues"
|
|
48
48
|
},
|
|
49
49
|
"sideEffects": false,
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "87957d360b18806f16d169612d924d383ac043c4"
|
|
51
51
|
}
|
package/src/core/proxy.js
CHANGED
|
@@ -44,6 +44,8 @@ import {
|
|
|
44
44
|
ONHIDE,
|
|
45
45
|
ONRESIZE
|
|
46
46
|
} from './innerLifecycle'
|
|
47
|
+
import contextMap from '../dynamic/vnode/context'
|
|
48
|
+
import { getAst } from '../dynamic/astCache'
|
|
47
49
|
|
|
48
50
|
let uid = 0
|
|
49
51
|
|
|
@@ -131,6 +133,8 @@ export default class MpxProxy {
|
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
created () {
|
|
136
|
+
// 缓存上下文,在 destoryed 阶段删除
|
|
137
|
+
contextMap.set(this.uid, this.target)
|
|
134
138
|
if (__mpx_mode__ !== 'web') {
|
|
135
139
|
// web中BEFORECREATE钩子通过vue的beforeCreate钩子单独驱动
|
|
136
140
|
this.callHook(BEFORECREATE)
|
|
@@ -190,6 +194,8 @@ export default class MpxProxy {
|
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
unmounted () {
|
|
197
|
+
// 页面/组件销毁清除上下文的缓存
|
|
198
|
+
contextMap.remove(this.uid)
|
|
193
199
|
this.callHook(BEFOREUNMOUNT)
|
|
194
200
|
this.scope?.stop()
|
|
195
201
|
if (this.update) this.update.active = false
|
|
@@ -379,7 +385,10 @@ export default class MpxProxy {
|
|
|
379
385
|
this.doRender(this.processRenderDataWithStrictDiff(renderData))
|
|
380
386
|
}
|
|
381
387
|
|
|
382
|
-
renderWithData (skipPre) {
|
|
388
|
+
renderWithData (skipPre, vnode) {
|
|
389
|
+
if (vnode) {
|
|
390
|
+
return this.doRenderWithVNode(vnode)
|
|
391
|
+
}
|
|
383
392
|
const renderData = skipPre ? this.renderData : preProcessRenderData(this.renderData)
|
|
384
393
|
this.doRender(this.processRenderDataWithStrictDiff(renderData))
|
|
385
394
|
// 重置renderData准备下次收集
|
|
@@ -478,6 +487,25 @@ export default class MpxProxy {
|
|
|
478
487
|
return result
|
|
479
488
|
}
|
|
480
489
|
|
|
490
|
+
doRenderWithVNode (vnode) {
|
|
491
|
+
if (!this._vnode) {
|
|
492
|
+
this.target.__render({ r: vnode })
|
|
493
|
+
} else {
|
|
494
|
+
let diffPath = diffAndCloneA(vnode, this._vnode).diffData
|
|
495
|
+
if (!isEmptyObject(diffPath)) {
|
|
496
|
+
// 构造 diffPath 数据
|
|
497
|
+
diffPath = Object.keys(diffPath).reduce((preVal, curVal) => {
|
|
498
|
+
const key = 'r' + curVal
|
|
499
|
+
preVal[key] = diffPath[curVal]
|
|
500
|
+
return preVal
|
|
501
|
+
}, {})
|
|
502
|
+
this.target.__render(diffPath)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// 缓存本地的 vnode 用以下一次 diff
|
|
506
|
+
this._vnode = diffAndCloneA(vnode).clone
|
|
507
|
+
}
|
|
508
|
+
|
|
481
509
|
doRender (data, cb) {
|
|
482
510
|
if (typeof this.target.__render !== 'function') {
|
|
483
511
|
error('Please specify a [__render] function to render view.', this.options.mpxFileResource)
|
|
@@ -545,12 +573,30 @@ export default class MpxProxy {
|
|
|
545
573
|
const _c = this.target._c.bind(this.target)
|
|
546
574
|
const _r = this.target._r.bind(this.target)
|
|
547
575
|
const _sc = this.target._sc.bind(this.target)
|
|
576
|
+
const _g = this.target._g?.bind(this.target)
|
|
577
|
+
const __getAst = this.target.__getAst?.bind(this.target)
|
|
578
|
+
const moduleId = this.target.__moduleId
|
|
579
|
+
const dynamicTarget = this.target.__dynamic
|
|
580
|
+
|
|
548
581
|
const effect = this.effect = new ReactiveEffect(() => {
|
|
549
582
|
// pre render for props update
|
|
550
583
|
if (this.propsUpdatedFlag) {
|
|
551
584
|
this.updatePreRender()
|
|
552
585
|
}
|
|
553
|
-
|
|
586
|
+
if (dynamicTarget || __getAst) {
|
|
587
|
+
try {
|
|
588
|
+
const ast = getAst(__getAst, moduleId)
|
|
589
|
+
return _r(false, _g(ast, moduleId))
|
|
590
|
+
} catch (e) {
|
|
591
|
+
e.errType = 'mpx-dynamic-render'
|
|
592
|
+
e.errmsg = e.message
|
|
593
|
+
if (!__mpx_dynamic_runtime__) {
|
|
594
|
+
return error('Please make sure you have set dynamicRuntime true in mpx webpack plugin config because you have use the dynamic runtime feature.', this.options.mpxFileResource, e)
|
|
595
|
+
} else {
|
|
596
|
+
return error('Dynamic rendering error', this.options.mpxFileResource, e)
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
554
600
|
if (this.target.__injectedRender) {
|
|
555
601
|
try {
|
|
556
602
|
return this.target.__injectedRender(_i, _c, _r, _sc)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isFunction, isObject, error } from '@mpxjs/utils'
|
|
2
|
+
|
|
3
|
+
class DynamicAstCache {
|
|
4
|
+
#astCache = {}
|
|
5
|
+
|
|
6
|
+
getAst (id) {
|
|
7
|
+
return this.#astCache[id]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
setAst (id, ast) {
|
|
11
|
+
this.#astCache[id] = ast
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const dynamic = new DynamicAstCache()
|
|
16
|
+
|
|
17
|
+
export const getAst = (__getAst, moduleId) => {
|
|
18
|
+
if ((__getAst && isFunction(__getAst))) {
|
|
19
|
+
const ast = __getAst()
|
|
20
|
+
if (!isObject(ast)) return error('__getAst returned data is not of type object')
|
|
21
|
+
return Object.values(ast)[0]
|
|
22
|
+
} else {
|
|
23
|
+
return dynamic.getAst(moduleId)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { hasOwn, isObject, error } from '@mpxjs/utils'
|
|
2
|
+
import genVnodeTree from './vnode/render'
|
|
3
|
+
import contextMap from './vnode/context'
|
|
4
|
+
import { CREATED } from '../core/innerLifecycle'
|
|
5
|
+
|
|
6
|
+
function dynamicRefsMixin () {
|
|
7
|
+
return {
|
|
8
|
+
[CREATED] () {
|
|
9
|
+
// 处理ref场景,如果是在容器组件的上下文渲染
|
|
10
|
+
if (this.mpxCustomElement) {
|
|
11
|
+
this._getRuntimeRefs()
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
methods: {
|
|
15
|
+
_getRuntimeRefs () {
|
|
16
|
+
const vnodeContext = contextMap.get(this.uid)
|
|
17
|
+
if (vnodeContext) {
|
|
18
|
+
const refsArr = vnodeContext.__getRefsData && vnodeContext.__getRefsData()
|
|
19
|
+
if (Array.isArray(refsArr)) {
|
|
20
|
+
refsArr.forEach((ref) => {
|
|
21
|
+
const all = ref.all
|
|
22
|
+
if (!vnodeContext.$refs[ref.key] || (all && !vnodeContext.$refs[ref.key].length)) {
|
|
23
|
+
const refNode = this.__getRefNode(ref)
|
|
24
|
+
if ((all && refNode.length) || refNode) {
|
|
25
|
+
Object.defineProperty(vnodeContext.$refs, ref.key, {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
get: () => {
|
|
29
|
+
return refNode
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function dynamicSlotMixin () {
|
|
43
|
+
if (__mpx_mode__ === 'ali') {
|
|
44
|
+
return {
|
|
45
|
+
props: { slots: {} }
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
return {
|
|
49
|
+
properties: { slots: { type: Object } }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function dynamicRenderHelperMixin () {
|
|
55
|
+
return {
|
|
56
|
+
methods: {
|
|
57
|
+
_g (astData, moduleId) {
|
|
58
|
+
const location = this.__mpxProxy && this.__mpxProxy.options.mpxFileResource
|
|
59
|
+
if (astData && isObject(astData) && hasOwn(astData, 'template')) {
|
|
60
|
+
const vnodeTree = genVnodeTree(astData, [this], { moduleId, location })
|
|
61
|
+
return vnodeTree
|
|
62
|
+
} else {
|
|
63
|
+
error('Dynamic component get the wrong json ast data, please check.', location, {
|
|
64
|
+
errType: 'mpx-dynamic-render',
|
|
65
|
+
errmsg: 'invalid json ast data'
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
dynamicRefsMixin,
|
|
75
|
+
dynamicSlotMixin,
|
|
76
|
+
dynamicRenderHelperMixin
|
|
77
|
+
}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import tokenizer from './tokenizer'
|
|
2
|
+
|
|
3
|
+
export default function language (lookups, matchComparison) {
|
|
4
|
+
return function (selector, moduleId) {
|
|
5
|
+
return parse(
|
|
6
|
+
selector,
|
|
7
|
+
remap(lookups),
|
|
8
|
+
moduleId,
|
|
9
|
+
matchComparison || caseSensitiveComparison
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function remap (opts) {
|
|
15
|
+
// 对于字符串类型的 value 转化为函数
|
|
16
|
+
for (const key in opts) {
|
|
17
|
+
if (opt_okay(opts, key)) {
|
|
18
|
+
/* eslint-disable-next-line */
|
|
19
|
+
opts[key] = Function(
|
|
20
|
+
'return function(node, attr) { return node.' + opts[key] + ' }'
|
|
21
|
+
)
|
|
22
|
+
opts[key] = opts[key]()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return opts
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function opt_okay (opts, key) {
|
|
30
|
+
return Object.prototype.hasOwnProperty.call(opts, key) && typeof opts[key] === 'string'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parse (selector, options, moduleId, matchComparison) {
|
|
34
|
+
const stream = tokenizer()
|
|
35
|
+
// const default_subj = true
|
|
36
|
+
const selectors = [[]]
|
|
37
|
+
let bits = selectors[0]
|
|
38
|
+
|
|
39
|
+
// 逆向关系
|
|
40
|
+
const traversal = {
|
|
41
|
+
'': any_parents,
|
|
42
|
+
'>': direct_parent,
|
|
43
|
+
'+': direct_sibling,
|
|
44
|
+
'~': any_sibling
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
stream.on('data', group)
|
|
48
|
+
stream.end(selector)
|
|
49
|
+
|
|
50
|
+
function group (token) {
|
|
51
|
+
if (token.type === 'comma') {
|
|
52
|
+
selectors.unshift((bits = []))
|
|
53
|
+
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 获取节点之间的关系路径,匹配的规则从右往左依次进行,因此在后面的匹配规则需要存储在栈结构的前面
|
|
58
|
+
if (token.type === 'op' || token.type === 'any-child') {
|
|
59
|
+
bits.unshift(traversal[token.data]) // 获取节点之间关系的操作数
|
|
60
|
+
bits.unshift(check()) // 添加空的匹配操作数
|
|
61
|
+
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
bits[0] = bits[0] || check()
|
|
66
|
+
const crnt = bits[0]
|
|
67
|
+
|
|
68
|
+
if (token.type === '!') {
|
|
69
|
+
crnt.subject = selectors[0].subject = true
|
|
70
|
+
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
crnt.push(
|
|
75
|
+
token.type === 'class'
|
|
76
|
+
? listContains(token.type, token.data)
|
|
77
|
+
: token.type === 'attr'
|
|
78
|
+
? attr(token)
|
|
79
|
+
: token.type === ':' || token.type === '::'
|
|
80
|
+
? pseudo(token)
|
|
81
|
+
: token.type === '*'
|
|
82
|
+
? Boolean
|
|
83
|
+
: matches(token.type, token.data, matchComparison)
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return selector_fn
|
|
88
|
+
|
|
89
|
+
// 单节点对比
|
|
90
|
+
function selector_fn (node, as_boolean) {
|
|
91
|
+
if (node.data?.moduleId !== moduleId) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
let current, length, subj
|
|
95
|
+
|
|
96
|
+
const orig = node
|
|
97
|
+
const set = []
|
|
98
|
+
|
|
99
|
+
for (let i = 0, len = selectors.length; i < len; ++i) {
|
|
100
|
+
bits = selectors[i]
|
|
101
|
+
current = entry // 当前 bits 检测规则
|
|
102
|
+
length = bits.length
|
|
103
|
+
node = orig // 引用赋值
|
|
104
|
+
subj = []
|
|
105
|
+
|
|
106
|
+
let j = 0
|
|
107
|
+
// 步长为2,因为2个节点之间的关系中间会有一个 OP 操作符
|
|
108
|
+
for (j = 0; j < length; j += 2) {
|
|
109
|
+
node = current(node, bits[j], subj)
|
|
110
|
+
|
|
111
|
+
if (!node) {
|
|
112
|
+
break
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// todo 这里的规则和步长设计的很巧妙
|
|
116
|
+
current = bits[j + 1] // 改变当前的 bits 检测规则
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (j >= length) {
|
|
120
|
+
if (as_boolean) {
|
|
121
|
+
return true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
add(!bits.subject ? [orig] : subj)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (as_boolean) {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return !set.length ? false : set.length === 1 ? set[0] : set
|
|
133
|
+
|
|
134
|
+
function add (items) {
|
|
135
|
+
let next
|
|
136
|
+
|
|
137
|
+
while (items.length) {
|
|
138
|
+
next = items.shift()
|
|
139
|
+
|
|
140
|
+
if (set.indexOf(next) === -1) {
|
|
141
|
+
set.push(next)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 匹配操作数
|
|
148
|
+
function check () {
|
|
149
|
+
_check.bits = []
|
|
150
|
+
_check.subject = false
|
|
151
|
+
_check.push = function (token) {
|
|
152
|
+
_check.bits.push(token)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return _check
|
|
156
|
+
|
|
157
|
+
function _check (node, subj) {
|
|
158
|
+
for (let i = 0, len = _check.bits.length; i < len; ++i) {
|
|
159
|
+
if (!_check.bits[i](node)) {
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (_check.subject) {
|
|
165
|
+
subj.push(node)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function listContains (type, data) {
|
|
173
|
+
return function (node) {
|
|
174
|
+
let val = options[type](node)
|
|
175
|
+
val = Array.isArray(val) ? val : val ? val.toString().split(/\s+/) : []
|
|
176
|
+
return val.indexOf(data) >= 0
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function attr (token) {
|
|
181
|
+
return token.data.lhs
|
|
182
|
+
? valid_attr(options.attr, token.data.lhs, token.data.cmp, token.data.rhs)
|
|
183
|
+
: valid_attr(options.attr, token.data)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function matches (type, data, matchComparison) {
|
|
187
|
+
return function (node) {
|
|
188
|
+
return matchComparison(type, options[type](node), data)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function any_parents (node, next, subj) {
|
|
193
|
+
do {
|
|
194
|
+
node = options.parent(node)
|
|
195
|
+
} while (node && !next(node, subj))
|
|
196
|
+
|
|
197
|
+
return node
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function direct_parent (node, next, subj) {
|
|
201
|
+
node = options.parent(node)
|
|
202
|
+
|
|
203
|
+
return node && next(node, subj) ? node : null
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function direct_sibling (node, next, subj) {
|
|
207
|
+
const parent = options.parent(node)
|
|
208
|
+
let idx = 0
|
|
209
|
+
|
|
210
|
+
const children = options.children(parent)
|
|
211
|
+
|
|
212
|
+
for (let i = 0, len = children.length; i < len; ++i) {
|
|
213
|
+
if (children[i] === node) {
|
|
214
|
+
idx = i
|
|
215
|
+
|
|
216
|
+
break
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return children[idx - 1] && next(children[idx - 1], subj)
|
|
221
|
+
? children[idx - 1]
|
|
222
|
+
: null
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function any_sibling (node, next, subj) {
|
|
226
|
+
const parent = options.parent(node)
|
|
227
|
+
|
|
228
|
+
const children = options.children(parent)
|
|
229
|
+
|
|
230
|
+
for (let i = 0, len = children.length; i < len; ++i) {
|
|
231
|
+
if (children[i] === node) {
|
|
232
|
+
return null
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (next(children[i], subj)) {
|
|
236
|
+
return children[i]
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return null
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function pseudo (token) {
|
|
244
|
+
return valid_pseudo(options, token.data, matchComparison)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function entry (node, next, subj) {
|
|
249
|
+
return next(node, subj) ? node : null
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function valid_pseudo (options, match, matchComparison) {
|
|
253
|
+
switch (match) {
|
|
254
|
+
case 'empty':
|
|
255
|
+
return valid_empty(options)
|
|
256
|
+
case 'first-child':
|
|
257
|
+
return valid_first_child(options)
|
|
258
|
+
case 'last-child':
|
|
259
|
+
return valid_last_child(options)
|
|
260
|
+
case 'root':
|
|
261
|
+
return valid_root(options)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (match.indexOf('contains') === 0) {
|
|
265
|
+
return valid_contains(options, match.slice(9, -1))
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (match.indexOf('any') === 0) {
|
|
269
|
+
return valid_any_match(options, match.slice(4, -1), matchComparison)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (match.indexOf('not') === 0) {
|
|
273
|
+
return valid_not_match(options, match.slice(4, -1), matchComparison)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (match.indexOf('nth-child') === 0) {
|
|
277
|
+
return valid_nth_child(options, match.slice(10, -1))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return function () {
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function valid_not_match (options, selector, matchComparison) {
|
|
286
|
+
const fn = parse(selector, options, matchComparison)
|
|
287
|
+
|
|
288
|
+
return not_function
|
|
289
|
+
|
|
290
|
+
function not_function (node) {
|
|
291
|
+
return !fn(node, true)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function valid_any_match (options, selector, matchComparison) {
|
|
296
|
+
const fn = parse(selector, options, matchComparison)
|
|
297
|
+
|
|
298
|
+
return fn
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function valid_attr (fn, lhs, cmp, rhs) {
|
|
302
|
+
return function (node) {
|
|
303
|
+
const attr = fn(node, lhs)
|
|
304
|
+
|
|
305
|
+
if (!cmp) {
|
|
306
|
+
return !!attr
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (cmp.length === 1) {
|
|
310
|
+
return attr === rhs
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (attr === undefined || attr === null) {
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return checkattr[cmp.charAt(0)](attr, rhs)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function valid_first_child (options) {
|
|
322
|
+
return function (node) {
|
|
323
|
+
return options.children(options.parent(node))[0] === node
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function valid_last_child (options) {
|
|
328
|
+
return function (node) {
|
|
329
|
+
const children = options.children(options.parent(node))
|
|
330
|
+
|
|
331
|
+
return children[children.length - 1] === node
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function valid_empty (options) {
|
|
336
|
+
return function (node) {
|
|
337
|
+
return options.children(node).length === 0
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function valid_root (options) {
|
|
342
|
+
return function (node) {
|
|
343
|
+
return !options.parent(node)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function valid_contains (options, contents) {
|
|
348
|
+
return function (node) {
|
|
349
|
+
return options.contents(node).indexOf(contents) !== -1
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function valid_nth_child (options, nth) {
|
|
354
|
+
let test = function () {
|
|
355
|
+
return false
|
|
356
|
+
}
|
|
357
|
+
if (nth === 'odd') {
|
|
358
|
+
nth = '2n+1'
|
|
359
|
+
} else if (nth === 'even') {
|
|
360
|
+
nth = '2n'
|
|
361
|
+
}
|
|
362
|
+
const regexp = /( ?([-|+])?(\d*)n)? ?((\+|-)? ?(\d+))? ?/
|
|
363
|
+
const matches = nth.match(regexp)
|
|
364
|
+
if (matches) {
|
|
365
|
+
let growth = 0
|
|
366
|
+
if (matches[1]) {
|
|
367
|
+
const positiveGrowth = matches[2] !== '-'
|
|
368
|
+
growth = parseInt(matches[3] === '' ? 1 : matches[3])
|
|
369
|
+
growth = growth * (positiveGrowth ? 1 : -1)
|
|
370
|
+
}
|
|
371
|
+
let offset = 0
|
|
372
|
+
if (matches[4]) {
|
|
373
|
+
offset = parseInt(matches[6])
|
|
374
|
+
const positiveOffset = matches[5] !== '-'
|
|
375
|
+
offset = offset * (positiveOffset ? 1 : -1)
|
|
376
|
+
}
|
|
377
|
+
if (growth === 0) {
|
|
378
|
+
if (offset !== 0) {
|
|
379
|
+
test = function (children, node) {
|
|
380
|
+
return children[offset - 1] === node
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
test = function (children, node) {
|
|
385
|
+
const validPositions = []
|
|
386
|
+
const len = children.length
|
|
387
|
+
for (let position = 1; position <= len; position++) {
|
|
388
|
+
const divisible = (position - offset) % growth === 0
|
|
389
|
+
if (divisible) {
|
|
390
|
+
if (growth > 0) {
|
|
391
|
+
validPositions.push(position)
|
|
392
|
+
} else {
|
|
393
|
+
if ((position - offset) / growth >= 0) {
|
|
394
|
+
validPositions.push(position)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
for (let i = 0; i < validPositions.length; i++) {
|
|
400
|
+
if (children[validPositions[i] - 1] === node) {
|
|
401
|
+
return true
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return false
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return function (node) {
|
|
409
|
+
const children = options.children(options.parent(node))
|
|
410
|
+
|
|
411
|
+
return test(children, node)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const checkattr = {
|
|
416
|
+
$: check_end,
|
|
417
|
+
'^': check_beg,
|
|
418
|
+
'*': check_any,
|
|
419
|
+
'~': check_spc,
|
|
420
|
+
'|': check_dsh
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function check_end (l, r) {
|
|
424
|
+
return l.slice(l.length - r.length) === r
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function check_beg (l, r) {
|
|
428
|
+
return l.slice(0, r.length) === r
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function check_any (l, r) {
|
|
432
|
+
return l.indexOf(r) > -1
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function check_spc (l, r) {
|
|
436
|
+
return l.split(/\s+/).indexOf(r) > -1
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function check_dsh (l, r) {
|
|
440
|
+
return l.split('-').indexOf(r) > -1
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function caseSensitiveComparison (type, pattern, data) {
|
|
444
|
+
return pattern === data
|
|
445
|
+
}
|