infamous
Version:
A CSS3D/WebGL UI library.
112 lines (93 loc) • 4.61 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _lowclass = _interopRequireDefault(require("lowclass"));
var _Mixin = _interopRequireDefault(require("../../core/Mixin"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _default = (0, _Mixin.default)(Base => // TODO This is here for now. Make it an extension to
// element-behaviors so that it can be applied to any element
// generically.
(0, _lowclass.default)('DefaultBehaviors').extends(Base, ({
Super
}) => ({
static: {
// override in subclasses
defaultBehaviors: []
},
constructor(...args) {
// Use the constructor return value, because we assume Base must be
// a HTML element class, in which case this is currently unavoidable
// because lowclass currently uses ES5-style constructors. This may
// change when lowclass has `class` syntax option.
const self = Super(this).constructor(...args); // If no geometry or material behavior is detected, add default ones.
//
// Deferring to a microtask doesn't work here, we must defer to a
// macrotask with setTimeout, so we can detect if the element has
// initial behaviors, otherwise the element's initial attributes
// haven't been added yet (this is how HTML engines work, see
// https://github.com/whatwg/dom/issues/522).
//
// TODO: If we use setTimeout (macrotask) deferral anywhere (like we do
// here), and maybe even with microtask deferral (f.e. Promise), maybe
// we should have a single place that initiate this deferral so that
// everything in the engine can hook into it. Otherwise if different
// call sites use setTimeout, logic will be firing at random and in
// different order.
setTimeout(() => self._setDefaultBehaviorsIfNeeded(), 0);
return self;
},
_setDefaultBehaviorsIfNeeded() {
let defaultBehaviors = this.constructor.defaultBehaviors; // do nothing if there's no defaults
if (!defaultBehaviors) return;
if (Object.keys(defaultBehaviors).length == 0) return;
const initialBehaviorNames = Array.from(this.behaviors.keys()); // small optimization: if there are no initial behaviors and we
// have default behaviors, just set the default behaviors.
if (initialBehaviorNames.length == 0) {
// if not an array, then it's an object.
if (!(defaultBehaviors instanceof Array)) defaultBehaviors = Object.keys(defaultBehaviors);
this.setAttribute('has', this.getAttribute('has') + ` ${defaultBehaviors.join(' ')}`);
} // otherwise detect which default behavior(s) to add
else {
let behaviorNamesToAdd = ''; // if defaultBehaviors is an array, use default logic to add
// behaviors that aren't already added.
if (defaultBehaviors instanceof Array) {
for (const defaultBehaviorName of defaultBehaviors) {
let hasBehavior = false;
for (const initialBehaviorName of initialBehaviorNames) {
if (defaultBehaviorName == initialBehaviorName) {
hasBehavior = true;
break;
}
}
if (hasBehavior) continue;else {
// TODO programmatic API:
//this.behaviors.add('box-geometry')
// add a space in front of each name except the first
if (behaviorNamesToAdd) behaviorNamesToAdd += ' ';
behaviorNamesToAdd += defaultBehaviorName;
}
}
} // if defaultBehaviors is an object, then behaviors are added
// based on conditions.
else if (typeof defaultBehaviors == 'object') {
const defaultBehaviorNames = Object.keys(defaultBehaviors);
for (const defaultBehaviorName of defaultBehaviorNames) {
const condition = defaultBehaviors[defaultBehaviorName];
if (typeof condition == 'function' && condition(initialBehaviorNames) || typeof condition != 'function' && condition) {
// add a space in front of each name except the first
if (behaviorNamesToAdd) behaviorNamesToAdd += ' ';
behaviorNamesToAdd += defaultBehaviorName;
}
}
} // add the needed behaviors all at once.
if (behaviorNamesToAdd) {
let currentHasValue = this.getAttribute('has');
if (currentHasValue) currentHasValue += ' ';
this.setAttribute('has', currentHasValue + behaviorNamesToAdd);
}
}
}
})));
exports.default = _default;
;