/** * @ignore * infrastructure for create plugin/extension-enabled class * @author yiminghe@gmail.com */ KISSY.add('rich-base', function (S, Base) { var ATTRS = 'ATTRS', ucfirst = S.ucfirst, ON_SET = '_onSet', noop = S.noop; /** * infrastructure for create plugin/extension-enabled class * @class KISSY.RichBase * @extend KISSY.Base */ function RichBase() { var self = this, n, listeners; // apply attribute Base.apply(self, arguments); // setup listeners listeners = self.get("listeners"); for (n in listeners) { self.on(n, listeners[n]); } // initialize main class and extension self.callMethodByHierarchy("initializer", "constructor"); // initialize plugins self.constructPlugins(); self.callPluginsMethod("initializer"); self.bindInternal(); self.syncInternal(); } S.extend(RichBase, Base, { /** * collection all constructor by extend hierarchy * @protected * @return {Array} */ collectConstructorChains: function () { var self = this, constructorChains = [], c = self.constructor; while (c) { constructorChains.push(c); c = c.superclass && c.superclass.constructor; } return constructorChains; }, /** * call methods on main class and extension class by order * @protected * @param mainMethod method name of main class. * @param extMethod method name of extension class */ callMethodByHierarchy: function (mainMethod, extMethod) { var self = this, c = self.constructor, extChains = [], ext, main, i, exts, t; // define while (c) { // 收集扩展类 t = []; if (exts = c.__ks_exts) { for (i = 0; i < exts.length; i++) { ext = exts[i]; if (ext) { if (extMethod != "constructor") { //只调用真正自己构造器原型的定义,继承原型链上的不要管 if (ext.prototype.hasOwnProperty(extMethod)) { ext = ext.prototype[extMethod]; } else { ext = null; } } ext && t.push(ext); } } } // 收集主类 // 只调用真正自己构造器原型的定义,继承原型链上的不要管 !important // 所以不用自己在 renderUI 中调用 superclass.renderUI 了,UIBase 构造器自动搜寻 // 以及 initializer 等同理 if (c.prototype.hasOwnProperty(mainMethod) && (main = c.prototype[mainMethod])) { t.push(main); } // 原地 reverse if (t.length) { extChains.push.apply(extChains, t.reverse()); } c = c.superclass && c.superclass.constructor; } // 初始化函数 // 顺序:父类的所有扩展类函数 -> 父类对应函数 -> 子类的所有扩展函数 -> 子类对应函数 for (i = extChains.length - 1; i >= 0; i--) { extChains[i] && extChains[i].call(self); } }, /** * call plugin method * @protected * @param method method name of plugin */ callPluginsMethod: function (method) { var self = this; method = 'plugin' + ucfirst(method); S.each(self.get('plugins'), function (plugin) { if (plugin[method]) { plugin[method](self); } }); }, /** * construct plugins * @protected */ constructPlugins: function () { var plugins = this.get('plugins'); S.each(plugins, function (plugin, i) { if (S.isFunction(plugin)) { plugins[i] = new plugin(); } }); }, /** * bind attribute change event * @protected */ bindInternal: function () { var self = this, attrs = self.getAttrs(), attr, m; for (attr in attrs) { m = ON_SET + ucfirst(attr); if (self[m]) { // 自动绑定事件到对应函数 self.on('after' + ucfirst(attr) + 'Change', onSetAttrChange); } } }, /** * sync attribute change event * @protected */ syncInternal: function () { var self = this, attributeValue, onSetMethod, i, constructor, attributeName, onSetMethodName, cache = {}, constructorChains = self.collectConstructorChains(), attrs; // 从父类到子类执行同步属性函数 for (i = constructorChains.length - 1; i >= 0; i--) { constructor = constructorChains[i]; if (attrs = constructor[ATTRS]) { for (attributeName in attrs) { // 防止子类覆盖父类属性定义造成重复执行 if (!cache[attributeName]) { cache[attributeName] = 1; onSetMethodName = ON_SET + ucfirst(attributeName); // 存在方法,并且用户设置了初始值或者存在默认值,就同步状态 if ((onSetMethod = self[onSetMethodName]) && // 用户如果设置了显式不同步,就不同步, // 比如一些值从 html 中读取,不需要同步再次设置 attrs[attributeName].sync !== false && (attributeValue = self.get(attributeName)) !== undefined) { onSetMethod.call(self, attributeValue); } } } } } }, /** * initialize for overridden * @protected */ initializer: noop, /** * destroy for overridden * @protected */ destructor: noop, /** * destroy current instance. */ destroy: function () { var self = this; self.callPluginsMethod("destructor"); destroyHierarchy(self); self.fire('destroy'); self.detach(); }, /** * plugin a new plugins to current instance * @param {Function|Object} plugin * @chainable */ 'plug': function (plugin) { var self = this; if (S.isFunction(plugin)) { plugin = new plugin(); } // initialize plugin if (plugin['pluginInitializer']) { plugin['pluginInitializer'](self); } self.get('plugins').push(plugin); return self; }, /** * unplug by pluginId or plugin instance. * if called with no parameter, then destroy all plugins. * @param {Object|String} [plugin] * @chainable */ 'unplug': function (plugin) { var plugins = [], self = this, isString = typeof plugin == 'string'; S.each(self.get('plugins'), function (p) { var keep = 0, pluginId; if (plugin) { if (isString) { // user defined takes priority pluginId = p.get && p.get('pluginId') || p.pluginId; if (pluginId != plugin) { plugins.push(p); keep = 1; } } else { if (p != plugin) { plugins.push(p); keep = 1; } } } if (!keep) { p.pluginDestructor(self); } }); self.setInternal('plugins', plugins); return self; }, /** * get specified plugin instance by id * @param {String} id pluginId of plugin instance * @return {Object} */ 'getPlugin': function (id) { var plugin = null; S.each(this.get('plugins'), function (p) { // user defined takes priority var pluginId = p.get && p.get('pluginId') || p.pluginId; if (pluginId == id) { plugin = p; return false; } }); return plugin; } }, { ATTRS: { /** * Plugins for current component. * @cfg {Function[]/Object[]} plugins */ /** * @ignore */ plugins: { value: [] }, /** * Config listener on created. * * for example: * * { * click:{ * context:{x:1}, * fn:function(){ * alert(this.x); * } * } * } * // or * { * click:function(){ * alert(this.x); * } * } * * @cfg {Object} listeners */ /** * @ignore */ listeners: { value: [] } } }); S.mix(RichBase, { /** * create a new class from extensions and static/prototype properties/methods. * @param {Function[]} [extensions] extension classes * @param {Object} [px] key-value map for prototype properties/methods. * @param {Object} [sx] key-value map for static properties/methods. * @return {Function} new class which extend called, it also has a static extend method * * for example: * * var parent = RichBase.extend({ * isParent: 1 * }); * var child = parent.extend({ * isChild: 1, * isParent: 0 * }) */ extend: function extend(extensions, px, sx) { var baseClass = this, name = "RichBaseDerived", t, C, args = S.makeArray(arguments); if (extensions == null || S.isObject(extensions)) { sx = px; px = extensions; extensions = []; } if (typeof (t = args[args.length - 1]) == 'string') { name = t; } px = px || {}; if (px.hasOwnProperty('constructor')) { C = px.constructor; } else { C = function () { C.superclass.constructor.apply(this, arguments); }; // debug mode, give the right name for constructor // refer : http://limu.iteye.com/blog/1136712 if (S.Config.debug) { eval("C=function " + name.replace(/[-./]/g, "_") + "(){ C.superclass.constructor.apply(this, arguments);}"); } } C.name = name; S.extend(C, baseClass, px, sx); if (extensions) { C.__ks_exts = extensions; var attrs = {}, prototype = {}; // [ex1,ex2],扩展类后面的优先,ex2 定义的覆盖 ex1 定义的 // 主类最优先 S.each(extensions['concat'](C), function (ext) { if (ext) { // 合并 ATTRS 到主类 // 不覆盖主类上的定义(主类位于 constructors 最后) // 因为继承层次上扩展类比主类层次高 // 注意:最好 value 必须是简单对象,自定义 new 出来的对象就会有问题 // (用 function return 出来)! // a {y:{value:2}} b {y:{value:3,getter:fn}} // b is a extension of a // => // a {y:{value:2,getter:fn}} S.each(ext[ATTRS], function (v, name) { var av = attrs[name] = attrs[name] || {}; S.mix(av, v); }); // 方法合并 var exp = ext.prototype, p; for (p in exp) { // do not mess with parent class if (exp.hasOwnProperty(p)) { prototype[p] = exp[p]; } } } }); C[ATTRS] = attrs; S.augment(C, prototype); } C.extend = extend; return C; } }); // # private start -------------------------------------- // 销毁顺序: 子类 destructor -> 子类扩展 destructor -> 父类 destructor -> 父类扩展 destructor function destroyHierarchy(self) { var c = self.constructor, extensions, d, i; while (c) { // 只触发该类真正的析构器,和父亲没关系,所以不要在子类析构器中调用 superclass if (c.prototype.hasOwnProperty("destructor")) { c.prototype.destructor.apply(self); } if ((extensions = c.__ks_exts)) { for (i = extensions.length - 1; i >= 0; i--) { d = extensions[i] && extensions[i].prototype.__destructor; d && d.apply(self); } } c = c.superclass && c.superclass.constructor; } } function onSetAttrChange(e) { // ignore bubbling if (e.target == this) { var m = ON_SET + e.type.slice(5).slice(0, -6); this[m](e.newVal, e); } } // # private end -------------------------------------- return RichBase; }, { requires: ['base'] });