167 lines
4.5 KiB
JavaScript
167 lines
4.5 KiB
JavaScript
/**
|
|
* This Mixin contains a lot of nasty looking unrolls and string functions.
|
|
* Trying to optimize this very hot spot!
|
|
*/
|
|
export function PropertyMixin(key, meta = {}) {
|
|
// Param check.
|
|
if (!meta || 'object' !== typeof meta) {
|
|
throw new TypeError(
|
|
`Expected meta for Property(${
|
|
key
|
|
}) to be object. ${
|
|
JSON.stringify(meta)
|
|
} given.`
|
|
);
|
|
}
|
|
return (Superclass) => {
|
|
// Sanity check.
|
|
if (Superclass.prototype[key]) {
|
|
throw new SyntaxError(`redeclaration of Avocado property ${key}`);
|
|
}
|
|
// Handle defaults.
|
|
let metaDefault;
|
|
if (null === meta.default) {
|
|
metaDefault = null;
|
|
}
|
|
else if (undefined === meta.default) {
|
|
metaDefault = undefined;
|
|
}
|
|
else {
|
|
metaDefault = JSON.parse(JSON.stringify(meta.default));
|
|
}
|
|
const transformedKey = `$$avocado_property_${key}`;
|
|
class Property extends Superclass {
|
|
|
|
static get propertyList() {
|
|
const list = (super.propertyList ? super.propertyList : {});
|
|
return {...list, [key]: meta};
|
|
}
|
|
|
|
constructor(...args) {
|
|
super(...args);
|
|
// Initialize?
|
|
if (meta.initialize) {
|
|
meta.initialize.call(this);
|
|
}
|
|
else {
|
|
// Set default.
|
|
if (undefined !== metaDefault) {
|
|
this[transformedKey] = metaDefault;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
// Getter.
|
|
const getter = meta.get ? meta.get : new Function(`return this.${transformedKey};`);
|
|
// Setter.
|
|
let setter;
|
|
// Helper to define assigner.
|
|
function defineAssigner() {
|
|
const assignMethod = `${transformedKey}$assign`;
|
|
Object.defineProperty(Property.prototype, assignMethod, {
|
|
value: meta.set,
|
|
});
|
|
return assignMethod;
|
|
}
|
|
// Tracking?
|
|
if (meta.track) {
|
|
// Define emitter.
|
|
const emitter = meta.emit ? meta.emit : function(...args) {
|
|
if (this.emit) {
|
|
this.emit(...args);
|
|
}
|
|
};
|
|
const emitMethod = `${transformedKey}$emit`;
|
|
Object.defineProperty(Property.prototype, emitMethod, {
|
|
value: emitter,
|
|
});
|
|
// Compare?
|
|
if (meta.eq) {
|
|
// Define comparator.
|
|
const comparator = meta.eq ? meta.eq : function(l, r) {
|
|
return l === r;
|
|
};
|
|
const compareMethod = `${transformedKey}$compare`;
|
|
Object.defineProperty(Property.prototype, compareMethod, {
|
|
value: comparator,
|
|
});
|
|
// Assign?
|
|
if (meta.set) {
|
|
// Define assigner.
|
|
const assignMethod = defineAssigner();
|
|
// Set with assigner, comparator, and emitter.
|
|
setter = new Function('value', `
|
|
const old = this.${key};
|
|
this.${assignMethod}(value);
|
|
if (!this.${compareMethod}(old, value)) {
|
|
this.${emitMethod}('${key}Changed', old, value);
|
|
}
|
|
`);
|
|
}
|
|
else {
|
|
// Set with comparator and emitter.
|
|
setter = new Function('value', `
|
|
const old = this.${key};
|
|
this.${transformedKey} = value;
|
|
if (!this.${compareMethod}(old, value)) {
|
|
this.${emitMethod}('${key}Changed', old, value);
|
|
}
|
|
`);
|
|
}
|
|
}
|
|
// No compare.
|
|
else {
|
|
// Assign?
|
|
if (meta.set) {
|
|
// Define assigner.
|
|
const assignMethod = defineAssigner();
|
|
// Set with assigner and emitter.
|
|
setter = new Function('value', `
|
|
const old = this.${key};
|
|
this.${assignMethod}(value);
|
|
if (old !== value) {
|
|
this.${emitMethod}('${key}Changed', old, value);
|
|
}
|
|
`);
|
|
}
|
|
else {
|
|
// Set with emitter.
|
|
setter = new Function('value', `
|
|
const old = this.${key};
|
|
this.${transformedKey} = value;
|
|
if (old !== value) {
|
|
this.${emitMethod}('${key}Changed', old, value);
|
|
}
|
|
`);
|
|
}
|
|
}
|
|
}
|
|
// No tracking?
|
|
else {
|
|
// Assign?
|
|
if (meta.set) {
|
|
// Define assigner.
|
|
const assignMethod = defineAssigner();
|
|
// Set with assigner.
|
|
setter = new Function('value', `
|
|
this.${assignMethod}(value);
|
|
`);
|
|
}
|
|
else {
|
|
// Set raw.
|
|
setter = new Function('value', `
|
|
this.${transformedKey} = value;
|
|
`);
|
|
}
|
|
}
|
|
Object.defineProperty(Property.prototype, key, {
|
|
configurable: true,
|
|
enumerable: true,
|
|
get: getter,
|
|
set: setter,
|
|
});
|
|
return Property;
|
|
}
|
|
}
|