| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import { __DEV__ } from '../config'; |
| import * as zrUtil from 'zrender/src/core/util'; |
| var TYPE_DELIMITER = '.'; |
| var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___'; |
| /** |
| * Notice, parseClassType('') should returns {main: '', sub: ''} |
| * @public |
| */ |
| |
| export function parseClassType(componentType) { |
| var ret = { |
| main: '', |
| sub: '' |
| }; |
| |
| if (componentType) { |
| componentType = componentType.split(TYPE_DELIMITER); |
| ret.main = componentType[0] || ''; |
| ret.sub = componentType[1] || ''; |
| } |
| |
| return ret; |
| } |
| /** |
| * @public |
| */ |
| |
| function checkClassType(componentType) { |
| zrUtil.assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType "' + componentType + '" illegal'); |
| } |
| /** |
| * @public |
| */ |
| |
| |
| export function enableClassExtend(RootClass, mandatoryMethods) { |
| RootClass.$constructor = RootClass; |
| |
| RootClass.extend = function (proto) { |
| var superClass = this; |
| |
| var ExtendedClass = function () { |
| if (!proto.$constructor) { |
| superClass.apply(this, arguments); |
| } else { |
| proto.$constructor.apply(this, arguments); |
| } |
| }; |
| |
| zrUtil.extend(ExtendedClass.prototype, proto); |
| ExtendedClass.extend = this.extend; |
| ExtendedClass.superCall = superCall; |
| ExtendedClass.superApply = superApply; |
| zrUtil.inherits(ExtendedClass, this); |
| ExtendedClass.superClass = superClass; |
| return ExtendedClass; |
| }; |
| } |
| var classBase = 0; |
| /** |
| * Can not use instanceof, consider different scope by |
| * cross domain or es module import in ec extensions. |
| * Mount a method "isInstance()" to Clz. |
| */ |
| |
| export function enableClassCheck(Clz) { |
| var classAttr = ['__\0is_clz', classBase++, Math.random().toFixed(3)].join('_'); |
| Clz.prototype[classAttr] = true; |
| |
| Clz.isInstance = function (obj) { |
| return !!(obj && obj[classAttr]); |
| }; |
| } // superCall should have class info, which can not be fetch from 'this'. |
| // Consider this case: |
| // class A has method f, |
| // class B inherits class A, overrides method f, f call superApply('f'), |
| // class C inherits class B, do not overrides method f, |
| // then when method of class C is called, dead loop occured. |
| |
| function superCall(context, methodName) { |
| var args = zrUtil.slice(arguments, 2); |
| return this.superClass.prototype[methodName].apply(context, args); |
| } |
| |
| function superApply(context, methodName, args) { |
| return this.superClass.prototype[methodName].apply(context, args); |
| } |
| /** |
| * @param {Object} entity |
| * @param {Object} options |
| * @param {boolean} [options.registerWhenExtend] |
| * @public |
| */ |
| |
| |
| export function enableClassManagement(entity, options) { |
| options = options || {}; |
| /** |
| * Component model classes |
| * key: componentType, |
| * value: |
| * componentClass, when componentType is 'xxx' |
| * or Object.<subKey, componentClass>, when componentType is 'xxx.yy' |
| * @type {Object} |
| */ |
| |
| var storage = {}; |
| |
| entity.registerClass = function (Clazz, componentType) { |
| if (componentType) { |
| checkClassType(componentType); |
| componentType = parseClassType(componentType); |
| |
| if (!componentType.sub) { |
| storage[componentType.main] = Clazz; |
| } else if (componentType.sub !== IS_CONTAINER) { |
| var container = makeContainer(componentType); |
| container[componentType.sub] = Clazz; |
| } |
| } |
| |
| return Clazz; |
| }; |
| |
| entity.getClass = function (componentMainType, subType, throwWhenNotFound) { |
| var Clazz = storage[componentMainType]; |
| |
| if (Clazz && Clazz[IS_CONTAINER]) { |
| Clazz = subType ? Clazz[subType] : null; |
| } |
| |
| if (throwWhenNotFound && !Clazz) { |
| throw new Error(!subType ? componentMainType + '.' + 'type should be specified.' : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.'); |
| } |
| |
| return Clazz; |
| }; |
| |
| entity.getClassesByMainType = function (componentType) { |
| componentType = parseClassType(componentType); |
| var result = []; |
| var obj = storage[componentType.main]; |
| |
| if (obj && obj[IS_CONTAINER]) { |
| zrUtil.each(obj, function (o, type) { |
| type !== IS_CONTAINER && result.push(o); |
| }); |
| } else { |
| result.push(obj); |
| } |
| |
| return result; |
| }; |
| |
| entity.hasClass = function (componentType) { |
| // Just consider componentType.main. |
| componentType = parseClassType(componentType); |
| return !!storage[componentType.main]; |
| }; |
| /** |
| * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx'] |
| */ |
| |
| |
| entity.getAllClassMainTypes = function () { |
| var types = []; |
| zrUtil.each(storage, function (obj, type) { |
| types.push(type); |
| }); |
| return types; |
| }; |
| /** |
| * If a main type is container and has sub types |
| * @param {string} mainType |
| * @return {boolean} |
| */ |
| |
| |
| entity.hasSubTypes = function (componentType) { |
| componentType = parseClassType(componentType); |
| var obj = storage[componentType.main]; |
| return obj && obj[IS_CONTAINER]; |
| }; |
| |
| entity.parseClassType = parseClassType; |
| |
| function makeContainer(componentType) { |
| var container = storage[componentType.main]; |
| |
| if (!container || !container[IS_CONTAINER]) { |
| container = storage[componentType.main] = {}; |
| container[IS_CONTAINER] = true; |
| } |
| |
| return container; |
| } |
| |
| if (options.registerWhenExtend) { |
| var originalExtend = entity.extend; |
| |
| if (originalExtend) { |
| entity.extend = function (proto) { |
| var ExtendedClass = originalExtend.call(this, proto); |
| return entity.registerClass(ExtendedClass, proto.type); |
| }; |
| } |
| } |
| |
| return entity; |
| } |
| /** |
| * @param {string|Array.<string>} properties |
| */ |
| |
| export function setReadOnly(obj, properties) {// FIXME It seems broken in IE8 simulation of IE11 |
| // if (!zrUtil.isArray(properties)) { |
| // properties = properties != null ? [properties] : []; |
| // } |
| // zrUtil.each(properties, function (prop) { |
| // var value = obj[prop]; |
| // Object.defineProperty |
| // && Object.defineProperty(obj, prop, { |
| // value: value, writable: false |
| // }); |
| // zrUtil.isArray(obj[prop]) |
| // && Object.freeze |
| // && Object.freeze(obj[prop]); |
| // }); |
| } |