mirror of
https://github.com/danbulant/node-html-parser
synced 2026-05-20 12:58:52 +00:00
240 lines
6.3 KiB
TypeScript
240 lines
6.3 KiB
TypeScript
import HTMLElement from './nodes/html';
|
|
|
|
interface MatherFunction {
|
|
func(el: HTMLElement, tagName: string, classes: string[] | string, attr_key: string, value: string): boolean;
|
|
tagName: string;
|
|
classes: string | string[];
|
|
attr_key: string;
|
|
value: string;
|
|
}
|
|
|
|
/**
|
|
* Cache to store generated match functions
|
|
* @type {Object}
|
|
*/
|
|
let pMatchFunctionCache = {} as { [name: string]: MatherFunction };
|
|
|
|
/**
|
|
* Function cache
|
|
*/
|
|
const functionCache = {
|
|
f145(el: HTMLElement, tagName: string, classes: string[]) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
classes = classes || [];
|
|
if (el.id !== tagName.substr(1)) {
|
|
return false;
|
|
}
|
|
for (let cls = classes, i = 0; i < cls.length; i++) {
|
|
if (el.classNames.indexOf(cls[i]) === -1) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
f45(el: HTMLElement, tagName: string, classes: string[]) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
classes = classes || [];
|
|
for (let cls = classes, i = 0; i < cls.length; i++) {
|
|
if (el.classNames.indexOf(cls[i]) === -1) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
f15(el: HTMLElement, tagName: string) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
if (el.id !== tagName.substr(1)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
f1(el: HTMLElement, tagName: string) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
if (el.id !== tagName.substr(1)) {
|
|
return false;
|
|
}
|
|
},
|
|
f5() {
|
|
'use strict';
|
|
return true;
|
|
},
|
|
f245(el: HTMLElement, tagName: string, classes: string[], attr_key: string, value: string) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
classes = classes || [];
|
|
attr_key = attr_key || '';
|
|
value = value || '';
|
|
const attrs = el.attributes;
|
|
return Object.keys(attrs).some((key) => {
|
|
const val = attrs[key];
|
|
return key === attr_key && val === value
|
|
});
|
|
// for (let cls = classes, i = 0; i < cls.length; i++) {if (el.classNames.indexOf(cls[i]) === -1){ return false;}}
|
|
// return true;
|
|
},
|
|
f25(el: HTMLElement, tagName: string, classes: string[], attr_key: string, value: string) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
classes = classes || [];
|
|
attr_key = attr_key || '';
|
|
value = value || '';
|
|
const attrs = el.attributes;
|
|
return Object.keys(attrs).some((key) => {
|
|
const val = attrs[key];
|
|
return key === attr_key && val === value
|
|
});
|
|
// return true;
|
|
},
|
|
f2(el: HTMLElement, tagName: string, classes: string[], attr_key: string, value: string) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
classes = classes || [];
|
|
attr_key = attr_key || '';
|
|
value = value || '';
|
|
const attrs = el.attributes;
|
|
return Object.keys(attrs).some((key) => {
|
|
const val = attrs[key];
|
|
return key === attr_key && val === value
|
|
});
|
|
},
|
|
f345(el: HTMLElement, tagName: string, classes: string[]) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
classes = classes || [];
|
|
if (el.tagName !== tagName) {
|
|
return false;
|
|
}
|
|
for (let cls = classes, i = 0; i < cls.length; i++) {
|
|
if (el.classNames.indexOf(cls[i]) === -1) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
f35(el: HTMLElement, tagName: string) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
return el.tagName === tagName;
|
|
},
|
|
f3(el: HTMLElement, tagName: string) {
|
|
'use strict';
|
|
tagName = tagName || '';
|
|
if (el.tagName !== tagName) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Matcher class to make CSS match
|
|
*
|
|
* @class Matcher
|
|
*/
|
|
export default class Matcher {
|
|
private matchers: MatherFunction[];
|
|
private nextMatch = 0;
|
|
/**
|
|
* Creates an instance of Matcher.
|
|
* @param {string} selector
|
|
*
|
|
* @memberof Matcher
|
|
*/
|
|
constructor(selector: string) {
|
|
functionCache.f5 = functionCache.f5;
|
|
this.matchers = selector.split(' ').map((matcher) => {
|
|
if (pMatchFunctionCache[matcher])
|
|
return pMatchFunctionCache[matcher];
|
|
const parts = matcher.split('.');
|
|
const tagName = parts[0];
|
|
const classes = parts.slice(1).sort();
|
|
// let source = '"use strict";';
|
|
let function_name = 'f';
|
|
let attr_key = '';
|
|
let value = '';
|
|
if (tagName && tagName !== '*') {
|
|
let reg: RegExpMatchArray;
|
|
if (tagName.startsWith('#')) {
|
|
// source += 'if (el.id != ' + JSON.stringify(tagName.substr(1)) + ') return false;';// 1
|
|
function_name += '1';
|
|
} else {
|
|
reg = /^\[\s*(\S+)\s*(=|!=)\s*((((["'])([^\6]*)\6))|(\S*?))\]\s*/.exec(tagName);
|
|
if (reg) {
|
|
attr_key = reg[1];
|
|
let method = reg[2];
|
|
if (method !== '=' && method !== '!=') {
|
|
throw new Error('Selector not supported, Expect [key${op}value].op must be =,!=');
|
|
}
|
|
if (method === '=') {
|
|
method = '==';
|
|
}
|
|
value = reg[7] || reg[8];
|
|
|
|
// source += `let attrs = el.attributes;for (let key in attrs){const val = attrs[key]; if (key == "${attr_key}" && val == "${value}"){return true;}} return false;`;// 2
|
|
function_name += '2';
|
|
} else {
|
|
// source += 'if (el.tagName != ' + JSON.stringify(tagName) + ') return false;';// 3
|
|
function_name += '3';
|
|
}
|
|
}
|
|
}
|
|
if (classes.length > 0) {
|
|
// source += 'for (let cls = ' + JSON.stringify(classes) + ', i = 0; i < cls.length; i++) if (el.classNames.indexOf(cls[i]) === -1) return false;';// 4
|
|
function_name += '4';
|
|
}
|
|
// source += 'return true;';// 5
|
|
function_name += '5';
|
|
const obj = {
|
|
func: functionCache[function_name],
|
|
tagName: tagName || '',
|
|
classes: classes || '',
|
|
attr_key: attr_key || '',
|
|
value: value || ''
|
|
}
|
|
// source = source || '';
|
|
return pMatchFunctionCache[matcher] = obj as MatherFunction;
|
|
});
|
|
}
|
|
/**
|
|
* Trying to advance match pointer
|
|
* @param {HTMLElement} el element to make the match
|
|
* @return {bool} true when pointer advanced.
|
|
*/
|
|
advance(el: HTMLElement) {
|
|
if (this.nextMatch < this.matchers.length &&
|
|
this.matchers[this.nextMatch].func(el, this.matchers[this.nextMatch].tagName, this.matchers[this.nextMatch].classes, this.matchers[this.nextMatch].attr_key, this.matchers[this.nextMatch].value)) {
|
|
this.nextMatch++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Rewind the match pointer
|
|
*/
|
|
rewind() {
|
|
this.nextMatch--;
|
|
}
|
|
/**
|
|
* Trying to determine if match made.
|
|
* @return {bool} true when the match is made
|
|
*/
|
|
get matched() {
|
|
return this.nextMatch === this.matchers.length;
|
|
}
|
|
/**
|
|
* Rest match pointer.
|
|
* @return {[type]} [description]
|
|
*/
|
|
reset() {
|
|
this.nextMatch = 0;
|
|
}
|
|
/**
|
|
* flush cache to free memory
|
|
*/
|
|
flushCache() {
|
|
pMatchFunctionCache = {};
|
|
}
|
|
}
|