import { Flags, ObjectProxy } from '@uino/base-thing';
import { MathUtils } from '../math/MathUtils';
import { Utils } from '../common/Utils'
import { Selector } from '../selector/Selector';
import { DynamicSelector } from '../selector/DynamicSelector';
import { BaseComponentGroup } from '../components/BaseComponentGroup';
import { EventType, InheritType } from '../const';
import { Tags } from '../common/Tags';
const __ = {
private: Symbol('private'),
}
const Flag = {
Destroyed: 1 << 0,
Dirty: 1 << 1,
Queryable: 1 << 2,
Destroyable: 1 << 3,
}
let _orderId = 1;
let _dummySelector = new Selector();
let _eventListeners = [];
let _defaultParams = {};
// #region Private Functions
// #endregion
/**
* @class BaseObject
* ThingJS中最基础的对象
* @memberof THING
* @extends THING.BaseComponentGroup
* @public
*/
class BaseObject extends BaseComponentGroup {
static defaultTagArray = ['BaseObject'];
/**
* 基础对象类,负责 ThingJS 中最基础的对象功能实现。
* ⚠️ 不建议直接 new 该类,推荐通过 THING.Object3D 或其子类来创建实例。
* @param {object} param 初始化 BaseObject 的属性
* @param {string} [param.name=''] 物体名称
* @param {string} [param.id=''] 物体 id
* @param {string} [param.uuid] 物体唯一标识,如果不传会自动生成唯一字符串
* @param {object} [param.userData] 设置/获取物体的自定义属性
* @param {boolean} [param.queryable=true] 物体是否可被查询,默认 true
* @param {boolean} [param.destroyable=true] 物体是否可被销毁,默认 true
*/
constructor(param = {}) {
super();
this[__.private] = {};
let _private = this[__.private];
this.onSetupApp(param);
// Common
_private.tags = null;
_private.userData = null;
_private.internalData = null;
_private.type = '';
_private.dtType = ''; // Add dtType for digital twin type
// Temporary Data
_private.tempData = null;
// Update internal order ID for fast searching
_private.orderId = _orderId++;
// Relation
_private.parent = null;
_private.children = null;
// The proxy object
_private.proxyObject = null;
// Show important info what we can not wrap into private space, so bind to object
this._name = Utils.parseValue(param['name'], '');
this._id = Utils.parseValue(param['id'], '');
this._uuid = param['uuid'] || MathUtils.generateUUID();
// Setup flags
this._flags = new Flags();
this._flags.enable(Flag.Queryable, Utils.parseValue(param['queryable'], true));
this._flags.enable(Flag.Destroyable, Utils.parseValue(param['destroyable'], true));
// Setup type first, because we need type attribute
this.onSetupType(param);
// Setup
this.onBeforeSetup(param);
this.onSetupFlags(param);
this.onSetupTags(param);
this.onSetupParent(param); // Set parent first to make sure parent order ID is valid
this.onSetupAsyncSelector(param); // Add object first to make sure object order ID is valid
this.onSetupUserData(param);
this.onSetupInternalData(param)
this.onAfterSetup(param);
// Add object into manager
_private.app.objectManager.addObject(this);
}
// #region Private
_removeFromParent() {
let _private = this[__.private];
// Remove it from previous parent
if (_private.parent) {
let children = _private.parent.children;
let index = children.indexOf(this);
if (index !== -1) {
children.removeAt(index);
}
_private.parent = null;
}
}
_addEventListener(type, condition, callback, tag, priority, options, once = false) {
let info = Utils.parseEvent(type, condition, callback, tag, priority, options, this.onInitEventArgs);
if (this.onParseEvent(info)) {
let app = this.app || Utils.getCurrentApp();
return app.eventManager.addEventListener(info.type.toLowerCase(), this, info.condition, info.callback, info.tag, {
once,
priority: info.priority,
propagate: info.propagate,
useCapture: info.useCapture,
enumerable: info.enumerable,
pausable: info.pausable,
includeSelf: info.includeSelf
});
}
return null;
}
_getEventListener(type, condition, tag) {
return this.app.eventManager.getEventListener(type.toLowerCase(), this, condition, tag);
}
_removeEventListener(type, condition, tag) {
this.app.eventManager.removeEventListener(type.toLowerCase(), this, condition, tag);
}
_getUserData() {
let _private = this[__.private];
let userData = _private.userData;
if (userData) {
return userData;
}
if (Selector.asyncSelector) {
_private.userData = new ObjectProxy({
onChange: (ev) => {
Selector.updateObjectAttribute(this, 'userData', ev.data);
}
});
}
else {
_private.userData = {};
}
return _private.userData;
}
_getInternalData() {
let _private = this[__.private];
if (!_private.internalData) {
_private.internalData = {};
}
return _private.internalData;
}
_getTempData(callback) {
let _private = this[__.private];
if (!_private.tempData) {
_private.tempData = {
tickableCounter: 0,
resizableCounter: 0,
};
if (callback) {
callback(_private.tempData);
}
}
return _private.tempData;
}
_getParentObject(param) {
let parent = param['parent'];
if (parent === null) {
return null;
}
return parent || this.app.root;
}
_isExpression(condition) {
// It's string format
if (condition._startsWith('"') || condition._startsWith('\'')) {
return true;
}
// It's id format
if (condition._startsWith('#')) {
return true;
}
// It's type format
if (condition._startsWith('.')) {
return true;
}
// It's attribute name
if (condition._startsWith('[') && condition._endsWith(']')) {
return true;
}
// Has OR operator
if (condition.indexOf('||') !== -1 || condition.indexOf('|') !== -1) {
return true;
}
// Has AND operator
if (condition.indexOf('&&') !== -1 || condition.indexOf('&') !== -1) {
return true;
}
// It's tags format
if (condition._startsWith('tags')) {
if (condition[4] === ':' || condition[4] === '(') {
return true;
}
}
return false;
}
// #endregion
// #region Overrides
onSetupApp(param) {
const _private = this[__.private];
// Setup app
const parent = param['parent']
if (parent && parent.app) {
_private.app = parent.app;
}
else {
_private.app = param['app'] || Utils.getCurrentApp();
}
}
onAddTickableObject() {
let tempData = this._getTempData();
if (tempData['tickableCounter'] === 0) {
this.app.objectManager.addTickableObject(this);
}
tempData['tickableCounter']++;
}
onAddResizableObject() {
let tempData = this._getTempData();
if (tempData['resizableCounter'] === 0) {
this.app.objectManager.addResizableObject(this);
}
tempData['resizableCounter']++;
}
onRemoveTickableObject() {
let tempData = this._getTempData();
tempData['tickableCounter']--;
if (tempData['tickableCounter'] === 0) {
this.app.objectManager.removeTickableObject(this);
}
}
onRemoveResizableObject() {
let tempData = this._getTempData();
tempData['resizableCounter']--;
if (tempData['resizableCounter'] === 0) {
this.app.objectManager.removeResizableObject(this);
}
}
// #endregion
// #region Setup
onSetupFlags(param) {
}
onBeforeSetup(param) {
}
onAfterSetup(param) {
}
onSetupType(param) {
let _private = this[__.private];
let prototype = this.__proto__;
let className = Utils.getClassNameFromProto(prototype);
_private.type = className;
_private.dtType = param['dtType'] || ''; // Initialize dtType from param
let selfClassKey = 'is' + className;
if (this.selfClassKey == undefined) {
Object.defineProperty(this, selfClassKey, {
enumerable: false,
configurable: false,
get() {
return true;
},
});
}
// Check whether we had processed yet
prototype.__inherits = prototype.__inherits || {};
if (prototype.__inherits[className]) {
return;
}
// The types of each class to get inherition
prototype.__inherits[className] = {
types: {}
};
// Get the class names
let classNames;
let app = this.app;
if (app) {
classNames = app.global.getClassNamesFromObject(this);
}
else {
let proto = this.__proto__;
classNames = [Utils.getClassNameFromProto(proto)];
}
classNames.forEach(name => {
prototype.__inherits[className].types[name] = true;
let key = 'is' + name;
if (!this[key]) {
Object.defineProperty(prototype, key, {
enumerable: false,
configurable: false,
get() {
return prototype.__inherits[className].types[name];
},
});
}
});
}
onSetupParent(param) {
let parent = this._getParentObject(param);
if (!parent) {
return;
}
const indexOfParent = param['indexOfParent'];
parent.add(this, { attachMode: false, indexOfParent });
}
onSetupTags(param) {
let _private = this[__.private];
// Setup tags
if (param['tags'] === undefined) {
const defaultTags = this.onGetDefaultTags();
_private.tags = new Tags(Utils.mergeSet(param['tags'], defaultTags));
}
else {
_private.tags = new Tags(param['tags']);
}
}
onGetDefaultTags() {
return this.constructor.defaultTagArray;
}
onSetupAsyncSelector(param) {
// Add into async selector
Selector.addObject(this);
}
onSetupUserData(param) {
let fromUserData = param['userData'];
if (!fromUserData) {
return;
}
let toUserData = this._getUserData();
if (toUserData.isObjectProxy) {
toUserData.copy(fromUserData);
}
else {
Object.assign(toUserData, fromUserData);
}
}
onSetupInternalData(param) {
let fromInternalData = param['internalData'];
if (!fromInternalData) {
return;
}
let toInternalData = this._getInternalData();
Object.assign(toInternalData, fromInternalData);
}
// #endregion
// #region Overrides
/**
* When update.
* @param {number} deltaTime The delta time in seconds.
* @private
* @example
* let object = new THING.BaseObject();
* object.onUpdate = function(deltaTime) {
* console.log(deltaTime);
* };
*/
onUpdate(deltaTime) {
let tickableComponents = this.tickableComponents;
if (tickableComponents) {
for (let i = 0, l = tickableComponents.length; i < l; i++) {
let component = tickableComponents[i];
if (component.active === false) {
continue;
}
component.onUpdate(deltaTime);
}
}
}
/**
* When resize.
* @param {number} width The width in pixel.
* @param {number} height The height in pixel.
* @private
* @example
* let object = new THING.BaseObject();
* object.onResize = function(width, height) {
* console.log(width, height);
* };
*/
onResize(width, height) {
let resizableComponents = this.resizableComponents;
if (resizableComponents) {
for (let i = 0, l = resizableComponents.length; i < l; i++) {
let component = resizableComponents[i];
component.onResize(width, height);
}
}
}
/**
* When refresh.
* @private
* @example
* let object = new THING.BaseObject();
* object.onRefresh = function() {
* console.log('refresh');
* };
*/
onRefresh() {
let refreshableComponents = this.refreshableComponents;
if (refreshableComponents) {
for (let i = 0, l = refreshableComponents.length; i < l; i++) {
let component = refreshableComponents[i];
component.onRefresh();
}
}
this._flags.enable(Flag.Dirty, false);
}
/**
* When before destroy.
* @returns {boolean} false indicates break the destroy operation.
* @private
* @example
* let object = new THING.BaseObject();
* object.onBeforeDestroy = function() {
* console.log('before destroy');
* return false;
* };
*/
onBeforeDestroy() {
let flags = this._flags;
// Check whether can be destroyed
if (!flags.has(Flag.Destroyable)) {
return false;
}
// Check whether had destroyed
if (flags.has(Flag.Destroyed)) {
return false;
}
return true;
}
/**
* When destroy.
* @private
* @example
* let object = new THING.BaseObject();
* object.onDestroy = function() {
* console.log('destroy');
* };
*/
onDestroy() {
let _private = this[__.private];
// Notify it's going to destroy it
this.trigger(EventType.Destroy);
// Remove all children
if (_private.children) {
let children = _private.children.slice(0);
children.forEach(child => {
child.destroy();
});
_private.children.clear();
}
// Notify parent to remove child object
let parent = _private.parent;
if (parent) {
parent.onBeforeRemoveChild(this);
this.onSetParent(null);
parent.onAfterRemoveChild(this);
}
// Remove it from object manager
_private.app.objectManager.removeObject(this);
_private.app.objectManager.removeTickableObject(this);
_private.app.objectManager.removeResizableObject(this);
// Remove it from async selector
Selector.removeObject(this);
// Remove all components
this.removeAllComponents(true);
// Clear all accessor info
this.clearAccessorInfo();
// Clear user data
let userData = _private.userData;
if (userData) {
if (userData.isObjectProxy) {
userData.dispose();
}
_private.userData = null;
}
// We must clear parent here, due to 'AfterDestroy' event need to check parent object
_private.parent = null;
// Remove all events of it
_private.app.eventManager.removeAllEventListeners(this);
// Make it destroyed, be careful:
// We must set the destroy flag after object manager remove it to prevent 'Destroy' event trigger failed
this._flags.enable(Flag.Destroyed, true);
}
/**
* When after destroy.
* @private
* @example
* let object = new THING.BaseObject();
* object.onAfterDestroy = function() {
* console.log('after destroy');
* };
*/
onAfterDestroy() {
let _private = this[__.private];
// Remove it from previous parent
this._removeFromParent();
// Resume all paused events
// _private.app.eventManager.resumeAllEvents(this);
_private.app = null;
}
onParseEvent(info) {
return true;
}
onBeforeAddChild(object) {
// Notify all components
let components = this.components;
components.forEach((component) => {
if (component.onBeforeAddChild) {
component.onBeforeAddChild(object);
}
})
this.trigger(EventType.BeforeAddChild, { child: object });
}
onAddChild(object, options) {
object.onSetParent(this, options);
}
onAfterAddChild(object) {
// Notify all components
let components = this.components;
components.forEach((component) => {
if (component.onAfterAddChild) {
component.onAfterAddChild(object);
}
})
this.trigger(EventType.AfterAddChild, { child: object });
}
onBeforeRemoveChild(object) {
// Notify all components
let components = this.components;
components.forEach((component) => {
if (component.onBeforeRemoveChild) {
component.onBeforeRemoveChild(object);
}
})
this.trigger(EventType.BeforeRemoveChild, { child: object });
}
onAfterRemoveChild(object) {
// Notify all components
let components = this.components;
components.forEach((component) => {
if (component.onAfterRemoveChild) {
component.onAfterRemoveChild(object);
}
})
this.trigger(EventType.AfterRemoveChild, { child: object });
}
// #endregion
// #region BaseComponentGroup Interfaces
onAddComponent(component, args) {
super.onAddComponent(component, args);
if (component.onUpdate || component.onRender) {
this.onAddTickableObject();
}
if (component.onResize) {
this.onAddResizableObject();
}
}
onRemoveComponent(component) {
super.onRemoveComponent(component);
if (component.onUpdate || component.onRender) {
this.onRemoveTickableObject();
}
if (component.onResize) {
this.onRemoveResizableObject();
}
}
onBeforeSetParent() {
}
// #endregion
// #region Common
/**
* 检查是否含有属性名为name的属性
* @param {string} name 属性名称,支持嵌套,例如查询object.a.b.c 可以使用object.hasAttribute('a/b/c')
* @param {string} separator 分隔符,默认是'/'
* @returns {boolean} 返回一个布尔值,表示是否存在指定的属性
* @example
* let object = new THING.BaseObject();
* object.userData['power'] = 100;
* let ret = object.hasAttribute('userData/power');
* // @expect(ret == true)
* @public
*/
hasAttribute(name, separator = '/') {
return Utils.hasAttribute(this, name, separator);
}
/**
* 根据name获取属性值
* @param {string} key 属性名称,支持嵌套,例如获取object.a.b.c 可以使用object.getAttribute('a/b/c')
* @param {string} separator 分隔符,默认是'/'
* @returns {*} 返回指定属性的值
* @example
* let object = new THING.BaseObject();
* object.userData['power'] = 100;
* let power = object.getAttribute('userData/power');
* // @expect(power == 100)
* @public
*/
getAttribute(key, separator = '/') {
return Utils.getAttribute(this, key, separator);
}
/**
* 设置属性名称为key的属性值
* @param {string} key 属性名称,支持嵌套,例如设置object.a.b.c=1 可以使用object.setAttribute('a/b/c',1)
* @param {*} value 属性值
* @param {string} separator 分隔符,默认是'/'
* @example
* let object = new THING.BaseObject();
* object.setAttribute('userData/power', 200);
* let power = object.getAttribute('userData/power');
* // @expect(power == 200)
* @public
*/
setAttribute(key, value, separator = '/') {
Utils.setAttribute(this, key, value, separator);
}
/**
* 设置属性
* @param {object} attributes 属性对象
* @param {boolean} [overwrite=true] 是否覆盖
* @param {string} separator 分隔符,默认是'/'
* @example
* let object = new THING.BaseObject();
* object.setAttributes({
* "userData/name": 'Mr.Door',
* "userData/age": 18
* })
* let name = object.getAttribute('userData/name');
* // @expect(name == 'Mr.Door')
* let age = object.getAttribute('userData/age');
* // @expect(age == 18)
* @public
*/
setAttributes(attributes = {}, overwrite = true, separator = '/') {
for (let key in attributes) {
if (!overwrite && !Utils.isUndefined(this.getAttribute(key))) {
continue;
}
this.setAttribute(key, attributes[key], separator);
}
this.trigger(EventType.ChangeAttributes, { attributes });
}
/**
* 获取app对象
* @type {THING.App}
* @example
* let object = new THING.BaseObject();
* let app = object.app;
* let ret = app == THING.App.current;
* // @expect(ret == true)
* @public
*/
get app() {
return this[__.private].app;
}
/**
* 获取类型
* @type {string}
* @example
* let object = new THING.BaseObject();
* let type = object.type;
* // @expect(type == 'BaseObject')
* @public
*/
get type() {
return this[__.private].type;
}
/**
* 设置/获取 孪生体类型
* @type {string}
* @example
* let object = new THING.BaseObject();
* object.dtType = 'Equipment';
* // @expect(object.dtType == 'Equipment')
* @public
*/
get dtType() {
return this[__.private].dtType;
}
set dtType(value) {
this[__.private].dtType = value;
Selector.updateObjectAttribute(this, 'dtType', value);
}
/**
* Get order ID.
* @type {number}
* @private
*/
get orderId() {
return this[__.private].orderId;
}
/**
* 设置/获取 物体名称
* @type {string}
* @example
* let object = new THING.BaseObject();
* // @expect(object.name == '');
* object.name = 'car';
* // @expect(object.name == 'car');
* @public
*/
get name() {
return this._name;
}
set name(value) {
this._name = value;
Selector.updateObjectAttribute(this, 'name', value);
}
/**
* 设置/获取 物体id
* @type {string}
* @example
* let object = new THING.BaseObject();
* object.id = 'DEVICE_007';
* // @expect(object.id == 'DEVICE_007')
* @public
*/
get id() {
return this._id;
}
set id(value) {
this._id = value;
Selector.updateObjectAttribute(this, 'id', value);
}
/**
* 设置/获取 物体的唯一标识
* @type {string}
* @example
* let object = new THING.BaseObject({uuid: 10000});
* // @expect(object.uuid == 10000)
* object.uuid = THING.Math.generateUUID();
* // @expect(object.id != 10000)
* @public
*/
get uuid() {
return this._uuid;
}
set uuid(value) {
if (this._uuid != value) {
this.app.objectManager.removeObject(this);
this._uuid = value;
this.app.objectManager.addObject(this);
Selector.updateObjectAttribute(this, 'uuid', value);
}
}
/**
* 设置/获取 物体标签
* @type {Set<string>}
* @example
* // Get tags
* let tags = object.tags;
* console.log(tags);
*
* // Set tags by array
* object.tags = ['one','two','three'];
*
* // Set tags by set
* object.tags = new Set(['one','two','three']);
* @public
*/
get tags() {
let _private = this[__.private];
_private.tags = _private.tags || new Tags();
return _private.tags;
}
set tags(value) {
if (Utils.isArray(value) || Utils.isSet(value)) {
const tags = this.tags;
tags.clear();
value.forEach(element => {
tags.add(element);
});
}
}
/**
* 设置/获取 物体的自定义属性
* @type {object}
* @example
* let object = new THING.BaseObject();
* object.userData['Notebook'] = {
* name: 'FlyingCar',
* price: 100
* };
* let name = object.userData['Notebook'].name;
* // @expect(name == 'FlyingCar')
* let price = object.userData['Notebook'].price
* // @expect(price == 100)
* @public
*/
get userData() {
let userData = this._getUserData();
if (userData.isObjectProxy) {
return userData.dataProxy;
}
else {
return userData;
}
}
set userData(value) {
let userData = this._getUserData();
if (userData.isObjectProxy) {
userData.copy(value);
}
else {
Utils.copyObject(userData, value);
}
}
get internalData() {
return this._getInternalData();
}
set internalData(value) {
let internalData = this.internalData;
Utils.copyObject(internalData, value);
}
// #endregion
// #region Inherit
hasInherit() {
return false;
}
// #region Traverse
traverseInheritance(callback, onGetInheritance, jumpSelf) {
if (!jumpSelf) {
callback(this);
}
if (this.hasChildren()) {
let children = this.children;
for (let i = 0, l = children.length; i < l; i++) {
let child = children[i];
let inheritance = onGetInheritance(child);
switch (inheritance) {
case InheritType.Normal:
child.traverseInheritance(callback, onGetInheritance);
break;
case InheritType.Break:
callback(child);
break;
case InheritType.Jump:
child.traverseInheritance(callback, onGetInheritance, true);
break;
case InheritType.Stop:
break;
default:
break;
}
}
}
}
// #endregion
// #endregion
// #region State
/**
* Get queryable state.
* @returns {boolean} 返回一个布尔值,表示是否可以查询
* @private
* @example
* let object = new THING.BaseObject();
* let ret = object.getQueryable();
* // @expect(ret == false);
*/
getQueryable() {
return this._flags.has(Flag.Queryable);
}
/**
* Set queryable state.
* @param {boolean} value The state value.
* @param {boolean} recursive True indicates process it with all children.
* @private
* @example
* let object = new THING.BaseObject();
* object.setQueryable(true);
* // @expect(object.getQueryable() == true);
*/
setQueryable(value, recursive) {
// Update queryable flag
this._flags.enable(Flag.Queryable, value);
// Sync background selector's flag
Selector.updateObjectAttribute(this, 'queryable', value);
// Change all children if needed
if (recursive) {
let _private = this[__.private];
if (_private.children) {
let children = _private.children;
for (let i = 0, l = children.length; i < l; i++) {
children[i].setQueryable(value, true);
}
}
}
}
/**
* Get destroyable state.
* @returns {boolean} 返回一个布尔值,表示是否可以销毁
* @private
* @example
* let object = new THING.BaseObject();
* let ret = object.getDestroyable();
* // @expect(ret == true);
*/
getDestroyable() {
return this._flags.has(Flag.Destroyable);
}
/**
* Set destroyable state.
* @param {boolean} value The state value.
* @param {boolean} recursive True indicates process it with all children.
* @private
* @example
* let object = new THING.BaseObject();
* object.setDestroyable(false);
* // @expect(object.getDestroyable() == false);
*/
setDestroyable(value, recursive) {
// Update destroyable flag
this._flags.enable(Flag.Destroyable, value);
// Change all children if needed
if (recursive) {
let _private = this[__.private];
if (_private.children) {
let children = _private.children;
for (let i = 0, l = children.length; i < l; i++) {
children[i].setDestroyable(value, true);
}
}
}
}
/**
* Make object to call onRefresh() interface later.
* @param {boolean} force 是否强制刷新
* @example
* let object = new THING.BaseObject();
* object.setDirty(true);
* // @expect(object.onRefresh() == true);
* @private
*/
setDirty(force) {
let flags = this._flags;
if (flags.has(Flag.Dirty)) {
return;
}
flags.enable(Flag.Dirty, true);
if (force) {
this.onRefresh();
}
else {
this.app.objectManager.addDirtyObject(this);
}
}
/**
* 销毁物体
* @returns {boolean} 返回一个布尔值,表示是否销毁成功
* @example
* let object = new THING.BaseObject();
* // @expect(object.destroyed == false);
* object.destroy();
* // @expect(object.destroyed == true)
* @public
*/
destroy() {
if (this.onBeforeDestroy() === false) {
return false;
}
this.onDestroy();
this.onAfterDestroy();
return true;
}
/**
* Get flags.
* @type {object}
* @private
*/
get flags() {
return this._flags;
}
/**
* Get destroyed state.
* @type {boolean}
* @private
*/
get destroyed() {
return this._flags.has(Flag.Destroyed);
}
/**
* 设置/获取 对象是否可以被销毁
* @type {boolean}
* @example
* let object = new THING.BaseObject();
* object.destroyable = true;
* // @expect(object.destroyable == true)
* @public
*/
get destroyable() {
return this._flags.has(Flag.Destroyable);
}
set destroyable(value) {
this.setDestroyable(value, true);
}
/**
* 设置/获取 对象是否可以被查询
* @type {boolean}
* @example
* let object = new THING.BaseObject();
* object.name = 'Hidden';
* let ret = app.query('Hidden');
* // @expect(ret[0].name = 'Hidden')
* object.queryable = false;
* ret = app.query('Hidden');
* // @expect(ret.length = 0)
* @public
*/
get queryable() {
return this._flags.has(Flag.Queryable);
}
set queryable(value) {
this.setQueryable(value, true);
}
// #endregion
// #region Event
/**
* Check whether has specified event listener.
* @param {string} type The event type.
* @param {string} tag The event tag.
* @returns {boolean} 返回一个布尔值,表示是否存在指定的事件
* @private
* @example
* let object = new THING.BaseObject();
* let ret = object.hasEvent('click', 'MyTag');
* // @expect(ret == false);
*/
hasEvent(type, tag) {
_eventListeners.length = 0;
let events = this.app.eventManager.getEventListeners(type.toLowerCase(), this, _eventListeners);
for (let i = 0; i < events.length; i++) {
let event = events[i];
if (event.removed) {
continue;
}
if (tag !== event.tag) {
continue;
}
return true;
}
return false;
}
/**
* @typedef {object} ObjectListenerInfo
* @property {string} type 事件类型。
* @property {string} condition 选择对象的条件。
* @property {Function} callback 事件回调函数。
* @property {string} tag 事件标签。
* @property {number} priority 事件优先级。
* @property {boolean} once 表示只触发一次的事件。
* @property {boolean} paused 表示事件是否已暂停。
* @public
*/
/**
* 获取指定类型的事件。
* @param {string} type 事件类型,为null表示获取所有事件。
* @returns {Array<ObjectListenerInfo>} 返回一个数组,包含所有指定类型的事件
* @public
* @example
* let object = new THING.BaseObject();
* let events = object.getEvents();
* // @expect(events.length > 0);
*/
getEvents(type) {
let eventManager = this.app.eventManager;
let events;
if (type) {
events = eventManager.getEventListeners(type.toLowerCase(), this);
}
else {
events = eventManager.getAllEventListeners(this);
}
let aliveEvents = [];
events.forEach(event => {
if (event.removed) {
return;
}
if (!event.enumerable) {
return;
}
aliveEvents.push(event);
});
return aliveEvents;
}
/**
* 获取事件。
* @param {string} type 事件类型。
* @param {string} condition 选择对象的条件。
* @param {string} tag 事件标签。
* @returns {ObjectListenerInfo} 返回一个对象,包含指定类型的事件
* @private
* @example
* let object = new THING.BaseObject();
* let event = object.getEvent('click', '.Box', 'MyTag');
* // @expect(event != null);
*/
getEvent(type, condition, tag) {
type = type.toLowerCase();
let events = this.getEvents();
for (let i = 0; i < events.length; i++) {
let event = events[i];
if (event.type != type) {
continue;
}
if (event.condition != condition) {
continue;
}
if (event.tag != tag) {
continue;
}
return event;
}
return null;
}
/**
* @public
* @typedef {object} ObjectEventOptions
* @property {boolean} once 是否只触发一次
* @property {boolean} useCapture 如果是true,会阻断孩子身上原有的事件,并给所有孩子注册这个事件
* @property {boolean} propagate 是否传播事件
*/
/**
* 注册事件
* @param {string} type 事件类型
* @param {string} condition 注册事件前先进行查询,对符合条件的对象进行注册
* @param {EventCallback} callback 事件回调函数
* @param {string} tag 事件的tag
* @param {number} priority 优先级 默认0
* @param {ObjectEventOptions} options 事件参数
* @example
* let object = new THING.BaseObject();
* let mark = 0;
* object.on('click', function(ev){
* mark = 1;
* }, 'MyClick');
* object.trigger('click');
* // @expect(mark == 1)
* let mark2 = 0;
* object.on('click', '.Box', function(ev){
* mark2 = 1;
* }, 'MyClick');
* // @expect(mark2 == 0)
* @public
*/
on(type, condition, callback, tag, priority, options) {
this._addEventListener(type, condition, callback, tag, priority, options, false);
}
/**
* 注册一次性的事件,此事件响应只会触发一次,触发后会被自动注销
* @param {string} type 事件类型
* @param {string} condition 注册事件前先进行查询,对符合条件的对象进行注册
* @param {EventCallback} callback 事件回调函数
* @param {string} tag 事件的tag
* @param {number} priority 优先级 默认0
* @param {ObjectEventOptions} options 事件参数
* @example
* let object = new THING.BaseObject();
* let markOnce = 0;
* object.once('testOnce', function(ev) {
* markOnce = 1;
* });
* object.trigger('testOnce');
* // @expect(markOnce == 1);
* markOnce = 0;
* object.trigger('testOnce');
* // @expect(markOnce == 0);
* @public
*/
once(type, condition, callback, tag, priority, options) {
this._addEventListener(type, condition, callback, tag, priority, options, true);
}
/**
* 注销事件
* @param {string} type 事件类型
* @param {string} condition 注册事件的条件
* @param {string} tag 事件的tag
* @example
* let object = new THING.BaseObject();
* let markOff = 0;
* object.on('testOff', function(ev) {
* markOff = 1;
* });
* object.trigger('testOff');
* // @expect(markOff == 1);
* markOff = 0;
* object.off('testOff');
* object.trigger('testOff');
* // @expect(markOff == 0);
* @public
*/
off(type, condition, tag) {
// Use condition as tag
if (Utils.isString(condition) && Utils.isNull(tag)) {
this._removeEventListener(type, null, condition);
}
// Use both condition and tag
else {
this._removeEventListener(type, condition, tag);
}
}
/**
* 暂停事件
* @param {string} type 事件类型
* @param {string} condition 注册事件的条件
* @param {string} tag 事件的tag
* @example
* let object = new THING.BaseObject();
* let markPause = 0;
* object.on('testPause', function(ev) {
* markPause = 1;
* });
* object.trigger('testPause');
* // @expect(markPause == 1);
* markPause = 0;
* object.pauseEvent('testPause');
* object.trigger('testPause');
* // @expect(markPause == 0);
* app.on('test', ".Box", (e) => {
* console.log(e.object)
* }, 'MyTag');
* app.pauseEvent('test', '.Box', 'MyTag');
* @public
*/
pauseEvent(type, condition, tag) {
this.app.eventManager.pauseEvent(this, type, condition, tag);
}
/**
* 恢复事件
* @param {string} type 事件类型
* @param {string} condition 注册事件的条件
* @param {string} tag 事件的tag
* @example
* let object = new THING.BaseObject();
* let markResume = 0;
* object.on('testResume', function(ev) {
* markResume = 1;
* });
* object.trigger('testResume');
* // @expect(markResume == 1);
* markResume = 0;
* object.pauseEvent('testResume');
* object.trigger('testResume');
* // @expect(markResume == 0);
* object.resumeEvent('testResume');
* object.trigger('testResume');
* // @expect(markResume == 1);
* app.on('test', ".Box", (e) => {
* console.log(e.object)
* }, 'MyTag');
* app.pauseEvent('test', '.Box', 'MyTag');
* app.resumeEvent('test', '.Box', 'MyTag');
* @public
*/
resumeEvent(type, condition, tag) {
this.app.eventManager.resumeEvent(this, type, condition, tag);
}
/**
* 抛出事件
* @param {string} type 事件类型。
* @param {string|object} condition 事件条件 或 事件数据。
* @param {object|string} data 事件数据 或 事件标签。
* @param {string} tagName 事件标签名称。
* @returns {*} 返回一个对象,包含指定类型的事件
* @example
* let object = new THING.BaseObject();
* let markTrigger = {};
* object.on('myEvent', function(ev) {
* markTrigger = ev;
* });
* object.trigger('myEvent', { result: true });
* let ret = markTrigger.result;
* // @expect(ret == true);
*
* new THING.Box();
*
* app.on('test', (e) => {
* console.log('无条件+tag1', e)
* }, 'MyTag1');
* app.on('test', (e) => {
* console.log('无条件+tag2', e)
* }, 'MyTag2');
*
* app.on('test', ".Box", (e) => {
* console.log('条件+tag1', e)
* }, 'MyTag1');
* app.on('test', ".Box", (e) => {
* console.log('条件+tag2', e)
* }, 'MyTag2');
*
* // 事件类型 + 事件条件,触发事件。
* app.trigger('test', '.Box');
* // 事件类型 + 事件数据,触发事件
* app.trigger('test', { result: true });
* // 事件类型 + 事件条件 + 事件标签,触发事件
* app.trigger('test', '.Box', 'MyTag2');
* // 事件类型 + 事件条件 + 事件参数,触发事件
* app.trigger('test', '.Box', { result: true });
* // 事件类型 + 事件条件 + 事件数据 + 事件标签,触发事件
* app.trigger('test', '.Box', { result: true },'MyTag2');
*
* // 注意事项:
* // 1. 事件标签不能单独使用,否则事件标签和事件条件无法区分。
* app.trigger('test', null, 'MyTag2');
* // 2. 事件数据兼容了数组的情况,如果传了数组将无法从参数中获取object。
* app.trigger('test', '.Box', [{ result: 'r1' },{ result: 'r2' }], 'MyTag2')
* @public
*/
trigger(type, condition, data, tagName) {
if (this.destroyed) return null;
// condition 为null时,设置为空字符串
if (condition == null) {
condition = '';
}
// 参数解析逻辑
let eventCondition = null;
let eventData = {};
let eventOptions = {};
if (typeof condition === 'object') {
// condition 为对象时,表示事件数据。data可能是tag (用typeof判断,兼容数组的情况)
eventData = condition;
if (Utils.isString(data)) {
eventOptions.tag = data;
}
}
else if (typeof data === 'object') {
// data 为对象时,表示事件数据。condition为条件 tagName为tag (用typeof判断,兼容数组的情况)
eventCondition = condition;
eventData = data;
if (Utils.isString(tagName)) {
eventOptions.tag = tagName;
}
}
else {
// condition 和 data 都不是对象,那么 condition 为条件,如果data也是字符串data为tag
eventCondition = condition;
if (Utils.isString(data)) {
eventOptions.tag = data;
}
}
if (eventCondition) {
const result = this.query(eventCondition);
return result.trigger(type, eventData, eventOptions);
}
else {
return this.app.eventManager.dispatchEvent(
type.toLowerCase(),
this,
eventData,
eventOptions
);
}
}
/**
* 触发并等待事件完成
* @param {string} type 事件类型。
* @param {string|object} condition 事件条件 或 事件数据。
* @param {object|string} data 事件数据 或 事件标签。
* @param {string} tagName 事件标签名称。
* @returns {*} 返回一个Promise对象,包含指定类型的事件
* @public
*/
async invoke(type, condition, data, tagName) {
if (this.destroyed) return null;
// 参数解析逻辑
let eventCondition = null;
let eventData = {};
let eventOptions = {};
if (typeof condition === 'object') {
// condition 为对象时,表示事件数据。data可能是tag (用typeof判断,兼容数组的情况)
eventData = condition;
if (Utils.isString(data)) {
eventOptions.tag = data;
}
}
else if (typeof data === 'object') {
// data 为对象时,表示事件数据。condition为条件 tagName为tag (用typeof判断,兼容数组的情况)
eventCondition = condition;
eventData = data;
if (Utils.isString(tagName)) {
eventOptions.tag = tagName;
}
}
else {
// condition 和 data 都不是对象,那么 condition 为条件,如果data也是字符串data为tag
eventCondition = condition;
if (Utils.isString(data)) {
eventOptions.tag = data;
}
}
if (eventCondition) {
const result = this.query(eventCondition);
return result.invoke(type, eventData, eventOptions);
}
else {
return this.app.eventManager.invokeEvent(
type.toLowerCase(),
this,
eventData,
eventOptions
);
}
}
/**
* 设置事件过滤器
* @param {string} type 事件类型。
* @param {string} condition 事件条件。
* @param {object} tag 事件标签。
* @param {Function} callback 事件回调函数。
*/
addEventFilter(type, condition, callback, tag) {
if (this.destroyed) return null;
let info = Utils.parseEvent(type, condition, callback, tag);
this.app.eventManager.addEventFilter(
this,
info.type,
info.condition,
info.tag,
info.callback
);
}
/**
* 移除事件过滤器
* @param {string} type 事件类型。
* @param {string} condition 事件条件
* @param {ostring} tag 事件标签
*/
removeEventFilter(type, condition, tag) {
if (this.destroyed) return null;
let callback = () => { }
let info = Utils.parseEvent(type, condition, callback, tag);
this.app.eventManager.removeEventFilter(
this,
info.type,
info.condition,
info.tag
);
}
/**
* 移除所有事件过滤器
*/
removeAllEventFilters() {
if (this.destroyed) return null;
this.app.eventManager.removeAllEventFilters(this);
}
/**
* Get proxy object. 获取代理对象
* @returns {THING.BaseObject} 返回一个代理对象
* @private
* @example
* let object = new THING.BaseObject();
* let proxyObject = object.getProxyObject();
* // @expect(proxyObject != null);
*/
getProxyObject() {
let _private = this[__.private];
return _private.proxyObject;
}
/**
* 设置代理对象
* </br>设置代理对象(它将捕获该对象的所有事件)。
* @param {THING.BaseObject} object The object.
* @example
* let car = app.query('#car01')[0];
* object.setProxyObject(car); // The car will catch all events of object
* @private
*/
setProxyObject(object) {
let _private = this[__.private];
_private.proxyObject = object;
}
triggerError(error, callback) {
if (typeof error != 'object') {
error = { error }
}
this.trigger(EventType.Error, error);
Utils.error(error);
callback && callback(error);
}
/**
* 获取当前对象注册的所有事件监听器的详细列表。
* 该属性返回一个 ObjectListenerInfo 类型的数组,每一项包含一个事件监听器的全部信息。
*
* ObjectListenerInfo 格式参考(常用字段如下):
* - name: 事件名称(一般为小写,如 "click", "update", "mouseenter" 等)
* - callback: 回调函数,事件触发时调用
* - context: 回调函数的上下文对象
* - once: 是否为一次性监听(只会触发一次,之后移除)
* - filter: 事件过滤函数(可选)
* - id: 事件监听唯一标识(可选)
* - other: 可能包含的其他参数或元数据
* @type {Array<ObjectListenerInfo>}
* @public
* @example
* let object = new THING.BaseObject();
* object.on('click', function() { console.log('clicked'); });
* // 获取所有事件监听器
* let allEvents = object.events;
* // @expect(Array.isArray(allEvents));
* // @expect(allEvents.length >= 0);
* // allEvents 结果类似:
* // [{
* // name: 'click',
* // callback: ƒ,
* // context: object,
* // once: false,
* // filter: undefined,
* // id: 1
* // }, ...]
*/
get events() {
return this.getEvents();
}
// #endregion
// #region Relative
/**
* When set the parent.
* @param {THING.BaseObject} parent The parent.
* @param {object} options 选项
* @private
* @example
* let object = new THING.BaseObject();
* let parent = new THING.BaseObject();
* object.onSetParent(parent);
* // @expect(object.parent == parent);
*/
onSetParent(parent, options) {
let _private = this[__.private];
// Prevent for the same parent
if (parent == _private.parent) {
return;
}
// Attach to new parent
if (parent) {
parent.addChild(this, options);
}
// Remove it from previous parent
this._removeFromParent();
// Update current parent
_private.parent = parent;
}
/**
* Get the parent.
* @returns {THING.BaseObject} 返回一个父对象
* @private
* @example
* let object = new THING.BaseObject();
* let parent = new THING.BaseObject();
* object.onSetParent(parent);
* // @expect(object.parent == parent);
*/
getParent() {
return this[__.private].parent;
}
/**
* Get the parnet by type.
* @param {string} type The type string.
* @returns {THING.BaseObject} 返回一个父对象
* @private
* @example
* let object = new THING.BaseObject();
* let parent = new THING.BaseObject();
* object.onSetParent(parent);
* // @expect(object.parent == parent);
*/
getParentByType(type) {
let typeString = 'is' + type;
for (let parent = this.parent; parent; parent = parent.parent) {
if (parent[typeString]) {
return parent;
}
}
return null;
}
/**
* Get the parents.
* @param {object} param The parameters.
* @param {THING.BaseObject} param.root The root to stop.
* @param {boolean} param.includeSelf True indicates including self object.
* @returns {THING.Selector} 返回一个选择器
* @private
* @example
* let object = new THING.BaseObject();
* let parents = object.getParents();
* // @expect(parents.length > 0);
*/
getParents(param = _defaultParams) {
let _private = this[__.private];
let root = param['root'];
let includeSelf = param['includeSelf'];
let selector = new Selector();
if (includeSelf) {
selector.push(this);
}
let parent = _private.parent;
while (parent) {
if (parent == root) {
break;
}
selector.push(parent);
parent = parent.parent;
}
return selector;
}
/**
* Get the brothers except self.
* @returns {THING.Selector} 返回一个选择器
* @private
* @example
* let object = new THING.BaseObject();
* let brothers = object.getBrothers();
* // @expect(brothers.length > 0);
*/
getBrothers() {
let _private = this[__.private];
let parent = _private.parent;
if (parent) {
return parent.children.filter(object => {
if (object === this) {
return false;
}
return true;
});
}
return new Selector();
}
/**
* Get the path to specific object.
* @param {THING.BaseObject} object The object to check.
* @returns {THING.Selector} 返回一个选择器
* @private
* @example
* let object = new THING.BaseObject();
* let path = object.getPathTo(object);
* // @expect(path.length > 0);
*/
getPathTo(object) {
if (!object) {
return null;
}
if (this.isChildOf(object)) {
let path = this.getParents({ root: object });
path.push(object);
return path;
}
else if (object.isChildOf(this)) {
let path = object.getParents({ root: this }).reverse();
path.push(object);
return path;
}
else {
// Get all parents
let targetParents = object.parents.reverse();
let selfParents = this.parents.reverse();
// Check whether it has parents
if (targetParents.length && selfParents.length) {
// Check whether it come from the same root
if (targetParents[0] == selfParents[0]) {
// Find the link parent
let linkParent;
while (targetParents.length && selfParents.length) {
if (targetParents[0] != selfParents[0]) {
break;
}
linkParent = selfParents[0];
targetParents.splice(0, 1);
selfParents.splice(0, 1);
}
if (linkParent) {
return selfParents.reverse().concat([linkParent]).concat(targetParents).concat(object);
}
}
}
}
return null;
}
/**
* Check whether it's child.
* @param {THING.BaseObject} object The object to check.
* @returns {boolean} 返回一个布尔值,表示是否是子对象
* @example
* let parent = new THING.BaseObject();
* let child = new THING.BaseObject({parent: parent});
* let ret = child.isChildOf(parent);
* // @expect(ret == true);
*/
isChildOf(object) {
if (!object) {
return false;
}
let parent = this.parent;
while (parent) {
if (parent == object) {
return true;
}
parent = parent.parent;
}
return false;
}
/**
* Check whether it's brother.
* @param {THING.BaseObject} object The object to check.
* @returns {boolean} 返回一个布尔值,表示是否是兄弟对象
* @example
* let parent = new THING.BaseObject();
* let child1 = new THING.BaseObject({parent: parent});
* let child2 = new THING.BaseObject({parent: parent});
* let ret = child1.isBrotherOf(child2);
* // @expect(ret == true);
*/
isBrotherOf(object) {
if (!object) {
return false;
}
if (this == object) {
return false;
}
if (this.parent != object.parent) {
return false;
}
return true;
}
addChild(object, options) {
let _private = this[__.private];
_private.children = _private.children || new Selector();
if (options && Utils.isNumber(options.indexOfParent)) {
_private.children.splice(options.indexOfParent, 0, object);
}
else {
_private.children.push(object);
}
}
/**
* 添加一个BaseObject作为自己的孩子
* @param {THING.BaseObject} object 待添加的BaseObject
* @param {object} options 参数 主要是子类定义
* @returns {boolean} 返回一个布尔值,表示是否添加成功
* @example
* let parent = new THING.BaseObject();
* let child = new THING.BaseObject();
* parent.add(child);
* let ret = child.isChildOf(parent);
* // @expect(ret == true);
* @public
*/
add(object, options) {
if (!object) {
return false;
}
if (!object.isBaseObject) {
options = object;
object = options.object;
delete options.object;
}
let parent = object.parent;
if (parent) {
// The final parent is passed in as an argument.
object.onBeforeSetParent(this);
parent.remove(object, options);
}
this.onBeforeAddChild(object);
this.onAddChild(object, options);
this.onAfterAddChild(object);
return true;
}
/**
* 从自己的孩子中移除BaseObject
* @param {THING.BaseObject} object 待移除物体
* @param {object} options 选项
* @returns {boolean} 返回一个布尔值,表示是否移除成功
* @example
* let parent = new THING.BaseObject();
* let child = new THING.BaseObject();
* parent.add(child);
* let ret = child.isChildOf(parent);
* // @expect(ret == true);
* parent.remove(child);
* ret = child.isChildOf(parent);
* // @expect(ret == false);
* @public
*/
remove(object, options) {
if (!object) {
return false;
}
this.onBeforeRemoveChild(object);
object.onSetParent(null, null, options);
this.onAfterRemoveChild(object);
return true;
}
/**
* 递归遍历自己和自己的孩子 这个方法会遍历到所有的后代节点(孩子,孩子的孩子等)
* @param {Function} callback The callback function. 执行遍历时执行的方法
* @example
* let parent = new THING.BaseObject();
* let child1 = new THING.BaseObject({parent: parent});
* let child2 = new THING.BaseObject({parent: parent});
* let mark = 0;
* parent.traverse((child) => {
* mark++;
* });
* // @expect(mark == 3)
* @public
*/
traverse(callback) {
callback(this);
if (this.children) {
let objects = this.children.objects;
for (let i = 0, l = objects.length; i < l; i++) {
objects[i].traverse(callback);
}
}
}
/**
* Traverse self and all children. (Support for exit at traverse runtime)
* @param {Function} callback The callback function. (Return false to exit)
* @returns {boolean} 返回一个布尔值,表示是否继续遍历
* @example
* let parent = new THING.BaseObject();
* let child1 = new THING.BaseObject({parent: parent});
* let child2 = new THING.BaseObject({parent: parent});
* let mark = 0;
* parent.traverseBranch((child)=>{
* mark++;
* });
* // @expect(mark == 3)
* mark = 0;
* parent.traverseBranch((child)=>{
* mark++;
* if(child.children.length > 0){
* return false;
* }
* return true;
* });
* // @expect(mark == 1)
*/
traverseBranch(callback) {
let isBranch = Utils.parseBoolean(callback(this), true);
if (!isBranch) {
return false;
}
let _private = this[__.private];
let children = _private.children;
if (children) {
for (let i = 0, l = children.length; i < l; i++) {
isBranch = children[i].traverseBranch(callback);
if (!isBranch) {
return false;
}
}
}
return true;
}
/**
* Traverse all parents except self.
* @param {Function} callback The callback function.
* @deprecated Deprecated, call with the selector returned by the object's parent interface
* @private
* @example
* let object = new THING.BaseObject();
* let parent = new THING.BaseObject();
* object.onSetParent(parent);
* // @expect(object.parent == parent);
*/
traverseParents(callback) {
console.warn('Plase use Selector.tarverse instead of tarverseParents');
for (let parent = this.parent; parent; parent = parent.parent) {
callback(parent);
}
}
/**
* Traverse all children except self.
* @param {Function} callback The callback function.
* @deprecated Deprecated, call with the selector returned by the object's children interface
* @private
* @example
* let object = new THING.BaseObject();
* let parent = new THING.BaseObject();
* object.onSetParent(parent);
* // @expect(object.parent == parent);
*/
traverseChildren(callback) {
console.warn('Plase use Selector.tarverse instead of traverseChildren');
let _private = this[__.private];
if (_private.children) {
let children = _private.children;
for (let i = 0, l = children.length; i < l; i++) {
children[i].traverse(callback);
}
}
}
/**
* Find all children except self.
* @param {Function} callback The callback function.
* @returns {THING.BaseObject} 返回一个物体
* @private
* @example
* let object = new THING.BaseObject();
* let parent = new THING.BaseObject();
* object.onSetParent(parent);
* // @expect(object.parent == parent);
*/
findChildren(callback) {
let _private = this[__.private];
let children = _private.children;
if (children) {
for (let i = 0, l = children.length; i < l; i++) {
let child = children[i];
if (callback(child) === true) {
return child;
}
let result = child.findChildren(callback);
if (result) {
return result;
}
}
}
return null;
}
/**
* Check whether has any children.
* @returns {boolean} 返回一个布尔值,表示是否存在孩子
* @example
* let parent = new THING.BaseObject();
* let child = new THING.BaseObject({parent: parent});
* let ret = parent.hasChildren();
* // @expect(ret == true);
*/
hasChildren() {
let _private = this[__.private];
if (!_private.children) {
return false;
}
if (!_private.children.length) {
return false;
}
return true;
}
addRelationship(value) {
this.relationship.addRelationship(value);
}
removeRelationship(value) {
this.relationship.removeRelationship(value);
}
/**
* 设置/获取 父物体
* @type {THING.BaseObject}
* @example
* let parent = new THING.BaseObject();
* let child = new THING.BaseObject({parent: parent});
* let ret = child.parent == parent;
* // @expect(ret == true);
* @public
*/
get parent() {
return this[__.private].parent;
}
set parent(value) {
if (value) {
value.add(this);
}
else if (this.parent) {
this.parent.remove(this);
}
}
/**
* 设置/获取 关系
* @type {Array<THING.Relationship>}
* @example
* let object = new THING.Object3D();
* let source = new THING.Object3D();
* let target = new THING.Object3D();
* let relationship = new THING.Relationship({
* type: 'control',
* source: source,
* target: target
* });
* object.addRelationship(relationship);
* let ret = object.relationships[0].type == 'control';
* // @expect(ret == true)
* @public
*/
get relationships() {
return this.relationship.relationships || [];
}
/**
* 获取所有的祖先(父物体,父物体的父物体等,直到查询到根节点--app.root)
* @type {THING.Selector}
* @example
* let box1 = new THING.Box();
* let box2 = new THING.Box({parent: box1});
* let parents = box2.parents;
* // @expect(parents.length == 2);
* @public
*/
get parents() {
return this.getParents();
}
/**
* 获取兄弟节点(同级节点)
* @type {THING.Selector}
* @example
* let box1 = new THING.Box();
* let box2 = new THING.Box({parent: box1});
* let box3 = new THING.Box({parent: box1});
* let brothers = box3.brothers;
* // @expect(brothers.length == 1);
* @public
*/
get brothers() {
return this.getBrothers();
}
/**
* 获取孩子
* @type {THING.Selector}
* @example
* let object = new THING.BaseObject();
* let child= new THING.BaseObject({parent: object});
* let children = object.children;
* let ret = children.length == 1;
* // @expect(ret == true)
* @public
*/
get children() {
let _private = this[__.private];
return _private.children || _dummySelector;
}
// #endregion
// #region Selector
/**
* Test whether fit condition or not.
* @param {string} condition The condition to check.
* @returns {boolean} 返回一个布尔值,表示是否满足条件
* @private
* @example
* let object = new THING.BaseObject();
* let ret = object.test('name == "liming"');
* // @expect(ret == true);
*/
test(condition) {
return Selector.test(condition, this);
}
/**
* @public
* @typedef {object} ObjectQueryOptions
* @property {boolean} recursive 是否递归查询 默认true
* @property {boolean} includeSelf 是否包括自身 默认不包含
*/
/**
* 根据条件查询孩子 返回所有满足条件的对象集合
* @param {ObjectQueryOptions|string} condition 查询条件
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child= new THING.BaseObject({parent: object});
* child.userData = {power: 1000};
* let children = object.children.query('[userData/power>100]');
* let ret = children.length == 1;
* // @expect(ret == true)
* @public
*/
query(condition) {
let options = {};
if (Utils.isObject(arguments[1])) {
options = arguments[1];
}
else if (Utils.isBoolean(arguments[1])) {
// 兼容1.0的写法
options = { recursive: arguments[1] };
}
if (Utils.isBoolean(arguments[2])) {
// 兼容1.0的写法
options['includeSelf'] = arguments[2];
}
options['app'] = this.app;
let recursive = Utils.parseValue(options['recursive'], true);
let includeSelf = options['includeSelf'];
let selector = options['dynamic'] ? new DynamicSelector(this, condition, options) : new Selector();
if (this.isRootObject && recursive) {
selector.query(condition, this.app.objectManager.objects, selector);
}
else {
if (includeSelf) {
if (this.test(condition)) {
selector.push(this);
}
}
if (recursive) {
if (Utils.isString(condition)) {
// All
var objs = [];
if (condition == '*') {
this.children.traverse(child => {
if (child.queryable) {
objs.push(child)
}
});
}
// Expression
else if (this._isExpression(condition)) {
let expression = Selector.buildExpression(condition);
if (expression) {
this.children.traverse(child => {
if (Selector.testByExpression(expression, child) && child.queryable) {
objs.push(child);
}
});
}
}
// Compare name
else {
this.children.traverse(child => {
if (child.name == condition && child.queryable) {
objs.push(child);
}
});
}
selector.push(objs);
}
// Reg Expression
else if (Utils.isRegExp(condition)) {
if (this.children) {
var objs = [];
this.children.traverse(child => {
if (Selector.testByRegExpName(condition, child) && child.queryable) {
objs.push(child);
}
});
selector.push(objs);
}
}
// Function
else if (Utils.isFunction(condition)) {
if (this.children) {
var objs = [];
this.children.traverse(child => {
if (Selector.testByFunction(condition, child) && child.queryable) {
objs.push(child);
}
});
selector.push(objs);
}
}
}
else {
if (this.children) {
selector.push(this.children.query(condition));
}
}
}
return selector;
}
/**
* 根据条件查询孩子 返回所有满足条件的对象集合(忽略 queryable 属性)
* @param {ObjectQueryOptions|string} condition 查询条件
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child= new THING.BaseObject({parent: object});
* child.userData = {power: 1000};
* let children = object.children.queryAll('[userData/power>100]');
* let ret = children.length == 1;
* // @expect(ret == true)
* @public
*/
queryAll(condition) {
let options = {};
if (Utils.isObject(arguments[1])) {
options = arguments[1];
}
else if (Utils.isBoolean(arguments[1])) {
// 兼容1.0的写法
options = { recursive: arguments[1] };
}
if (Utils.isBoolean(arguments[2])) {
// 兼容1.0的写法
options['includeSelf'] = arguments[2];
}
options['app'] = this.app;
let recursive = Utils.parseValue(options['recursive'], true);
let includeSelf = options['includeSelf'];
let selector = options['dynamic'] ? new DynamicSelector(this, condition, options) : new Selector();
if (this.isRootObject && recursive) {
selector.queryAll(condition, this.app.objectManager.objects, selector);
}
else {
if (includeSelf) {
if (this.test(condition)) {
selector.push(this);
}
}
if (recursive) {
if (Utils.isString(condition)) {
// All
var objs = [];
if (condition == '*') {
this.children.traverse(child => {
objs.push(child)
});
}
// Expression
else if (this._isExpression(condition)) {
let expression = Selector.buildExpression(condition);
if (expression) {
this.children.traverse(child => {
if (Selector.testByExpression(expression, child)) {
objs.push(child);
}
});
}
}
// Compare name
else {
this.children.traverse(child => {
if (child.name == condition) {
objs.push(child);
}
});
}
selector.push(objs);
}
// Reg Expression
else if (Utils.isRegExp(condition)) {
if (this.children) {
var objs = [];
this.children.traverse(child => {
if (Selector.testByRegExpName(condition, child)) {
objs.push(child);
}
});
selector.push(objs);
}
}
// Function
else if (Utils.isFunction(condition)) {
if (this.children) {
var objs = [];
this.children.traverse(child => {
if (Selector.testByFunction(condition, child)) {
objs.push(child);
}
});
selector.push(objs);
}
}
}
else {
if (this.children) {
selector.push(this.children.queryAll(condition));
}
}
}
return selector;
}
/**
* 根据名称查询孩子
* @param {string} condition 查询条件
* @param {ObjectQueryOptions} options 查询参数 主要包括是否递归查询和是否包含自身
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child= new THING.BaseObject({parent: object, name: 'liming'});
* let result = object.queryByName('liming');
* let ret = result[0].name == 'liming';
* // @expect(ret == true)
* @public
*/
queryByName(condition, options = {}) {
return this._queryByAttribute(this, options, (c) => { return c.name == condition });
}
/**
* 根据id查询孩子
* @param {string} condition 查询条件
* @param {ObjectQueryOptions} options 查询参数
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child1= new THING.BaseObject({parent: object});
* child1.id = '10000';
* let child2= new THING.BaseObject({parent: object});
* let result = object.queryById('10000');
* let ret = result[0].id == '10000';
* //@expect(ret == true)
* @public
*/
queryById(condition, options = {}) {
return this._queryByAttribute(this, options, (c) => { return c.id == condition });
}
/**
* 根据tag查询孩子
* @param {string} condition 查询条件
* @param {ObjectQueryOptions} options 查询参数
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child1= new THING.Object3D({parent: object, name: 'car1'});
* child1.tags.add('testCar');
* let child2= new THING.BaseObject({parent: object, name: 'car2'});
* let result = object.queryByTags('testCar');
* let ret = result.length == 1;
* //@expect(ret == true)
* @public
*/
queryByTags(condition, options = {}) {
return this._queryByCondition('tags(' + condition + ')', options, 'tags');
}
/**
* 根据uuid查询孩子
* @param {string} condition 查询条件
* @param {ObjectQueryOptions} options 查询参数
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child1= new THING.BaseObject({parent: object, uuid: '1000'});
* let child2= new THING.BaseObject({parent: object});
* let result = object.queryByUUID('1000');
* let ret = result[0].uuid == '1000';
* //@expect(ret == true)
* @public
*/
queryByUUID(condition, options = {}) {
return this._queryByAttribute(this, options, (c) => { return c.uuid == condition });
}
/**
* 根据类型查询孩子
* @param {string} condition 查询条件
* @param {ObjectQueryOptions} options 查询参数
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child1= new THING.Box({parent: object, id: '10000'});
* let child2= new THING.BaseObject({parent: object});
* let result = object.queryByType('Box');
* let ret = result[0].id == '10000';
* //@expect(ret == true)
* @public
*/
queryByType(condition, options = {}) {
var key = "is" + condition;
return this._queryByAttribute(this, options, (c) => { return c[key] == true });
}
/**
* 根据孪生体类型查询孩子
* @param {string} condition 查询条件
* @param {ObjectQueryOptions} options 查询参数
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child1= new THING.BaseObject({parent: object});
* child1.dtType = 'Equipment';
* let child2= new THING.BaseObject({parent: object});
* let result = object.queryByDTType('Equipment');
* let ret = result[0].dtType == 'Equipment';
* //@expect(ret == true)
* @public
*/
queryByDTType(condition, options = {}) {
return this._queryByAttribute(this, options, (c) => { return c.dtType === condition });
}
/**
* 根据userData(用户数据)查询孩子
* @param {string} condition 查询条件
* @param {ObjectQueryOptions} options 查询参数
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child1= new THING.Box({parent: object});
* child1.userData['power'] = 100;
* let child2= new THING.BaseObject({parent: object});
* let result = object.queryByUserData('power=100');
* let ret = result[0].userData.power == 100;
* //@expect(ret == true)
* @public
*/
queryByUserData(condition, options = {}) {
return this._queryByCondition(condition, options, 'userData');
}
/**
* 根据userData查询孩子。
* @param {string} condition 查询条件。
* @param {ObjectQueryOptions} options 查询选项。
* @returns {THING.Selector} 查询结果。
* @example
* let result = object.queryByData('test=1');
* console.log(result);
* @deprecated 2.7
* @private
*/
queryByData(condition, options = {}) {
return this.queryByUserData(condition, options);
}
/**
* 根据正则表达式查询孩子
* @param {string} condition 正则表达式
* @param {ObjectQueryOptions} options 查询参数
* @returns {THING.Selector} 查询结果
* @example
* let object = new THING.BaseObject();
* let child1= new THING.BaseObject({parent: object, name: 'car1'});
* let child2= new THING.BaseObject({parent: object, name: 'car2'});
* let result = object.queryByRegExp(/car/);
* let ret = result.length == 2;
* //@expect(ret == true)
* @public
*/
queryByRegExp(condition, options = {}) {
return this.query(condition, options);
}
/**
* 根据正则表达式查询孩子。
* @param {string} condition 查询条件。
* @param {ObjectQueryOptions} options 查询选项。
* @returns {THING.Selector} 查询结果。
* @example
* let result = object.queryByReg(/Sphere/);
* console.log(result);
* @deprecated 2.7
* @private
*/
queryByReg(condition, options = {}) {
return this.queryByRegExp(condition, options);
}
_queryByAttribute(root, options, cb) {
let selectorOptions = Object.assign({}, options, { app: this.app });
let selector = options['dynamic'] ? new DynamicSelector(this, condition, selectorOptions) : new Selector(selectorOptions);
let recursive = Utils.parseValue(options['recursive'], true);
let includeSelf = options['includeSelf'];
// RootObject query from all objects
if (root.isRootObject && recursive) {
selector._queryByAttribute(this.app.objectManager.objects, cb, selector);
}
else {
let objects = [];
if (includeSelf) {
if (cb(root) && root.queryable) {
objects.push(root);
}
}
if (recursive) {
this.children.traverse(c => {
if (cb(c) && c.queryable) {
objects.push(c);
}
});
}
else {
objects = objects.concat(this.children.objects.filter((c) => { return cb(c) && c.queryable; }));
}
selector.push(objects);
}
return selector;
}
_queryByCondition(condition, options = {}, mode) {
let selectorOptions = Object.assign({}, options, { app: this.app });
let selector = options['dynamic'] ? new DynamicSelector(this, condition, selectorOptions) : new Selector(selectorOptions);
let recursive = Utils.parseValue(options['recursive'], true);
let includeSelf = options['includeSelf'];
var objects = [];
if (Utils.isString(condition)) {
let expression = Selector.buildExpression(condition, mode);
if (expression) {
// RootObject query from all objects
if (this.isRootObject && recursive) {
selector._queryByCondition(condition, this.app.objectManager.objects, selector, mode);
}
else {
if (includeSelf) {
if (Selector.testByExpression(expression, this) && this.queryable) {
objects.push(this);
}
}
if (recursive) {
this.children.traverse(child => {
if (Selector.testByExpression(expression, child) && child.queryable) {
objects.push(child);
}
});
}
else {
this.children.forEach((child) => {
if (Selector.testByExpression(expression, child) && child.queryable) {
objects.push(child);
}
})
}
}
}
}
selector.push(objects);
return selector;
}
/**
* 异步模式下根据条件查询孩子。
* @param {string} condition 查询条件。
* @param {ObjectQueryOptions} options 查询选项。
* @returns {Promise<any>} 返回一个Promise对象
* @private
* @example
* let object = new THING.BaseObject();
* let result = await object.queryAsync('name == "liming"');
* console.log(result);
*/
queryAsync(condition, options = {}) {
let recursive = Utils.parseValue(options['recursive'], true);
let includeSelf = options['includeSelf'];
return Selector.queryAsync(condition, this, recursive).then(selector => {
if (includeSelf) {
if (this.test(condition)) {
selector.push(this);
}
}
return selector;
});
}
/**
* 根据条件查找子对象。
* @param {string} condition 查找对象的条件。
* @param {ObjectQueryOptions} options 选项。
* @returns {THING.BaseObject} 返回一个物体
* @private
* @example
* let object = new THING.BaseObject();
* let child = new THING.BaseObject({parent: object});
* let result = object.find('name == "liming"');
* // @expect(result == child);
*/
find(condition, options = {}) {
let recursive = Utils.parseValue(options['recursive'], true);
let includeSelf = options['includeSelf'];
if (includeSelf) {
if (this.test(condition)) {
return this;
}
}
let _private = this[__.private];
if (recursive) {
if (Utils.isString(condition)) {
// All
if (condition == '*') {
return this.children[0];
}
else {
let expression = Selector.buildExpression(condition);
if (expression) {
return this.findChildren(child => {
return Selector.testByExpression(expression, child);
});
}
}
}
// Reg Expression
else if (Utils.isRegExp(condition)) {
if (_private.children) {
return this.findChildren(child => {
return Selector.testByRegExpName(condition, child);
});
}
}
// Function
else if (Utils.isFunction(condition)) {
if (_private.children) {
return this.findChildren(child => {
return Selector.testByFunction(condition, child);
});
}
}
}
else {
if (_private.children) {
return _private.children.find(condition);
}
}
return null;
}
/**
* 设置子在children中的索引
* @param {THING.BaseObject} child 子对象
* @param {number} index 索引
* @example
* let object = new THING.BaseObject();
* let child = new THING.BaseObject({parent: object});
* object.setChildIndex(child, 0);
* // @expect(object.children[0] == child);
*/
setChildIndex(child, index) {
const oriIndex = this.children.indexOf(child);
this.children.splice(oriIndex, 1);
this.children.insert(index, child);
}
// #endregion
get isBaseObject() {
return true;
}
get indexOfParent() {
return this.parent.children.indexOf(this);
}
}
export { BaseObject }