171 lines
4.6 KiB
JavaScript
171 lines
4.6 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 TypeError(`can't redefine 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));
|
|
}
|
|
// Transform key. TODO: dynamic? Comes at a price...
|
|
let transformedProperty;
|
|
if (meta.transformProperty) {
|
|
transformedProperty = meta.transformProperty(key);
|
|
}
|
|
else {
|
|
transformedProperty = `$$avocado_property_${key}`;
|
|
}
|
|
class Property extends Superclass {
|
|
|
|
constructor(...args) {
|
|
super(...args);
|
|
// Initialize?
|
|
if (meta.initialize) {
|
|
meta.initialize.call(this);
|
|
}
|
|
else {
|
|
// Set default.
|
|
if (undefined !== metaDefault) {
|
|
this[transformedProperty] = metaDefault;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
// Getter.
|
|
const getter = meta.get ? meta.get : new Function(`
|
|
return this.${transformedProperty};
|
|
`);
|
|
// Setter.
|
|
let setter;
|
|
// Helper to define assigner.
|
|
function defineAssigner() {
|
|
const assignMethod = `${transformedProperty}$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 = `${transformedProperty}$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 = `${transformedProperty}$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.${transformedProperty} = 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.${transformedProperty} = 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.${transformedProperty} = value;
|
|
`);
|
|
}
|
|
}
|
|
Object.defineProperty(Property.prototype, key, {
|
|
configurable: true,
|
|
enumerable: true,
|
|
get: getter,
|
|
set: setter,
|
|
});
|
|
return Property;
|
|
}
|
|
}
|