You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
3.5 KiB
JavaScript

'use strict';
const {DOCUMENT_FRAGMENT_NODE} = require('./shared/constants.js');
const {defineProperties, getOwnPropertyDescriptors} = require('./shared/object.js');
const {Attr} = require('./interface/attr.js');
const {CharacterData} = require('./interface/character-data.js');
const {Element} = require('./interface/element.js');
const {ParentNode} = require('./mixin/parent-node.js');
const {NonElementParentNode} = require('./mixin/non-element-parent-node.js');
const {HTMLDocument} = require('./html/document.js');
const {
childNodesWM,
childrenWM,
querySelectorWM,
querySelectorAllWM,
get,
reset
} = require('./shared/cache.js');
// Attr
const {value: {
get: getAttributeValue,
set: setAttributeValue
}} = getOwnPropertyDescriptors(Attr.prototype);
defineProperties(Attr.prototype, {
value: {
get: getAttributeValue,
set(value) {
reset(this.ownerElement);
setAttributeValue.call(this, value);
}
}
});
// CharacterData
// TODO: is txtContent really necessary to patch here?
const {remove: removeCharacterData} = CharacterData.prototype;
defineProperties(CharacterData.prototype, {
remove: {value() {
reset(this.parentNode);
removeCharacterData.call(this);
}}
});
// Element
const elementProtoDescriptors = {};
for (const name of [
'remove',
'setAttribute',
'setAttributeNS',
'setAttributeNode',
'setAttributeNodeNS',
'removeAttribute',
'removeAttributeNS',
'removeAttributeNode'
]) {
const method = Element.prototype[name];
elementProtoDescriptors[name] = {
value() {
reset(this.parentNode);
return method.apply(this, arguments);
}
};
}
defineProperties(Element.prototype, elementProtoDescriptors);
// ParentNode
const {
childNodes: {get: getChildNodes},
children: {get: getChildren}
} = getOwnPropertyDescriptors(ParentNode.prototype);
defineProperties(ParentNode.prototype, {
childNodes: {get() {
return get(childNodesWM, this, getChildNodes);
}},
children: {get() {
return get(childrenWM, this, getChildren);
}}
});
const {
insertBefore,
querySelector,
querySelectorAll
} = ParentNode.prototype;
const query = (wm, method, self, selectors) => {
if (!wm.has(self))
wm.set(self, new Map);
const map = wm.get(self);
if (map.has(selectors))
return map.get(selectors);
const result = method.call(self, selectors);
map.set(selectors, result);
return result;
};
defineProperties(ParentNode.prototype, {
insertBefore: {value(node, before) {
reset(this);
if (node.nodeType === DOCUMENT_FRAGMENT_NODE)
reset(node);
return insertBefore.call(this, node, before);
}},
getElementsByClassName: {value(className) {
return this.querySelectorAll('.' + className);
}},
getElementsByTagName: {value(tagName) {
return this.querySelectorAll(tagName);
}},
querySelector: {value(selectors) {
return query(querySelectorWM, querySelector, this, selectors);
}},
querySelectorAll: {value(selectors) {
return query(querySelectorAllWM, querySelectorAll, this, selectors);
}}
});
// NonElementParentNode
defineProperties(NonElementParentNode.prototype, {
getElementById: {value(id) {
return this.querySelector('#' + id);
}}
});
// HTMLDocument
const {title: {
get: getTitle,
set: setTitle
}} = getOwnPropertyDescriptors(HTMLDocument.prototype);
defineProperties(HTMLDocument.prototype, {
title: {
get: getTitle,
set(value) {
reset(this.head);
setTitle.call(this, value);
}
}
});
(m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k])))
(require('./index.js'));