mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-01-11 22:40:36 +00:00
410 lines
12 KiB
Dart
410 lines
12 KiB
Dart
import 'dart:convert';
|
|
import 'package:flutter_qjs/flutter_qjs.dart';
|
|
import 'package:html/dom.dart';
|
|
import 'package:html/parser.dart';
|
|
import 'package:mangayomi/utils/extensions/dom_extensions.dart';
|
|
|
|
class JsDomSelector {
|
|
late JavascriptRuntime runtime;
|
|
JsDomSelector(this.runtime);
|
|
final Map<int, Element?> _elements = {};
|
|
int _elementKey = 0;
|
|
void init() {
|
|
runtime.onMessage('get_doc_element', (dynamic args) {
|
|
final input = args[0];
|
|
final type = args[1];
|
|
final doc = parse(input);
|
|
final element = switch (type) {
|
|
'body' => doc.body,
|
|
'documentElement' => doc.documentElement,
|
|
'head' => doc.head,
|
|
_ => doc.parent,
|
|
};
|
|
_elementKey++;
|
|
_elements[_elementKey] = element;
|
|
return _elementKey;
|
|
});
|
|
runtime.onMessage('get_doc_string', (dynamic args) {
|
|
final input = args[0];
|
|
final type = args[1];
|
|
final doc = parse(input);
|
|
final res = switch (type) {
|
|
'text' => doc.text,
|
|
_ => doc.outerHtml,
|
|
};
|
|
return res ?? "";
|
|
});
|
|
runtime.onMessage('get_element_string', (dynamic args) {
|
|
final type = args[0];
|
|
final key = args[1];
|
|
final element = _elements[key];
|
|
final res = switch (type) {
|
|
'text' => element?.text,
|
|
'innerHtml' => element?.innerHtml,
|
|
'outerHtml' => element?.outerHtml,
|
|
'className' => element?.className,
|
|
'localName' => element?.localName,
|
|
'namespaceUri' => element?.namespaceUri,
|
|
'getSrc' => element?.getSrc,
|
|
'getImg' => element?.getImg,
|
|
'getHref' => element?.getHref,
|
|
_ => element?.getDataSrc,
|
|
};
|
|
return res ?? "";
|
|
});
|
|
runtime.onMessage('doc_select_first', (dynamic args) {
|
|
final input = args[0];
|
|
final selector = args[1];
|
|
_elementKey++;
|
|
_elements[_elementKey] = parse(input).selectFirst(selector);
|
|
return _elementKey;
|
|
});
|
|
runtime.onMessage('ele_selectFirst', (dynamic args) {
|
|
final selector = args[0];
|
|
final key = args[1];
|
|
_elementKey++;
|
|
_elements[_elementKey] = _elements[key]?.selectFirst(selector);
|
|
return _elementKey;
|
|
});
|
|
runtime.onMessage('ele_element_sibling', (dynamic args) {
|
|
final type = args[0];
|
|
final key = args[1];
|
|
final ele = _elements[key];
|
|
final element = switch (type) {
|
|
'nextElementSibling' => ele?.nextElementSibling,
|
|
_ => ele?.previousElementSibling,
|
|
};
|
|
_elementKey++;
|
|
_elements[_elementKey] = element;
|
|
return _elementKey;
|
|
});
|
|
runtime.onMessage('ele_attr', (dynamic args) {
|
|
final attr = args[0];
|
|
final key = args[1];
|
|
return _elements[key]?.attr(attr) ?? "";
|
|
});
|
|
runtime.onMessage('doc_attr', (dynamic args) {
|
|
final input = args[0];
|
|
final attr = args[1];
|
|
return parse(input).attr(attr) ?? "";
|
|
});
|
|
runtime.onMessage('ele_has_attr', (dynamic args) {
|
|
final attr = args[0];
|
|
final key = args[1];
|
|
return _elements[key]?.hasAtr(attr) ?? false;
|
|
});
|
|
runtime.onMessage('doc_has_attr', (dynamic args) {
|
|
final input = args[0];
|
|
final attr = args[1];
|
|
return parse(input).hasAtr(attr);
|
|
});
|
|
runtime.onMessage('doc_xpath_first', (dynamic args) {
|
|
final input = args[0];
|
|
final xpath = args[1];
|
|
return parse(input).xpathFirst(xpath) ?? "";
|
|
});
|
|
runtime.onMessage('ele_xpathFirst', (dynamic args) {
|
|
final xpath = args[0];
|
|
final key = args[1];
|
|
return _elements[key]?.xpathFirst(xpath) ?? "";
|
|
});
|
|
|
|
runtime.onMessage('doc_xpath', (dynamic args) {
|
|
final input = args[0];
|
|
final xpath = args[1];
|
|
return jsonEncode(parse(input).xpath(xpath));
|
|
});
|
|
runtime.onMessage('ele_xpath', (dynamic args) {
|
|
final xpath = args[0];
|
|
final key = args[1];
|
|
return jsonEncode(_elements[key]?.xpath(xpath));
|
|
});
|
|
runtime.onMessage('doc_get_elements_by', (dynamic args) {
|
|
final input = args[0];
|
|
final type = args[1];
|
|
final name = args[2];
|
|
final doc = parse(input);
|
|
final elements = switch (type) {
|
|
'children' => doc.children,
|
|
'getElementsByTagName' => doc.getElementsByTagName(name),
|
|
_ => doc.getElementsByClassName(name),
|
|
};
|
|
List<int> elementKeys = [];
|
|
for (var element in elements) {
|
|
_elementKey++;
|
|
_elements[_elementKey] = element;
|
|
elementKeys.add(_elementKey);
|
|
}
|
|
return jsonEncode(elementKeys);
|
|
});
|
|
runtime.onMessage('ele_get_elements_by', (dynamic args) {
|
|
final type = args[0];
|
|
final name = args[1];
|
|
final key = args[2];
|
|
final element = _elements[key];
|
|
final elements = switch (type) {
|
|
'children' => element?.children,
|
|
'getElementsByTagName' => element?.getElementsByTagName(name),
|
|
_ => element?.getElementsByClassName(name),
|
|
};
|
|
List<int> elementKeys = [];
|
|
for (var element in elements ?? []) {
|
|
_elementKey++;
|
|
_elements[_elementKey] = element;
|
|
elementKeys.add(_elementKey);
|
|
}
|
|
return jsonEncode(elementKeys);
|
|
});
|
|
runtime.onMessage('doc_get_element_by_id', (dynamic args) {
|
|
final input = args[0];
|
|
final id = args[1];
|
|
_elementKey++;
|
|
_elements[_elementKey] = parse(input).getElementById(id);
|
|
return _elementKey;
|
|
});
|
|
runtime.onMessage('doc_select', (dynamic args) {
|
|
final input = args[0];
|
|
final selector = args[1];
|
|
final elements = parse(input).select(selector);
|
|
List<int> elementKeys = [];
|
|
for (var element in elements ?? []) {
|
|
_elementKey++;
|
|
_elements[_elementKey] = element;
|
|
elementKeys.add(_elementKey);
|
|
}
|
|
return jsonEncode(elementKeys);
|
|
});
|
|
runtime.onMessage('ele_select', (dynamic args) {
|
|
final selector = args[0];
|
|
final key = args[1];
|
|
final elements = _elements[key]?.select(selector);
|
|
List<int> elementKeys = [];
|
|
for (var element in elements ?? []) {
|
|
_elementKey++;
|
|
_elements[_elementKey] = element;
|
|
elementKeys.add(_elementKey);
|
|
}
|
|
return jsonEncode(elementKeys);
|
|
});
|
|
|
|
runtime.evaluate('''
|
|
class Document {
|
|
constructor(html) {
|
|
this.html = html;
|
|
}
|
|
getElement(type) {
|
|
const key = sendMessage(
|
|
"get_doc_element",
|
|
JSON.stringify([this.html, type])
|
|
);
|
|
return new Element(key);
|
|
}
|
|
get body() {
|
|
return this.getElement('body');
|
|
}
|
|
get documentElement() {
|
|
return this.getElement('documentElement');
|
|
}
|
|
get head() {
|
|
return this.getElement('head');
|
|
}
|
|
get parent() {
|
|
return this.getElement('parent');
|
|
}
|
|
getString(type) {
|
|
return sendMessage(
|
|
"get_doc_string",
|
|
JSON.stringify([this.html, type]));
|
|
}
|
|
get text() {
|
|
return this.getString('text');
|
|
}
|
|
get outerHtml() {
|
|
return this.getString('outerHtml');
|
|
}
|
|
selectFirst(selector) {
|
|
const key = sendMessage(
|
|
"doc_select_first",
|
|
JSON.stringify([this.html, selector])
|
|
);
|
|
return new Element(key);
|
|
}
|
|
select(selector) {
|
|
let elements = [];
|
|
JSON.parse(
|
|
sendMessage("doc_select", JSON.stringify([this.html, selector]))
|
|
).forEach((key) => {
|
|
elements.push(new Element(key));
|
|
});
|
|
return elements;
|
|
}
|
|
xpathFirst(xpath) {
|
|
return sendMessage(
|
|
"doc_xpath_first",
|
|
JSON.stringify([this.html, xpath])
|
|
);
|
|
}
|
|
xpath(xpath) {
|
|
return JSON.parse(sendMessage(
|
|
"doc_xpath",
|
|
JSON.stringify([this.html, xpath]))
|
|
);
|
|
}
|
|
getElementsListBy(type, name) {
|
|
name = name || '';
|
|
let elements = [];
|
|
JSON.parse(sendMessage(
|
|
"doc_get_elements_by",
|
|
JSON.stringify([this.html, type, name]))
|
|
).forEach((key) => {
|
|
elements.push(new Element(key));
|
|
});
|
|
return elements;
|
|
}
|
|
get children() {
|
|
return this.getElementsListBy('children');
|
|
}
|
|
getElementsByTagName(name) {
|
|
return this.getElementsListBy('getElementsByTagName', name);
|
|
}
|
|
getElementsByClassName(name) {
|
|
return this.getElementsListBy('getElementsByClassName', name);
|
|
}
|
|
getElementById(id) {
|
|
const key = sendMessage(
|
|
"doc_get_element_by_id",
|
|
JSON.stringify([this.html, id])
|
|
);
|
|
return new Element(key);
|
|
}
|
|
attr(attr) {
|
|
return sendMessage(
|
|
"doc_attr",
|
|
JSON.stringify([this.html, attr])
|
|
);
|
|
}
|
|
hasAttr(attr) {
|
|
return sendMessage(
|
|
"doc_has_attr",
|
|
JSON.stringify([this.html, attr])
|
|
);
|
|
}
|
|
}
|
|
|
|
class Element {
|
|
constructor(key) {
|
|
this.key = key;
|
|
}
|
|
getString(type) {
|
|
return sendMessage(
|
|
"get_element_string",
|
|
JSON.stringify([type, this.key])
|
|
);
|
|
}
|
|
get text() {
|
|
return this.getString("text");
|
|
}
|
|
get outerHtml() {
|
|
return this.getString("outerHtml");
|
|
}
|
|
get innerHtml() {
|
|
return this.getString("innerHtml");
|
|
}
|
|
get className() {
|
|
return this.getString("className");
|
|
}
|
|
get localName() {
|
|
return this.getString("localName");
|
|
}
|
|
get namespaceUri() {
|
|
return this.getString("namespaceUri");
|
|
}
|
|
get getSrc() {
|
|
return this.getString("getSrc");
|
|
}
|
|
get getImg() {
|
|
return this.getString("getImg");
|
|
}
|
|
get getHref() {
|
|
return this.getString("getHref");
|
|
}
|
|
get getDataSrc() {
|
|
return this.getString("getDataSrc");
|
|
}
|
|
getElementSibling(type) {
|
|
const key = sendMessage(
|
|
"ele_element_sibling",
|
|
JSON.stringify([type, this.key])
|
|
);
|
|
return new Element(key);
|
|
}
|
|
get previousElementSibling() {
|
|
return this.getElementSibling("previousElementSibling");
|
|
}
|
|
get nextElementSibling() {
|
|
return this.getElementSibling("nextElementSibling");
|
|
}
|
|
getElementsListBy(type, name) {
|
|
name = name || '';
|
|
let elements = [];
|
|
JSON.parse(sendMessage(
|
|
"ele_get_elements_by",
|
|
JSON.stringify([type, name, this.key]))
|
|
).forEach((key) => {
|
|
elements.push(new Element(key));
|
|
});
|
|
return elements;
|
|
}
|
|
get children() {
|
|
return this.getElementsListBy('children');
|
|
}
|
|
getElementsByTagName(name) {
|
|
return this.getElementsListBy('getElementsByTagName', name);
|
|
}
|
|
getElementsByClassName(name) {
|
|
return this.getElementsListBy('getElementsByClassName', name);
|
|
}
|
|
xpath(xpath) {
|
|
return JSON.parse(sendMessage(
|
|
"xpath",
|
|
JSON.stringify([xpath, this.key]))
|
|
);
|
|
}
|
|
attr(attr) {
|
|
return sendMessage(
|
|
"ele_attr",
|
|
JSON.stringify([attr, this.key])
|
|
);
|
|
}
|
|
xpathFirst(xpath) {
|
|
return sendMessage(
|
|
"xpathFirst",
|
|
JSON.stringify([xpath, this.key])
|
|
);
|
|
}
|
|
selectFirst(selector) {
|
|
const key = sendMessage(
|
|
"ele_selectFirst",
|
|
JSON.stringify([selector, this.key])
|
|
);
|
|
return new Element(key);
|
|
}
|
|
select(selector) {
|
|
let elements = [];
|
|
JSON.parse(
|
|
sendMessage("ele_select", JSON.stringify([selector, this.key]))
|
|
).forEach((key) => {
|
|
elements.push(new Element(key));
|
|
});
|
|
return elements;
|
|
}
|
|
hasAttr(attr) {
|
|
return sendMessage(
|
|
"ele_has_attr",
|
|
JSON.stringify([this.html, attr])
|
|
);
|
|
}
|
|
}
|
|
''');
|
|
}
|
|
}
|