| 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 | __attachClassProperty(name, declarations[name], this); |
|---|
| | 21 | __ensureDefaults(this, declarations); |
|---|
| | 22 | __ensureOwnPrototypeMemory(this, declarations); |
|---|
| | 23 | }; |
|---|
| | 24 | |
|---|
| | 25 | var base = declarations.extending || {}; |
|---|
| | 26 | delete declarations.extending; |
|---|
| | 27 | |
|---|
| | 28 | var baseInstance = __inherit(ownPrototype, base); |
|---|
| | 29 | declarations['getBaseInstance'] = Class.encap(baseInstance); |
|---|
| | 30 | declarations[vocab['super']] = __makeSup(); |
|---|
| | 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 | return Object.extend(function() { |
|---|
| | 50 | return __ctor.apply(this, arguments); |
|---|
| | 51 | }, { 'prototype': |
|---|
| | 52 | __ensureDefaults(__new, __old) }); |
|---|
| | 53 | }; |
|---|
| | 54 | }; |
|---|
| | 55 | |
|---|
| | 56 | var __inherit = function(subclass, superclass) { |
|---|
| | 57 | switch (typeof(superclass)) { |
|---|
| | 58 | case 'object': default: |
|---|
| | 59 | superclass[vocab['init']] = superclass.constructor; |
|---|
| | 60 | subclass.prototype = superclass; break; |
|---|
| | 61 | case 'function': |
|---|
| | 62 | superclass.getInstance = superclass.getInstance || |
|---|
| | 63 | Class.encap(new superclass($doNotInit)); |
|---|
| | 64 | subclass.prototype = superclass.getInstance(); |
|---|
| | 65 | } |
|---|
| | 66 | return subclass.prototype; |
|---|
| | 67 | }; |
|---|
| | 68 | |
|---|
| | 69 | var __setupAttachDelete = function(declarations) { |
|---|
| | 70 | return Object.extend(declarations, { |
|---|
| | 71 | 'attachMethod': function(fnName, func) { |
|---|
| | 72 | if (!this.ownPrototypeHas(fnName)) { |
|---|
| | 73 | this._runTimeDeclarations[fnName] = true; |
|---|
| | 74 | __attachClassProperty(fnName, func, this); |
|---|
| | 75 | } else throw "Can't replace existing methods." |
|---|
| | 76 | }, |
|---|
| | 77 | |
|---|
| | 78 | 'deleteMethod': function(fnName) { |
|---|
| | 79 | if (this._runTimeDeclarations[fnName]) { |
|---|
| | 80 | __attachClassProperty(fnName, null, this, true); |
|---|
| | 81 | this._runTimeDeclarations[fnName] = false; |
|---|
| | 82 | } else throw "Only attachMethod additions can be deleted." |
|---|
| | 83 | } |
|---|
| | 84 | }); |
|---|
| | 85 | }; |
|---|
| | 86 | |
|---|
| | 87 | var __attachClassProperty = function(propName, prop, obj, |
|---|
| | 88 | /*optional*/ removeInstead) { |
|---|
| | 89 | if (removeInstead) { |
|---|
| | 90 | if (obj.ownPrototypeHas(propName)) |
|---|
| | 91 | delete obj[propName]; |
|---|
| | 92 | } else { |
|---|
| | 93 | if (typeof(obj[propName] = prop) == 'function') { |
|---|
| | 94 | if (typeof(prop.body) != 'function') |
|---|
| | 95 | __addStackLogic(obj, propName); |
|---|
| | 96 | var __origStr = prop.toString(); |
|---|
| | 97 | prop.toString = obj[propName].toString = function() { |
|---|
| | 98 | return __origStr.replace(/^\s*function/, propName); |
|---|
| | 99 | }; |
|---|
| | 100 | } |
|---|
| | 101 | } |
|---|
| | 102 | }; |
|---|
| | 103 | |
|---|
| | 104 | // special-purpose array object (slightly faster in tests) |
|---|
| | 105 | var __callStack = new (function() { |
|---|
| | 106 | var stack = []; var index = 0; |
|---|
| | 107 | this.push = function(what) { stack[index++] = what; } |
|---|
| | 108 | this.pop = function() { return stack[--index]; } |
|---|
| | 109 | this.empty = function() { return index == 0; } |
|---|
| | 110 | this.peek = function() { return stack[index - 1]; } |
|---|
| | 111 | })(); |
|---|
| | 112 | |
|---|
| | 113 | var $empty = ['', null]; |
|---|
| | 114 | var __lastOnCallStack = function() { |
|---|
| | 115 | if (__callStack.empty()) return $empty; |
|---|
| | 116 | else return __callStack.peek(); |
|---|
| | 117 | }; |
|---|
| | 118 | |
|---|
| | 119 | var __addStackLogic = function(obj, fnName) { |
|---|
| | 120 | switch (fnName) { |
|---|
| | 121 | case vocab['super']: case 'valueOf': |
|---|
| | 122 | case 'attachMethod': case 'deleteMethod': |
|---|
| | 123 | case 'getBaseInstance': return; |
|---|
| | 124 | default: |
|---|
| | 125 | obj[fnName] = Object.extend(__wrap(obj[fnName], fnName), { |
|---|
| | 126 | 'body': obj[fnName] // preserve underlying function |
|---|
| | 127 | }); |
|---|
| | 128 | } |
|---|
| | 129 | }; |
|---|
| | 130 | |
|---|
| | 131 | // same as bind(), but remembers callstack contents too |
|---|
| | 132 | Function.prototype.grant = function(obj) { |
|---|
| | 133 | return __wrap(this, __lastOnCallStack(), obj); |
|---|
| | 134 | }; |
|---|
| | 135 | |
|---|
| | 136 | var __wrap = function(func, toPush, base) { |
|---|
| | 137 | var isArray = (typeof(toPush) == 'array'); |
|---|
| | 138 | if (!isArray) toPush = [toPush]; |
|---|
| | 139 | |
|---|
| | 140 | return function() { |
|---|
| | 141 | if (!isArray) toPush.push(this); |
|---|
| | 142 | __callStack.push(toPush); |
|---|
| | 143 | try { |
|---|
| | 144 | var result = |
|---|
| | 145 | func.apply(base || this, arguments); |
|---|
| | 146 | } catch (ex) { throw ex; } |
|---|
| | 147 | finally { __callStack.pop(); } |
|---|
| | 148 | return result; |
|---|
| | 149 | }; |
|---|
| | 150 | }; |
|---|
| | 151 | |
|---|
| | 152 | var __ensureDefaults = function(ownPrototype, declarations) { |
|---|
| | 153 | ['toString', 'toLocaleString', 'valueOf'].each(function(prop) { |
|---|
| | 154 | __attachClassProperty(prop, declarations[prop], ownPrototype); |
|---|
| | 155 | }); |
|---|
| | 156 | return ownPrototype; |
|---|
| | 157 | }; |
|---|
| | 158 | |
|---|
| | 159 | var __ensureOwnPrototypeMemory = function(ownPrototype, declarations) { |
|---|
| | 160 | ownPrototype.ownPrototypeHas = function(property) { |
|---|
| | 161 | return (typeof(declarations[property]) != 'undefined' || |
|---|
| | 162 | (this._runTimeDeclarations && // present if instantiated |
|---|
| | 163 | this._runTimeDeclarations[property])); |
|---|
| | 164 | }; |
|---|
| | 165 | }; |
|---|
| | 166 | |
|---|
| | 167 | // convenient mechanism for creating accessor functions |
|---|
| | 168 | this.encap = function(__val, mutable) { |
|---|
| | 169 | if (!mutable) return function() { return __val; } |
|---|
| | 170 | else return function(/* optional new value */) { |
|---|
| | 171 | if (arguments.length > 0) __val = arguments[0]; |
|---|
| | 172 | return __val; |
|---|
| | 173 | } |
|---|
| | 174 | }; |
|---|
| | 175 | |
|---|
| | 176 | var __makeSup = function() { |
|---|
| | 177 | var __platform = false; // where to start lookup |
|---|
| | 178 | |
|---|
| | 179 | return function() { |
|---|
| | 180 | var methodName = __lastOnCallStack()[0]; |
|---|
| | 181 | var curObj = __platform || this; |
|---|
| | 182 | |
|---|
| | 183 | while ( curObj.ownPrototypeHas && |
|---|
| | 184 | !curObj.ownPrototypeHas(methodName) && |
|---|
| | 185 | curObj.getBaseInstance) |
|---|
| | 186 | curObj = curObj.getBaseInstance(); |
|---|
| | 187 | curObj = curObj.getBaseInstance |
|---|
| | 188 | ? curObj.getBaseInstance() : {}; |
|---|
| | 189 | |
|---|
| | 190 | var prevBase = __platform; |
|---|
| | 191 | __platform = curObj; |
|---|
| | 192 | try { var result = |
|---|
| | 193 | curObj[methodName].apply(this, arguments); } |
|---|
| | 194 | catch (ex) { throw ex; } |
|---|
| | 195 | finally { __platform = prevBase; } |
|---|
| | 196 | return result; |
|---|
| | 197 | }; |
|---|
| | 198 | }; |
|---|
| | 199 | })( // the vocabulary is arbitrary: |
|---|
| | 200 | {'super': 'sup', 'init': 'initialize'}); |
|---|
| | 201 | |
|---|
| | 202 | setTimeout(function() { |
|---|
| | 203 | window.Base = Class.create({}); |
|---|
| | 204 | }, 10); |
|---|
| | 205 | |
|---|