page-parser-tree
Version:
Library to find elements in a dynamic web page
347 lines (286 loc) • 11.2 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _liveSet = _interopRequireDefault(require("live-set"));
var _merge = _interopRequireDefault(require("live-set/merge"));
var _flatMapR = _interopRequireDefault(require("live-set/flatMapR"));
var _Scheduler = _interopRequireDefault(require("live-set/Scheduler"));
var _tagTree = require("tag-tree");
var _watcherFinderMerger = _interopRequireDefault(require("./watcherFinderMerger"));
var _createTransformer = _interopRequireDefault(require("./createTransformer"));
function makeTagOptions(options) {
var map = new Map();
var list = [];
Object.keys(options.tags).forEach(function (tag) {
var tagOptions = options.tags[tag];
var ownedBy = tagOptions.ownedBy;
list.push({
tag: tag,
ownedBy: ownedBy
});
map.set(tag, tagOptions);
});
Object.keys(options.finders).concat(options.watchers.map(function (w) {
return w.tag;
})).forEach(function (tag) {
if (!map.has(tag)) {
map.set(tag, {
ownedBy: []
});
list.push({
tag: tag
});
}
});
return {
map: map,
list: list
};
}
var PageParserTree =
/*#__PURE__*/
function () {
function PageParserTree(root, options) {
var _this = this;
(0, _classCallCheck2["default"])(this, PageParserTree);
(0, _defineProperty2["default"])(this, "tree", void 0);
(0, _defineProperty2["default"])(this, "_scheduler", new _Scheduler["default"]());
(0, _defineProperty2["default"])(this, "_treeController", void 0);
(0, _defineProperty2["default"])(this, "_rootMatchedSet", void 0);
(0, _defineProperty2["default"])(this, "_ecSources", void 0);
(0, _defineProperty2["default"])(this, "_logError", void 0);
(0, _defineProperty2["default"])(this, "_options", void 0);
(0, _defineProperty2["default"])(this, "_tagOptions", void 0);
(0, _defineProperty2["default"])(this, "_tagsList", void 0);
(0, _defineProperty2["default"])(this, "_subscriptions", []);
var rootEl;
if (root.nodeType === Node.DOCUMENT_NODE) {
rootEl = root.documentElement;
if (!rootEl) throw new Error('missing documentElement');
} else {
rootEl = root;
}
this._options = options;
this._logError = options.logError || function (err) {
setTimeout(function () {
throw err;
}, 0);
};
var _makeTagOptions = makeTagOptions(this._options),
tagOptionsMap = _makeTagOptions.map,
tags = _makeTagOptions.list;
this._tagOptions = tagOptionsMap;
this._tagsList = tags;
this.tree = new _tagTree.TagTree({
root: rootEl,
tags: tags,
executor: function executor(controller) {
_this._treeController = controller;
}
});
this._rootMatchedSet = _liveSet["default"].constant(new Set([{
el: this.tree.getValue(),
parents: [{
tag: null,
node: this.tree
}]
}]), {
scheduler: this._scheduler
});
this._setupWatchersAndFinders();
}
(0, _createClass2["default"])(PageParserTree, [{
key: "_setupWatchersAndFinders",
value: function _setupWatchersAndFinders() {
var _this2 = this;
var tagsWithWatchers = new Set();
this._options.watchers.forEach(function (watcher) {
tagsWithWatchers.add(watcher.tag);
});
this._ecSources = new Map(this._tagsList.map(function (_ref) {
var tag = _ref.tag;
var tagOptions = _this2._tagOptions.get(tag);
if (!tagOptions) throw new Error();
var ownedBy = new Set(tagOptions.ownedBy || []);
var _LiveSet$active = _liveSet["default"].active(null, {
scheduler: _this2._scheduler
}),
liveSet = _LiveSet$active.liveSet,
controller = _LiveSet$active.controller;
var combinedWatcherSet = tagsWithWatchers.has(tag) ? (0, _flatMapR["default"])(liveSet, function (s) {
return s;
}) : null;
var finder = _this2._options.finders[tag];
var ecsToTag = finder ? (0, _watcherFinderMerger["default"])(_this2._scheduler, _this2.tree, tag, tagOptions, combinedWatcherSet, finder, _this2._logError) : combinedWatcherSet || _liveSet["default"].constant(new Set(), {
scheduler: _this2._scheduler
});
var elementsToNodes = new Map();
function findParentNode(taggedParents) {
var parentNode;
for (var i = taggedParents.length - 1; i >= 0; i--) {
if (taggedParents[i].tag == null || ownedBy.has(taggedParents[i].tag)) {
parentNode = taggedParents[i].node;
break;
}
}
if (!parentNode) throw new Error();
return parentNode;
}
var ecSet = new _liveSet["default"]({
scheduler: _this2._scheduler,
read: function read() {
throw new Error();
},
listen: function listen(setValues, controller) {
var m = new Map();
var cb = function cb(ec) {
var el = ec.el,
parents = ec.parents;
var parentNode = findParentNode(parents);
var node = _this2._treeController.addTaggedValue(parentNode, tag, el);
if (elementsToNodes.has(el)) {
_this2._logError(new Error("PageParserTree(".concat(tag, ") watcher received element twice")), el);
}
elementsToNodes.set(el, node);
var newParents = ec.parents.concat([{
tag: tag,
node: node
}]);
return {
el: el,
parents: newParents
};
};
return ecsToTag.subscribe({
start: function start() {
var s = new Set();
ecsToTag.values().forEach(function (value) {
var newValue = cb(value);
m.set(value, newValue);
s.add(newValue);
});
setValues(s);
},
next: function next(changes) {
var ecsRemovedInNotification = new Set();
if (changes.length > 1) {
changes.forEach(function (change) {
if (change.type === 'remove') {
ecsRemovedInNotification.add(change.value);
}
});
}
changes.forEach(function (change) {
if (change.type === 'add') {
// Don't process adds of elements that are removed by a later
// change in this notification.
if (ecsRemovedInNotification.has(change.value)) return;
var newValue = cb(change.value);
m.set(change.value, newValue);
controller.add(newValue);
} else if (change.type === 'remove') {
var _newValue = m.get(change.value);
if (!_newValue) return;
m["delete"](change.value);
controller.remove(_newValue);
var node = elementsToNodes.get(_newValue.el);
if (!node) throw new Error('Should not happen: received removal of unseen element');
elementsToNodes["delete"](_newValue.el);
var nodeParent = node.getParent(); // The node might have already been removed from the tree if it
// is owned by a node that was just removed.
if (nodeParent && nodeParent.ownsNode(node)) {
_this2._treeController.removeTaggedNode(nodeParent, tag, node);
}
}
});
},
error: function error(err) {
controller.error(err);
},
complete: function complete() {
controller.end();
}
});
}
});
_this2._subscriptions.push(ecSet.subscribe({}));
return [tag, {
liveSet: liveSet,
controller: controller,
ecSet: ecSet
}];
}));
this._options.watchers.forEach(function (_ref2) {
var sources = _ref2.sources,
selectors = _ref2.selectors,
tag = _ref2.tag;
var sourceSets = sources.map(function (tag) {
if (!tag) return _this2._rootMatchedSet;
var entry = _this2._ecSources.get(tag);
if (!entry) throw new Error('Unknown source: ' + tag);
return entry.ecSet;
});
var sourceSet = sourceSets.length === 1 ? sourceSets[0] : (0, _merge["default"])(sourceSets);
var transformer = (0, _createTransformer["default"])(_this2._scheduler, selectors);
var ecEntry = _this2._ecSources.get(tag);
if (!ecEntry) throw new Error();
ecEntry.controller.add(transformer(sourceSet));
});
this._scheduler.flush();
}
}, {
key: "_dumpWithoutEnd",
value: function _dumpWithoutEnd() {
var _this3 = this;
this._subscriptions.forEach(function (sub) {
sub.unsubscribe();
});
this._subscriptions.length = 0;
this.tree.getOwned().forEach(function (liveSet, tag) {
liveSet.values().forEach(function (node) {
_this3._treeController.removeTaggedNode(_this3.tree, tag, node);
});
});
}
}, {
key: "dump",
value: function dump() {
this._dumpWithoutEnd();
this._treeController.end();
} // Intended for use with hot module replacement.
}, {
key: "replaceOptions",
value: function replaceOptions(options) {
var tagErrStr = 'replaceOptions does not support tag changes';
var _makeTagOptions2 = makeTagOptions(options),
tagOptionsMap = _makeTagOptions2.map;
if (this._tagOptions.size !== tagOptionsMap.size) {
throw new Error(tagErrStr);
}
this._tagOptions.forEach(function (oldOptions, tag) {
var newOptions = tagOptionsMap.get(tag);
if (!newOptions) throw new Error(tagErrStr);
var oldOwnedBy = oldOptions.ownedBy || [];
var newOwnedBy = new Set(newOptions.ownedBy || []);
if (oldOwnedBy.length !== newOwnedBy.size) throw new Error(tagErrStr);
oldOwnedBy.forEach(function (tag) {
if (!newOwnedBy.has(tag)) throw new Error(tagErrStr);
});
});
this._dumpWithoutEnd();
this._options = options;
this._setupWatchersAndFinders();
}
}]);
return PageParserTree;
}();
exports["default"] = PageParserTree;
module.exports = exports.default;
module.exports.default = exports.default;
//# sourceMappingURL=index.js.map