-
Notifications
You must be signed in to change notification settings - Fork 4
/
index.js
133 lines (120 loc) · 3.41 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
* Internal dependencies
*/
import getPath from './get-path';
/**
* Given a markup string or DOM element, creates an object aligning with the
* shape of the matchers object, or the value returned by the matcher.
*
* @param {(string|Element)} source Source content
* @param {(Object|Function)} matchers Matcher function or object of matchers
* @return {(Object|*)} Matched value(s), shaped by object
*/
export function parse( source, matchers ) {
if ( ! matchers ) {
return;
}
// Coerce to element
if ( 'string' === typeof source ) {
const doc = document.implementation.createHTMLDocument( '' );
doc.body.innerHTML = source;
source = doc.body.firstChild;
}
// Return singular value
if ( 'function' === typeof matchers ) {
return matchers( source );
}
// Bail if we can't handle matchers
if ( Object !== matchers.constructor ) {
return;
}
// Shape result by matcher object
return Object.keys( matchers ).reduce( ( memo, key ) => {
memo[ key ] = parse( source, matchers[ key ] );
return memo;
}, {} );
}
/**
* Generates a function which matches node of type selector, returning an
* attribute by property if the attribute exists. If no selector is passed,
* returns property of the query element.
*
* @param {?string} selector Optional selector
* @param {string} name Property name
* @return {*} Property value
*/
export function prop( selector, name ) {
if ( 1 === arguments.length ) {
name = selector;
selector = undefined;
}
return function( node ) {
let match = node;
if ( selector ) {
match = node.querySelector( selector );
}
if ( match ) {
return getPath( match, name );
}
};
}
/**
* Generates a function which matches node of type selector, returning an
* attribute by name if the attribute exists. If no selector is passed,
* returns attribute of the query element.
*
* @param {?string} selector Optional selector
* @param {string} name Attribute name
* @return {?string} Attribute value
*/
export function attr( selector, name ) {
if ( 1 === arguments.length ) {
name = selector;
selector = undefined;
}
return function( node ) {
const attributes = prop( selector, 'attributes' )( node );
if ( attributes && attributes.hasOwnProperty( name ) ) {
return attributes[ name ].value;
}
};
}
/**
* Convenience for `prop( selector, 'innerHTML' )`.
*
* @see prop()
*
* @param {?string} selector Optional selector
* @return {string} Inner HTML
*/
export function html( selector ) {
return prop( selector, 'innerHTML' );
}
/**
* Convenience for `prop( selector, 'textContent' )`.
*
* @see prop()
*
* @param {?string} selector Optional selector
* @return {string} Text content
*/
export function text( selector ) {
return prop( selector, 'textContent' );
}
/**
* Creates a new matching context by first finding elements matching selector
* using querySelectorAll before then running another `parse` on `matchers`
* scoped to the matched elements.
*
* @see parse()
*
* @param {string} selector Selector to match
* @param {(Object|Function)} matchers Matcher function or object of matchers
* @return {Array.<*,Object>} Array of matched value(s)
*/
export function query( selector, matchers ) {
return function( node ) {
const matches = node.querySelectorAll( selector );
return [].map.call( matches, ( match ) => parse( match, matchers ) );
};
}