/ext-4.0.7/src/core/src/class/Class.js

https://bitbucket.org/srogerf/javascript · JavaScript · 559 lines · 263 code · 79 blank · 217 comment · 59 complexity · b48aebe8ee79fcbd07c8218293d5f037 MD5 · raw file

  1. /*
  2. This file is part of Ext JS 4
  3. Copyright (c) 2011 Sencha Inc
  4. Contact: http://www.sencha.com/contact
  5. GNU General Public License Usage
  6. This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
  7. If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
  8. */
  9. /**
  10. * @author Jacky Nguyen <jacky@sencha.com>
  11. * @docauthor Jacky Nguyen <jacky@sencha.com>
  12. * @class Ext.Class
  13. *
  14. * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
  15. * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
  16. * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
  17. *
  18. * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
  19. * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
  20. *
  21. * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
  22. * from, see {@link Ext.Base}.
  23. */
  24. (function() {
  25. var Class,
  26. Base = Ext.Base,
  27. baseStaticProperties = [],
  28. baseStaticProperty;
  29. for (baseStaticProperty in Base) {
  30. if (Base.hasOwnProperty(baseStaticProperty)) {
  31. baseStaticProperties.push(baseStaticProperty);
  32. }
  33. }
  34. /**
  35. * @method constructor
  36. * Creates new class.
  37. * @param {Object} classData An object represent the properties of this class
  38. * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
  39. * Note that the creation process can be asynchronous depending on the pre-processors used.
  40. * @return {Ext.Base} The newly created class
  41. */
  42. Ext.Class = Class = function(newClass, classData, onClassCreated) {
  43. if (typeof newClass != 'function') {
  44. onClassCreated = classData;
  45. classData = newClass;
  46. newClass = function() {
  47. return this.constructor.apply(this, arguments);
  48. };
  49. }
  50. if (!classData) {
  51. classData = {};
  52. }
  53. var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
  54. registeredPreprocessors = Class.getPreprocessors(),
  55. index = 0,
  56. preprocessors = [],
  57. preprocessor, staticPropertyName, process, i, j, ln;
  58. for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
  59. staticPropertyName = baseStaticProperties[i];
  60. newClass[staticPropertyName] = Base[staticPropertyName];
  61. }
  62. delete classData.preprocessors;
  63. for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
  64. preprocessor = preprocessorStack[j];
  65. if (typeof preprocessor == 'string') {
  66. preprocessor = registeredPreprocessors[preprocessor];
  67. if (!preprocessor.always) {
  68. if (classData.hasOwnProperty(preprocessor.name)) {
  69. preprocessors.push(preprocessor.fn);
  70. }
  71. }
  72. else {
  73. preprocessors.push(preprocessor.fn);
  74. }
  75. }
  76. else {
  77. preprocessors.push(preprocessor);
  78. }
  79. }
  80. classData.onClassCreated = onClassCreated || Ext.emptyFn;
  81. classData.onBeforeClassCreated = function(cls, data) {
  82. onClassCreated = data.onClassCreated;
  83. delete data.onBeforeClassCreated;
  84. delete data.onClassCreated;
  85. cls.implement(data);
  86. onClassCreated.call(cls, cls);
  87. };
  88. process = function(cls, data) {
  89. preprocessor = preprocessors[index++];
  90. if (!preprocessor) {
  91. data.onBeforeClassCreated.apply(this, arguments);
  92. return;
  93. }
  94. if (preprocessor.call(this, cls, data, process) !== false) {
  95. process.apply(this, arguments);
  96. }
  97. };
  98. process.call(Class, newClass, classData);
  99. return newClass;
  100. };
  101. Ext.apply(Class, {
  102. /** @private */
  103. preprocessors: {},
  104. /**
  105. * Register a new pre-processor to be used during the class creation process
  106. *
  107. * @member Ext.Class
  108. * @param {String} name The pre-processor's name
  109. * @param {Function} fn The callback function to be executed. Typical format:
  110. *
  111. * function(cls, data, fn) {
  112. * // Your code here
  113. *
  114. * // Execute this when the processing is finished.
  115. * // Asynchronous processing is perfectly ok
  116. * if (fn) {
  117. * fn.call(this, cls, data);
  118. * }
  119. * });
  120. *
  121. * @param {Function} fn.cls The created class
  122. * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
  123. * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
  124. * regardless of whether the processing is synchronous or aynchronous
  125. *
  126. * @return {Ext.Class} this
  127. * @static
  128. */
  129. registerPreprocessor: function(name, fn, always) {
  130. this.preprocessors[name] = {
  131. name: name,
  132. always: always || false,
  133. fn: fn
  134. };
  135. return this;
  136. },
  137. /**
  138. * Retrieve a pre-processor callback function by its name, which has been registered before
  139. *
  140. * @param {String} name
  141. * @return {Function} preprocessor
  142. * @static
  143. */
  144. getPreprocessor: function(name) {
  145. return this.preprocessors[name];
  146. },
  147. getPreprocessors: function() {
  148. return this.preprocessors;
  149. },
  150. /**
  151. * Retrieve the array stack of default pre-processors
  152. *
  153. * @return {Function[]} defaultPreprocessors
  154. * @static
  155. */
  156. getDefaultPreprocessors: function() {
  157. return this.defaultPreprocessors || [];
  158. },
  159. /**
  160. * Set the default array stack of default pre-processors
  161. *
  162. * @param {Function/Function[]} preprocessors
  163. * @return {Ext.Class} this
  164. * @static
  165. */
  166. setDefaultPreprocessors: function(preprocessors) {
  167. this.defaultPreprocessors = Ext.Array.from(preprocessors);
  168. return this;
  169. },
  170. /**
  171. * Inserts this pre-processor at a specific position in the stack, optionally relative to
  172. * any existing pre-processor. For example:
  173. *
  174. * Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
  175. * // Your code here
  176. *
  177. * if (fn) {
  178. * fn.call(this, cls, data);
  179. * }
  180. * }).setDefaultPreprocessorPosition('debug', 'last');
  181. *
  182. * @param {String} name The pre-processor name. Note that it needs to be registered with
  183. * {@link #registerPreprocessor registerPreprocessor} before this
  184. * @param {String} offset The insertion position. Four possible values are:
  185. * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
  186. * @param {String} relativeName
  187. * @return {Ext.Class} this
  188. * @static
  189. */
  190. setDefaultPreprocessorPosition: function(name, offset, relativeName) {
  191. var defaultPreprocessors = this.defaultPreprocessors,
  192. index;
  193. if (typeof offset == 'string') {
  194. if (offset === 'first') {
  195. defaultPreprocessors.unshift(name);
  196. return this;
  197. }
  198. else if (offset === 'last') {
  199. defaultPreprocessors.push(name);
  200. return this;
  201. }
  202. offset = (offset === 'after') ? 1 : -1;
  203. }
  204. index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
  205. if (index !== -1) {
  206. Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
  207. }
  208. return this;
  209. }
  210. });
  211. /**
  212. * @cfg {String} extend
  213. * The parent class that this class extends. For example:
  214. *
  215. * Ext.define('Person', {
  216. * say: function(text) { alert(text); }
  217. * });
  218. *
  219. * Ext.define('Developer', {
  220. * extend: 'Person',
  221. * say: function(text) { this.callParent(["print "+text]); }
  222. * });
  223. */
  224. Class.registerPreprocessor('extend', function(cls, data) {
  225. var extend = data.extend,
  226. base = Ext.Base,
  227. basePrototype = base.prototype,
  228. prototype = function() {},
  229. parent, i, k, ln, staticName, parentStatics,
  230. parentPrototype, clsPrototype;
  231. if (extend && extend !== Object) {
  232. parent = extend;
  233. }
  234. else {
  235. parent = base;
  236. }
  237. parentPrototype = parent.prototype;
  238. prototype.prototype = parentPrototype;
  239. clsPrototype = cls.prototype = new prototype();
  240. if (!('$class' in parent)) {
  241. for (i in basePrototype) {
  242. if (!parentPrototype[i]) {
  243. parentPrototype[i] = basePrototype[i];
  244. }
  245. }
  246. }
  247. clsPrototype.self = cls;
  248. cls.superclass = clsPrototype.superclass = parentPrototype;
  249. delete data.extend;
  250. //<feature classSystem.inheritableStatics>
  251. // Statics inheritance
  252. parentStatics = parentPrototype.$inheritableStatics;
  253. if (parentStatics) {
  254. for (k = 0, ln = parentStatics.length; k < ln; k++) {
  255. staticName = parentStatics[k];
  256. if (!cls.hasOwnProperty(staticName)) {
  257. cls[staticName] = parent[staticName];
  258. }
  259. }
  260. }
  261. //</feature>
  262. //<feature classSystem.config>
  263. // Merge the parent class' config object without referencing it
  264. if (parentPrototype.config) {
  265. clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
  266. }
  267. else {
  268. clsPrototype.config = {};
  269. }
  270. //</feature>
  271. //<feature classSystem.onClassExtended>
  272. if (clsPrototype.$onExtended) {
  273. clsPrototype.$onExtended.call(cls, cls, data);
  274. }
  275. if (data.onClassExtended) {
  276. clsPrototype.$onExtended = data.onClassExtended;
  277. delete data.onClassExtended;
  278. }
  279. //</feature>
  280. }, true);
  281. //<feature classSystem.statics>
  282. /**
  283. * @cfg {Object} statics
  284. * List of static methods for this class. For example:
  285. *
  286. * Ext.define('Computer', {
  287. * statics: {
  288. * factory: function(brand) {
  289. * // 'this' in static methods refer to the class itself
  290. * return new this(brand);
  291. * }
  292. * },
  293. *
  294. * constructor: function() { ... }
  295. * });
  296. *
  297. * var dellComputer = Computer.factory('Dell');
  298. */
  299. Class.registerPreprocessor('statics', function(cls, data) {
  300. cls.addStatics(data.statics);
  301. delete data.statics;
  302. });
  303. //</feature>
  304. //<feature classSystem.inheritableStatics>
  305. /**
  306. * @cfg {Object} inheritableStatics
  307. * List of inheritable static methods for this class.
  308. * Otherwise just like {@link #statics} but subclasses inherit these methods.
  309. */
  310. Class.registerPreprocessor('inheritableStatics', function(cls, data) {
  311. cls.addInheritableStatics(data.inheritableStatics);
  312. delete data.inheritableStatics;
  313. });
  314. //</feature>
  315. //<feature classSystem.config>
  316. /**
  317. * @cfg {Object} config
  318. * List of configuration options with their default values, for which automatically
  319. * accessor methods are generated. For example:
  320. *
  321. * Ext.define('SmartPhone', {
  322. * config: {
  323. * hasTouchScreen: false,
  324. * operatingSystem: 'Other',
  325. * price: 500
  326. * },
  327. * constructor: function(cfg) {
  328. * this.initConfig(cfg);
  329. * }
  330. * });
  331. *
  332. * var iPhone = new SmartPhone({
  333. * hasTouchScreen: true,
  334. * operatingSystem: 'iOS'
  335. * });
  336. *
  337. * iPhone.getPrice(); // 500;
  338. * iPhone.getOperatingSystem(); // 'iOS'
  339. * iPhone.getHasTouchScreen(); // true;
  340. * iPhone.hasTouchScreen(); // true
  341. */
  342. Class.registerPreprocessor('config', function(cls, data) {
  343. var prototype = cls.prototype;
  344. Ext.Object.each(data.config, function(name) {
  345. var cName = name.charAt(0).toUpperCase() + name.substr(1),
  346. pName = name,
  347. apply = 'apply' + cName,
  348. setter = 'set' + cName,
  349. getter = 'get' + cName;
  350. if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
  351. data[apply] = function(val) {
  352. return val;
  353. };
  354. }
  355. if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
  356. data[setter] = function(val) {
  357. var ret = this[apply].call(this, val, this[pName]);
  358. if (typeof ret != 'undefined') {
  359. this[pName] = ret;
  360. }
  361. return this;
  362. };
  363. }
  364. if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
  365. data[getter] = function() {
  366. return this[pName];
  367. };
  368. }
  369. });
  370. Ext.Object.merge(prototype.config, data.config);
  371. delete data.config;
  372. });
  373. //</feature>
  374. //<feature classSystem.mixins>
  375. /**
  376. * @cfg {Object} mixins
  377. * List of classes to mix into this class. For example:
  378. *
  379. * Ext.define('CanSing', {
  380. * sing: function() {
  381. * alert("I'm on the highway to hell...")
  382. * }
  383. * });
  384. *
  385. * Ext.define('Musician', {
  386. * extend: 'Person',
  387. *
  388. * mixins: {
  389. * canSing: 'CanSing'
  390. * }
  391. * })
  392. */
  393. Class.registerPreprocessor('mixins', function(cls, data) {
  394. var mixins = data.mixins,
  395. name, mixin, i, ln;
  396. delete data.mixins;
  397. Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
  398. if (mixins instanceof Array) {
  399. for (i = 0,ln = mixins.length; i < ln; i++) {
  400. mixin = mixins[i];
  401. name = mixin.prototype.mixinId || mixin.$className;
  402. cls.mixin(name, mixin);
  403. }
  404. }
  405. else {
  406. for (name in mixins) {
  407. if (mixins.hasOwnProperty(name)) {
  408. cls.mixin(name, mixins[name]);
  409. }
  410. }
  411. }
  412. });
  413. });
  414. //</feature>
  415. Class.setDefaultPreprocessors([
  416. 'extend'
  417. //<feature classSystem.statics>
  418. ,'statics'
  419. //</feature>
  420. //<feature classSystem.inheritableStatics>
  421. ,'inheritableStatics'
  422. //</feature>
  423. //<feature classSystem.config>
  424. ,'config'
  425. //</feature>
  426. //<feature classSystem.mixins>
  427. ,'mixins'
  428. //</feature>
  429. ]);
  430. //<feature classSystem.backwardsCompatible>
  431. // Backwards compatible
  432. Ext.extend = function(subclass, superclass, members) {
  433. if (arguments.length === 2 && Ext.isObject(superclass)) {
  434. members = superclass;
  435. superclass = subclass;
  436. subclass = null;
  437. }
  438. var cls;
  439. if (!superclass) {
  440. Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
  441. }
  442. members.extend = superclass;
  443. members.preprocessors = [
  444. 'extend'
  445. //<feature classSystem.statics>
  446. ,'statics'
  447. //</feature>
  448. //<feature classSystem.inheritableStatics>
  449. ,'inheritableStatics'
  450. //</feature>
  451. //<feature classSystem.mixins>
  452. ,'mixins'
  453. //</feature>
  454. //<feature classSystem.config>
  455. ,'config'
  456. //</feature>
  457. ];
  458. if (subclass) {
  459. cls = new Class(subclass, members);
  460. }
  461. else {
  462. cls = new Class(members);
  463. }
  464. cls.prototype.override = function(o) {
  465. for (var m in o) {
  466. if (o.hasOwnProperty(m)) {
  467. this[m] = o[m];
  468. }
  469. }
  470. };
  471. return cls;
  472. };
  473. //</feature>
  474. })();