Initial pass
This commit is contained in:
158
node_modules/jsdom/lib/jsdom/named-properties-tracker.js
generated
vendored
Normal file
158
node_modules/jsdom/lib/jsdom/named-properties-tracker.js
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
"use strict";
|
||||
// https://heycam.github.io/webidl/#idl-named-properties
|
||||
|
||||
const IS_NAMED_PROPERTY = Symbol("is named property");
|
||||
const TRACKER = Symbol("named property tracker");
|
||||
|
||||
/**
|
||||
* Create a new NamedPropertiesTracker for the given `object`.
|
||||
*
|
||||
* Named properties are used in DOM to let you lookup (for example) a Node by accessing a property on another object.
|
||||
* For example `window.foo` might resolve to an image element with id "foo".
|
||||
*
|
||||
* This tracker is a workaround because the ES6 Proxy feature is not yet available.
|
||||
*
|
||||
* @param {Object} object Object used to write properties to
|
||||
* @param {Object} objectProxy Object used to check if a property is already defined
|
||||
* @param {Function} resolverFunc Each time a property is accessed, this function is called to determine the value of
|
||||
* the property. The function is passed 3 arguments: (object, name, values).
|
||||
* `object` is identical to the `object` parameter of this `create` function.
|
||||
* `name` is the name of the property.
|
||||
* `values` is a function that returns a Set with all the tracked values for this name. The order of these
|
||||
* values is undefined.
|
||||
*
|
||||
* @returns {NamedPropertiesTracker}
|
||||
*/
|
||||
exports.create = function (object, objectProxy, resolverFunc) {
|
||||
if (object[TRACKER]) {
|
||||
throw Error("A NamedPropertiesTracker has already been created for this object");
|
||||
}
|
||||
|
||||
const tracker = new NamedPropertiesTracker(object, objectProxy, resolverFunc);
|
||||
object[TRACKER] = tracker;
|
||||
return tracker;
|
||||
};
|
||||
|
||||
exports.get = function (object) {
|
||||
if (!object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return object[TRACKER] || null;
|
||||
};
|
||||
|
||||
function NamedPropertiesTracker(object, objectProxy, resolverFunc) {
|
||||
this.object = object;
|
||||
this.objectProxy = objectProxy;
|
||||
this.resolverFunc = resolverFunc;
|
||||
this.trackedValues = new Map(); // Map<Set<value>>
|
||||
}
|
||||
|
||||
function newPropertyDescriptor(tracker, name) {
|
||||
const emptySet = new Set();
|
||||
|
||||
function getValues() {
|
||||
return tracker.trackedValues.get(name) || emptySet;
|
||||
}
|
||||
|
||||
const descriptor = {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return tracker.resolverFunc(tracker.object, name, getValues);
|
||||
},
|
||||
set(value) {
|
||||
Object.defineProperty(tracker.object, name, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
descriptor.get[IS_NAMED_PROPERTY] = true;
|
||||
descriptor.set[IS_NAMED_PROPERTY] = true;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track a value (e.g. a Node) for a specified name.
|
||||
*
|
||||
* Values can be tracked eagerly, which means that not all tracked values *have* to appear in the output. The resolver
|
||||
* function that was passed to the output may filter the value.
|
||||
*
|
||||
* Tracking the same `name` and `value` pair multiple times has no effect
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {*} value
|
||||
*/
|
||||
NamedPropertiesTracker.prototype.track = function (name, value) {
|
||||
if (name === undefined || name === null || name === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
let valueSet = this.trackedValues.get(name);
|
||||
if (!valueSet) {
|
||||
valueSet = new Set();
|
||||
this.trackedValues.set(name, valueSet);
|
||||
}
|
||||
|
||||
valueSet.add(value);
|
||||
|
||||
if (name in this.objectProxy) {
|
||||
// already added our getter or it is not a named property (e.g. "addEventListener")
|
||||
return;
|
||||
}
|
||||
|
||||
const descriptor = newPropertyDescriptor(this, name);
|
||||
Object.defineProperty(this.object, name, descriptor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop tracking a previously tracked `name` & `value` pair, see track().
|
||||
*
|
||||
* Untracking the same `name` and `value` pair multiple times has no effect
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {*} value
|
||||
*/
|
||||
NamedPropertiesTracker.prototype.untrack = function (name, value) {
|
||||
if (name === undefined || name === null || name === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const valueSet = this.trackedValues.get(name);
|
||||
if (!valueSet) {
|
||||
// the value is not present
|
||||
return;
|
||||
}
|
||||
|
||||
if (!valueSet.delete(value)) {
|
||||
// the value was not present
|
||||
return;
|
||||
}
|
||||
|
||||
if (valueSet.size === 0) {
|
||||
this.trackedValues.delete(name);
|
||||
}
|
||||
|
||||
if (valueSet.size > 0) {
|
||||
// other values for this name are still present
|
||||
return;
|
||||
}
|
||||
|
||||
// at this point there are no more values, delete the property
|
||||
|
||||
const descriptor = Object.getOwnPropertyDescriptor(this.object, name);
|
||||
|
||||
if (!descriptor || !descriptor.get || descriptor.get[IS_NAMED_PROPERTY] !== true) {
|
||||
// Not defined by NamedPropertyTracker
|
||||
return;
|
||||
}
|
||||
|
||||
// note: delete puts the object in dictionary mode.
|
||||
// if this turns out to be a performance issue, maybe add:
|
||||
// https://github.com/petkaantonov/bluebird/blob/3e36fc861ac5795193ba37935333eb6ef3716390/src/util.js#L177
|
||||
delete this.object[name];
|
||||
};
|
||||
Reference in New Issue
Block a user