avocado-old/packages/core/property.js

167 lines
4.5 KiB
JavaScript
Raw Normal View History

/**
* This Mixin contains a lot of nasty looking unrolls and string functions.
* Trying to optimize this very hot spot!
*/
2019-03-17 23:45:48 -05:00
export function PropertyMixin(key, meta = {}) {
2019-04-11 17:53:38 -05:00
// Param check.
2019-03-17 23:45:48 -05:00
if (!meta || 'object' !== typeof meta) {
throw new TypeError(
`Expected meta for Property(${
key
}) to be object. ${
JSON.stringify(meta)
} given.`
);
}
return (Superclass) => {
// Sanity check.
2019-03-17 23:45:48 -05:00
if (Superclass.prototype[key]) {
2020-06-17 18:18:03 -05:00
throw new SyntaxError(`redeclaration of Avocado property ${key}`);
2019-03-17 23:45:48 -05:00
}
2019-04-11 17:53:38 -05:00
// Handle defaults.
2019-03-17 23:45:48 -05:00
let metaDefault;
if (null === meta.default) {
metaDefault = null;
}
else if (undefined === meta.default) {
metaDefault = undefined;
}
else {
metaDefault = JSON.parse(JSON.stringify(meta.default));
}
2020-06-17 06:53:29 -05:00
const transformedKey = `$$avocado_property_${key}`;
2019-04-11 17:53:38 -05:00
class Property extends Superclass {
2019-03-17 23:45:48 -05:00
2020-06-17 18:18:03 -05:00
static get propertyList() {
const list = (super.propertyList ? super.propertyList : {});
return {...list, [key]: meta};
}
constructor(...args) {
super(...args);
2019-04-12 15:54:14 -05:00
// Initialize?
if (meta.initialize) {
meta.initialize.call(this);
}
else {
// Set default.
if (undefined !== metaDefault) {
2020-06-17 06:53:29 -05:00
this[transformedKey] = metaDefault;
}
2019-03-17 23:45:48 -05:00
}
}
2019-04-11 17:53:38 -05:00
}
// Getter.
2020-06-17 18:18:03 -05:00
const getter = meta.get ? meta.get : new Function(`return this.${transformedKey};`);
2019-04-11 17:53:38 -05:00
// Setter.
let setter;
// Helper to define assigner.
function defineAssigner() {
2020-06-17 06:53:29 -05:00
const assignMethod = `${transformedKey}$assign`;
2019-04-11 17:53:38 -05:00
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);
}
};
2020-06-17 06:53:29 -05:00
const emitMethod = `${transformedKey}$emit`;
2019-04-11 17:53:38 -05:00
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;
};
2020-06-17 06:53:29 -05:00
const compareMethod = `${transformedKey}$compare`;
2019-04-11 17:53:38 -05:00
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);
2019-04-11 17:53:38 -05:00
}
`);
}
else {
2019-04-11 17:53:38 -05:00
// Set with comparator and emitter.
setter = new Function('value', `
const old = this.${key};
2020-06-17 06:53:29 -05:00
this.${transformedKey} = value;
if (!this.${compareMethod}(old, value)) {
this.${emitMethod}('${key}Changed', old, value);
2019-04-11 17:53:38 -05:00
}
`);
}
2019-03-22 11:24:50 -05:00
}
2019-04-11 17:53:38 -05:00
// No compare.
else {
// Assign?
if (meta.set) {
2019-04-11 17:53:38 -05:00
// Define assigner.
const assignMethod = defineAssigner();
// Set with assigner and emitter.
setter = new Function('value', `
const old = this.${key};
this.${assignMethod}(value);
2019-04-11 17:53:38 -05:00
if (old !== value) {
this.${emitMethod}('${key}Changed', old, value);
2019-04-11 17:53:38 -05:00
}
`);
}
else {
2019-04-11 17:53:38 -05:00
// Set with emitter.
setter = new Function('value', `
const old = this.${key};
2020-06-17 06:53:29 -05:00
this.${transformedKey} = value;
2019-04-11 17:53:38 -05:00
if (old !== value) {
this.${emitMethod}('${key}Changed', old, value);
}
`);
2019-03-17 23:45:48 -05:00
}
2019-03-22 11:24:50 -05:00
}
}
2019-04-11 17:53:38 -05:00
// No tracking?
else {
// Assign?
if (meta.set) {
// Define assigner.
const assignMethod = defineAssigner();
// Set with assigner.
setter = new Function('value', `
this.${assignMethod}(value);
`);
2019-04-11 17:53:38 -05:00
}
else {
// Set raw.
setter = new Function('value', `
2020-06-17 06:53:29 -05:00
this.${transformedKey} = value;
`);
2019-04-11 17:53:38 -05:00
}
}
Object.defineProperty(Property.prototype, key, {
2019-04-18 20:44:33 -05:00
configurable: true,
2019-04-11 17:53:38 -05:00
enumerable: true,
get: getter,
set: setter,
});
return Property;
2019-03-17 23:45:48 -05:00
}
}