ayakashi
Version:
The next generation web scraping framework
191 lines (190 loc) • 7.15 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.runQuery = void 0;
const operators_1 = require("./operators");
const handleStyleColors_1 = require("./handleStyleColors");
const VALID_OPERATORS = ["and", "or", "eq", "neq", "$neq", "like", "nlike", "$nlike", "in", "nin", "$nin"];
function runQuery(env, query, el) {
query.where = query.where || {};
const top = Object.keys(query.where)[0];
if (Array.isArray(query.where[top])) {
const reductionTree = {};
reductionTree[top] = [];
if (top !== "and" && top !== "or") {
throw new Error(`Invalid operator: ${top}. Learn more at https://ayakashi-io.github.io/docs/guide/querying-with-domql.html`);
}
query.where[top].forEach(member => {
const key = Object.keys(member)[0];
expandQuery(env, member, key, reductionTree, el, top);
});
return operators_1.operators[top](...reductionTree[top])();
}
else {
const attribute = Object.keys(query.where)[0];
const op = Object.keys(query.where[attribute])[0];
//@ts-ignore
let expected = query.where[attribute][op];
throwIfInvalidExpected(expected);
if (!operators_1.operators[op]) {
throw new Error(`Invalid operator: ${op}. Learn more at https://ayakashi-io.github.io/docs/guide/querying-with-domql.html`);
}
expected = formatExpected(attribute, expected);
//@ts-ignore
return operators_1.operators[op](createNodeQuery(env, el, attribute), expected)();
}
}
exports.runQuery = runQuery;
function createNodeQuery(env, el, attribute) {
return function () {
if ((attribute === "className" || attribute === "class" || attribute === "classList")) {
if (!el.classList || el.classList.length === 0) {
return null;
}
else {
return Array.from(el.classList).filter(name => !!name);
}
}
else if (attribute === "dataKey") {
if (!el.dataset || Object.keys(el.dataset).length === 0) {
return null;
}
else {
return Object.keys(el.dataset).filter(dataKey => !!dataKey);
}
}
else if (attribute === "dataValue") {
if (!el.dataset || Object.keys(el.dataset).length === 0) {
return null;
}
else {
return Object.keys(el.dataset).map(k => el.dataset[k]).filter(dataValue => !!dataValue);
}
}
else if (attribute.includes("data-")) {
if (!el.dataset || Object.keys(el.dataset).length === 0) {
return null;
}
else {
const dataAttr = formatDataAttribute(attribute);
return el.dataset[dataAttr] || null;
}
}
else if (attribute.includes("style-")) {
if (el.nodeName !== "#text") {
const styleProp = attribute.replace("style-", "");
const compStyles = env.getComputedStyle(el);
const val = compStyles.getPropertyValue(styleProp);
if (val) {
return val;
}
else {
return null;
}
}
else {
return null;
}
}
else {
try {
if (el[attribute]) {
return el[attribute];
}
else if (el.getAttribute(attribute)) {
return el.getAttribute(attribute);
}
else {
return null;
}
}
catch (_e) {
return null;
}
}
};
}
function expandQuery(env, member, key,
//@ts-ignore
reductionTree, el, top) {
if (Array.isArray(member[key])) {
const newTop = key;
if (newTop !== "and" && newTop !== "or") {
throw new Error(`Invalid operator: ${newTop}. Learn more at https://ayakashi-io.github.io/docs/guide/querying-with-domql.html`);
}
const container = { [key]: [] };
member[key].forEach(submember => {
const subkey = Object.keys(submember)[0];
//@ts-ignore
if (!reductionTree[top].includes(container)) {
reductionTree[top].push(container);
}
expandQuery(env, submember, subkey, reductionTree[top][reductionTree[top].length - 1], el, newTop);
});
}
else {
const expKey = Object.keys(member[key])[0];
//@ts-ignore
let expected = member[key][expKey];
throwIfInvalidExpected(expected);
expected = formatExpected(key, expected);
const op = Object.keys(member[key])[0];
if (VALID_OPERATORS.indexOf(op) === -1) {
throw new Error(`Invalid operator: ${op}. Learn more at https://ayakashi-io.github.io/docs/guide/querying-with-domql.html`);
}
reductionTree[top].push(operators_1.operators[op](createNodeQuery(env, el, key), expected));
}
}
function throwIfInvalidExpected(expected) {
if (!expected ||
(Array.isArray(expected) && (expected.length === 0 || expected.some(val => !val))) ||
(typeof expected === "object" && !Array.isArray(expected) && !(expected instanceof RegExp))) {
throw new Error(`Invalid expected value: ${Array.isArray(expected) ||
(typeof expected === "object" && expected !== null)
? JSON.stringify(expected) : String(expected) || "empty_string"}. Learn more at https://ayakashi-io.github.io/docs/guide/querying-with-domql.html`);
}
}
function formatExpected(attribute, expected) {
if (attribute.indexOf("style-") > -1 && attribute.indexOf("color") > -1) {
return handleStyleColors_1.handleStyleColors(expected);
}
else if (attribute === "tagName") {
if (typeof expected === "string") {
return expected.toUpperCase();
}
else if (Array.isArray(expected)) {
return expected.map(function (ex) {
if (typeof ex === "string") {
return ex.toUpperCase();
}
else {
return ex;
}
});
}
}
return expected;
}
function formatDataAttribute(dataAttr) {
const myDataAttr = dataAttr.replace("data-", "");
const formatted = [];
let upcased = false;
for (let i = 0; i < myDataAttr.length; i += 1) {
if (upcased) {
upcased = false;
continue;
}
if (myDataAttr[i] === "-" && (!myDataAttr[i + 1] || myDataAttr[i + 1] !== "-")) {
if (myDataAttr[i + 1]) {
formatted.push(myDataAttr[i + 1].toUpperCase());
}
else {
formatted.push("-");
}
upcased = true;
}
else {
formatted.push(myDataAttr[i]);
}
}
return formatted.join("");
}