| 1 | | var Class = { |
|---|
| 2 | | create: function() { |
|---|
| 3 | | return function() { |
|---|
| 4 | | this.initialize.apply(this, arguments); |
|---|
| | 1 | var Class = new (function(vocab) { |
|---|
| | 2 | |
|---|
| | 3 | this.AUTHOR = "Ben Newman (newmanb@stanford.edu)"; |
|---|
| | 4 | |
|---|
| | 5 | var $doNotInit = {}; |
|---|
| | 6 | |
|---|
| | 7 | this.create = function(declarations) { |
|---|
| | 8 | |
|---|
| | 9 | var constructor = function() { |
|---|
| | 10 | if (arguments[0] !== $doNotInit) { |
|---|
| | 11 | this._runTimeDeclarations = {}; |
|---|
| | 12 | if (this[vocab['init']]) |
|---|
| | 13 | this[vocab['init']].apply(this, arguments); |
|---|
| | 14 | } |
|---|
| | 15 | }; |
|---|
| | 16 | |
|---|
| | 17 | if (declarations) { |
|---|
| | 18 | var ownPrototype = function() { |
|---|
| | 19 | for (var name in declarations) { |
|---|
| | 20 | declarations[name].ancestor = baseInstance[name]; |
|---|
| | 21 | __attachClassProperty(name, declarations[name], this); |
|---|
| | 22 | } |
|---|
| | 23 | __ensureDefaults(this, declarations); |
|---|
| | 24 | __ensureOwnPrototypeMemory(this, declarations); |
|---|
| | 25 | }; |
|---|
| | 26 | |
|---|
| | 27 | var base = declarations.extending || {}; |
|---|
| | 28 | var baseInstance = __inherit(ownPrototype, base); |
|---|
| | 29 | |
|---|
| | 30 | declarations['getBaseInstance'] = Class.encap(baseInstance); |
|---|
| | 31 | |
|---|
| | 32 | __setupAttachDelete(declarations); |
|---|
| | 33 | __inherit(constructor, ownPrototype); |
|---|
| | 34 | __enableMimicry(constructor); // experimental |
|---|
| | 35 | |
|---|
| | 36 | constructor['extend'] = function(declarations) { |
|---|
| | 37 | declarations['extending'] = constructor; |
|---|
| | 38 | return Class.create(declarations); |
|---|
| | 39 | } // a la Dean Edwards' Base.js |
|---|
| | 45 | var __enableMimicry = function(__ctor) { |
|---|
| | 46 | var __old = __ctor.prototype; |
|---|
| | 47 | __ctor['mimic'] = function(obj) { |
|---|
| | 48 | var __new = Object.extend(obj, __old); |
|---|
| | 49 | __ensureDefaults(__new, __old); |
|---|
| | 50 | return Object.extend(function() { |
|---|
| | 51 | return __ctor.apply(this, arguments); |
|---|
| | 52 | }, { 'prototype': __new }); |
|---|
| | 53 | }; |
|---|
| | 54 | }; |
|---|
| | 55 | |
|---|
| | 56 | var __inherit = function(subclass, superclass) { |
|---|
| | 57 | |
|---|
| | 58 | switch (typeof(superclass)) { |
|---|
| | 59 | case 'object': default: |
|---|
| | 60 | superclass[vocab['init']] = superclass.constructor; |
|---|
| | 61 | subclass.prototype = superclass; break; |
|---|
| | 62 | case 'function': |
|---|
| | 63 | superclass.getInstance = superclass.getInstance || |
|---|
| | 64 | Class.encap(new superclass($doNotInit)); |
|---|
| | 65 | subclass.prototype = superclass.getInstance(); |
|---|
| | 66 | } |
|---|
| | 67 | |
|---|
| | 68 | return subclass.prototype; |
|---|
| | 69 | |
|---|
| | 70 | }; |
|---|
| | 71 | |
|---|
| | 72 | var __setupAttachDelete = function(declarations) { |
|---|
| | 73 | return Object.extend(declarations, { |
|---|
| | 74 | |
|---|
| | 75 | 'attachMethod': function(fnName, func) { |
|---|
| | 76 | if (!this.ownPrototypeHas(fnName)) { |
|---|
| | 77 | this._runTimeDeclarations[fnName] = true; |
|---|
| | 78 | func.ancestor = this[fnName]; |
|---|
| | 79 | __attachClassProperty(fnName, func, this); |
|---|
| | 80 | } else throw "Can't replace existing methods." |
|---|
| | 81 | }, |
|---|
| | 82 | |
|---|
| | 83 | 'deleteMethod': function(fnName) { |
|---|
| | 84 | if (this._runTimeDeclarations[fnName]) { |
|---|
| | 85 | __attachClassProperty(fnName, null, this, true); |
|---|
| | 86 | this._runTimeDeclarations[fnName] = false; |
|---|
| | 87 | } else throw "Only attachMethod additions can be deleted." |
|---|
| | 88 | } |
|---|
| | 89 | |
|---|
| | 90 | }); |
|---|
| | 91 | }; |
|---|
| | 92 | |
|---|
| | 93 | var __validOverride = function(propName, hasAncestor) { |
|---|
| | 94 | switch (propName) { |
|---|
| | 95 | case vocab['super']: case 'valueOf': |
|---|
| | 96 | case 'attachMethod': case 'deleteMethod': |
|---|
| | 97 | case 'getBaseInstance': return false; |
|---|
| | 98 | default: return !!hasAncestor; |
|---|
| | 99 | } |
|---|
| | 100 | }; |
|---|
| | 101 | |
|---|
| | 102 | var __attachClassProperty = function(propName, prop, obj, |
|---|
| | 103 | /*optional*/ removeInstead) { |
|---|
| | 104 | if (removeInstead) { |
|---|
| | 105 | |
|---|
| | 106 | if (obj.ownPrototypeHas(propName)) |
|---|
| | 107 | delete obj[propName]; |
|---|
| | 108 | |
|---|
| | 109 | } else if (typeof(prop) == 'function' && |
|---|
| | 110 | __validOverride(propName, !!prop.ancestor)) { |
|---|
| | 111 | |
|---|
| | 112 | obj[propName] = function() { |
|---|
| | 113 | var __superSaver = this[vocab['super']]; |
|---|
| | 114 | this[vocab['super']] = prop.ancestor; |
|---|
| | 115 | try { var result = prop.apply(this, arguments); } |
|---|
| | 116 | catch (ex) { throw ex; } |
|---|
| | 117 | finally { this[vocab['super']] = __superSaver; } |
|---|
| | 118 | return result; |
|---|
| | 119 | }; |
|---|
| | 120 | |
|---|
| | 121 | obj[propName].valueOf = Class.encap(prop); |
|---|
| | 122 | |
|---|
| | 123 | var __origStr = prop.toString(); |
|---|
| | 124 | prop.toString = obj[propName].toString = function() { |
|---|
| | 125 | return __origStr.replace(/^\s*function/, propName); |
|---|
| | 126 | }; |
|---|
| | 127 | |
|---|
| | 128 | } else obj[propName] = prop; |
|---|
| | 129 | |
|---|
| | 130 | }; |
|---|
| | 131 | |
|---|
| | 132 | var __ensureDefaults = function(ownPrototype, declarations) { |
|---|
| | 133 | ['toString', 'toLocaleString', 'valueOf'].each(function(propName) { |
|---|
| | 134 | var prop = declarations[propName]; |
|---|
| | 135 | if (!!prop) { |
|---|
| | 136 | prop.ancestor = ownPrototype[propName]; |
|---|
| | 137 | __attachClassProperty(propName, prop, ownPrototype); |
|---|
| | 138 | } |
|---|
| | 139 | }); |
|---|
| | 140 | }; |
|---|
| | 141 | |
|---|
| | 142 | var __ensureOwnPrototypeMemory = function(ownPrototype, declarations) { |
|---|
| | 143 | ownPrototype.ownPrototypeHas = function(property) { |
|---|
| | 144 | return (typeof(declarations[property]) != 'undefined' || |
|---|
| | 145 | (this._runTimeDeclarations && // present if instantiated |
|---|
| | 146 | this._runTimeDeclarations[property])); |
|---|
| | 147 | }; |
|---|
| | 148 | }; |
|---|
| | 149 | |
|---|
| | 150 | // convenient mechanism for creating accessor functions |
|---|
| | 151 | this.encap = function(__val, mutable) { |
|---|
| | 152 | if (!mutable) return function() { return __val; } |
|---|
| | 153 | else return function(/* optional new value */) { |
|---|
| | 154 | if (arguments.length > 0) __val = arguments[0]; |
|---|
| | 155 | return __val; |
|---|
| | 156 | } |
|---|
| | 157 | }; |
|---|
| | 158 | |
|---|
| | 159 | })( // the vocabulary is arbitrary: |
|---|
| | 160 | {'super': 'sup', 'init': 'initialize'}); |
|---|
| | 161 | |
|---|