| 1 | | var Class = { |
|---|
| 2 | | create: function() { |
|---|
| 3 | | return function() { |
|---|
| 4 | | this.initialize.apply(this, arguments); |
|---|
| | 1 | var Class = new (function() { |
|---|
| | 2 | |
|---|
| | 3 | // You'll definitely want to check out |
|---|
| | 4 | // http://groupspace.org/devben/proto-svn/demo/ |
|---|
| | 5 | // to get a mile-high understanding of why I |
|---|
| | 6 | // bothered with all of this. |
|---|
| | 7 | |
|---|
| | 8 | this.NAME = "Class"; |
|---|
| | 9 | this.VERSION = Prototype.Version || 2.0; |
|---|
| | 10 | // put your name here if you've helped in any way! |
|---|
| | 11 | this.AUTHORS = "Ben Newman (of groupspace.org), et al."; |
|---|
| | 12 | |
|---|
| | 13 | this.__repr__ = function() { |
|---|
| | 14 | return "[" + this.NAME + " " + this.VERSION + "]"; |
|---|
| | 15 | }; |
|---|
| | 16 | |
|---|
| | 17 | this.toString = function() { |
|---|
| | 18 | return this.__repr__(); |
|---|
| | 19 | }; |
|---|
| | 20 | |
|---|
| | 21 | var __doNotInit = {}; |
|---|
| | 22 | |
|---|
| | 23 | this.create = function(declarations) { |
|---|
| | 24 | |
|---|
| | 25 | var ctor = function() { |
|---|
| | 26 | if (arguments[0] !== __doNotInit) { |
|---|
| | 27 | this._runTimeDeclarations = {}; |
|---|
| | 28 | if (this.initialize) |
|---|
| | 29 | this.initialize.apply(this, arguments); |
|---|
| | 30 | } |
|---|
| | 31 | }; |
|---|
| | 32 | |
|---|
| | 33 | if (declarations) { |
|---|
| | 34 | // runs when we call __inherit(ctor, ownPrototype) below |
|---|
| | 35 | var ownPrototype = function() { |
|---|
| | 36 | for (var i in declarations) |
|---|
| | 37 | __attachClassProperty(i, declarations[i], this); |
|---|
| | 38 | __ensureDefaults(this, declarations); |
|---|
| | 39 | __ensureOwnPrototypeMemory(this, declarations); |
|---|
| | 40 | }; |
|---|
| | 41 | |
|---|
| | 42 | // every class extends another class or Object |
|---|
| | 43 | var base = declarations.extending || {}; |
|---|
| | 44 | |
|---|
| | 45 | // baseInstance is the prototype *of* our prototype |
|---|
| | 46 | var baseInstance = __inherit(ownPrototype, base); |
|---|
| | 47 | declarations['getBaseInstance'] = Class.encap(baseInstance); |
|---|
| | 48 | declarations['sup'] = __makeSup(); |
|---|
| | 49 | |
|---|
| | 50 | __markCriticalFunctions(declarations); |
|---|
| | 51 | __setupAttachDelete(declarations); |
|---|
| | 52 | __inherit(ctor, ownPrototype); |
|---|
| | 55 | // Returns a constructor identical to the original, except that the |
|---|
| | 56 | // specified object will become a part of the prototype chain of any |
|---|
| | 57 | // instantitated objects. The inheritance chain of the extending |
|---|
| | 58 | // class has precedence over the prototype chain of obj, the extended |
|---|
| | 59 | // object, so mimicking other Class.created objects will break those |
|---|
| | 60 | // objects' existing inheritance relationships. Time taken is linear |
|---|
| | 61 | // in the number of properties of the extending class. |
|---|
| | 62 | ctor['mimic'] = function(obj) { |
|---|
| | 63 | // Unfortunately it seems that you just can't use |
|---|
| | 64 | // DOM objects as prototypes for constructor functions |
|---|
| | 65 | // in Safari... how braindead is that? Ugh :( |
|---|
| | 66 | var __ctor = this; |
|---|
| | 67 | var fn = function(/*arguments*/) { |
|---|
| | 68 | __ctor.apply(this, arguments); |
|---|
| | 69 | }; |
|---|
| | 70 | Object.extend(obj, __ctor.prototype); |
|---|
| | 71 | __ensureDefaults(obj, __ctor.prototype); |
|---|
| | 72 | fn.prototype = obj; |
|---|
| | 73 | return fn; |
|---|
| | 74 | }; |
|---|
| | 75 | |
|---|
| | 76 | return ctor; |
|---|
| | 77 | }; |
|---|
| | 78 | |
|---|
| | 79 | // superclass can be constructor function or any existing object |
|---|
| | 80 | var __inherit = function(subclass, superclass) { |
|---|
| | 81 | if (typeof(superclass) == 'function') { |
|---|
| | 82 | superclass.getInstance = superclass.getInstance || |
|---|
| | 83 | Class.encap(new superclass(__doNotInit)); |
|---|
| | 84 | subclass.prototype = superclass.getInstance(); |
|---|
| | 85 | } else if (typeof(superclass) == 'object') { |
|---|
| | 86 | subclass.prototype = superclass; |
|---|
| | 87 | superclass.initialize = superclass.constructor; |
|---|
| | 88 | } |
|---|
| | 89 | return subclass.prototype; |
|---|
| | 90 | }; |
|---|
| | 91 | |
|---|
| | 92 | // Functions marked here will be denied this.sup and privacy benefits, |
|---|
| | 93 | // but will be slightly faster because they will be called directly. |
|---|
| | 94 | var __markCriticalFunctions = function(declarations) { |
|---|
| | 95 | var critical = declarations.critical; |
|---|
| | 96 | if (typeof(critical) == 'object') { |
|---|
| | 97 | for (var i in critical) { |
|---|
| | 98 | critical[i]._critical = true; |
|---|
| | 99 | declarations[i] = critical[i]; |
|---|
| | 100 | } |
|---|
| | 101 | } |
|---|
| | 102 | }; |
|---|
| | 103 | |
|---|
| | 104 | var __setupAttachDelete = function(declarations) { |
|---|
| | 105 | // these two methods allow runtime method attachment and deletion, |
|---|
| | 106 | // with all the benefits of this.sup and privacy restrictions |
|---|
| | 107 | declarations['attachMethod'] = function(fnName, func, |
|---|
| | 108 | /*optional*/ critical) { |
|---|
| | 109 | // can't replace existing methods |
|---|
| | 110 | if (!this.ownPrototypeHas(fnName)) { |
|---|
| | 111 | func._critical = !!critical; |
|---|
| | 112 | this._runTimeDeclarations[fnName] = true; |
|---|
| | 113 | __attachClassProperty(fnName, func, this); |
|---|
| | 114 | } |
|---|
| | 115 | }; |
|---|
| | 116 | declarations['deleteMethod'] = function(fnName) { |
|---|
| | 117 | // can only delete non-runtime additions |
|---|
| | 118 | if (this._runTimeDeclarations[fnName]) { |
|---|
| | 119 | __attachClassProperty(fnName, null, this, true); |
|---|
| | 120 | this._runTimeDeclarations[fnName] = false; |
|---|
| | 121 | } |
|---|
| | 122 | }; |
|---|
| | 123 | }; |
|---|
| | 124 | |
|---|
| | 125 | var __attachClassProperty = function(propName, prop, obj, |
|---|
| | 126 | /*optional*/ removeInstead) { |
|---|
| | 127 | if (removeInstead) { |
|---|
| | 128 | if (obj.ownPrototypeHas(propName)) delete obj[propName]; |
|---|
| | 129 | } else { |
|---|
| | 130 | obj[propName] = prop; |
|---|
| | 131 | if (typeof(prop) == 'function') { |
|---|
| | 132 | if (!prop._critical && typeof(prop.body) != 'function') |
|---|
| | 133 | __addStackLogic(obj, propName); |
|---|
| | 134 | var __origStr = prop.toString(); // never changes |
|---|
| | 135 | prop.toString = obj[propName].toString = function() { |
|---|
| | 136 | return __origStr.replace(/^\s*function/, propName); |
|---|
| | 137 | }; |
|---|
| | 138 | } |
|---|
| | 139 | } |
|---|
| | 140 | }; |
|---|
| | 141 | |
|---|
| | 142 | // special-purpose array object (slightly faster in tests) |
|---|
| | 143 | var __callStack = new (function() { |
|---|
| | 144 | var stack = []; var index = 0; |
|---|
| | 145 | this.push = function(what) { stack[index++] = what; } |
|---|
| | 146 | this.pop = function() { return stack[--index]; } |
|---|
| | 147 | this.empty = function() { return index == 0; } |
|---|
| | 148 | this.peek = function() { return stack[index - 1]; } |
|---|
| | 149 | })(); |
|---|
| | 150 | |
|---|
| | 151 | var __lastOnCallStack = function() { |
|---|
| | 152 | if (__callStack.empty()) return ['', null]; |
|---|
| | 153 | else return __callStack.peek(); |
|---|
| | 154 | }; |
|---|
| | 155 | |
|---|
| | 156 | var __addStackLogic = function(obj, fnName) { |
|---|
| | 157 | switch (fnName) { |
|---|
| | 158 | case 'sup': case 'valueOf': |
|---|
| | 159 | case 'attachMethod': case 'deleteMethod': |
|---|
| | 160 | case 'getBaseInstance': return; |
|---|
| | 161 | default: { |
|---|
| | 162 | var __func = obj[fnName]; |
|---|
| | 163 | obj[fnName] = function() { |
|---|
| | 164 | // this privacy check can be omitted for slight performance |
|---|
| | 165 | // gains in production code (privacy is a development tool) |
|---|
| | 166 | var lastRef = __refOfLastCallingObject(); |
|---|
| | 167 | if (Class.followsPrivateNamingConvention(fnName) && |
|---|
| | 168 | (!lastRef || lastRef.constructor !== this.constructor)) { |
|---|
| | 169 | Class.privateCallError(fnName); |
|---|
| | 170 | } else { |
|---|
| | 171 | __callStack.push([fnName, this]); |
|---|
| | 172 | try { // don't let exceptions corrupt the callstack |
|---|
| | 173 | var result = __func.apply(this, arguments); |
|---|
| | 174 | } catch (exception) { |
|---|
| | 175 | throw exception; |
|---|
| | 176 | } finally { |
|---|
| | 177 | __callStack.pop(); |
|---|
| | 178 | } |
|---|
| | 179 | return result; |
|---|
| | 180 | } |
|---|
| | 181 | }; |
|---|
| | 182 | } |
|---|
| | 183 | obj[fnName].body = __func; // preserve original |
|---|
| | 184 | // e.g. obj[fnName].body.call(obj, arg1, ...) |
|---|
| | 185 | } |
|---|
| | 186 | }; |
|---|
| | 187 | |
|---|
| | 188 | Function.prototype.grant = function(obj) { |
|---|
| | 189 | var __func = this; |
|---|
| | 190 | var __callStackMemory = __lastOnCallStack(); |
|---|
| | 191 | return function() { |
|---|
| | 192 | __callStack.push(__callStackMemory); |
|---|
| | 193 | try { // don't let exceptions corrupt the callstack |
|---|
| | 194 | var result = __func.apply(obj || this, arguments); |
|---|
| | 195 | } catch (exception) { |
|---|
| | 196 | throw exception; |
|---|
| | 197 | } finally { |
|---|
| | 198 | __callStack.pop(); |
|---|
| | 199 | } |
|---|
| | 200 | return result; |
|---|
| | 201 | } |
|---|
| | 202 | }; |
|---|
| | 203 | |
|---|
| | 204 | // consider exposing these (they expose nothing but useful info) |
|---|
| | 205 | var __nameOfLastCalledMethod = function() { |
|---|
| | 206 | return __lastOnCallStack()[0]; |
|---|
| | 207 | }; |
|---|
| | 208 | |
|---|
| | 209 | var __refOfLastCallingObject = function() { |
|---|
| | 210 | return __lastOnCallStack()[1]; |
|---|
| | 211 | }; |
|---|
| | 212 | |
|---|
| | 213 | // change this at will |
|---|
| | 214 | this.followsPrivateNamingConvention = function(propName) { |
|---|
| | 215 | return propName.charAt(0) == '_'; |
|---|
| | 216 | }; |
|---|
| | 217 | |
|---|
| | 218 | this.privateCallError = function(fnName) { |
|---|
| | 219 | alert("Error: Tried to access private member " + fnName + |
|---|
| | 220 | " from from non-member method " + |
|---|
| | 221 | __nameOfLastCalledMethod()); |
|---|
| | 222 | }; |
|---|
| | 223 | |
|---|
| | 224 | var __ensureDefaults = function(ownPrototype, declarations) { |
|---|
| | 225 | ['toString', 'toLocaleString', 'valueOf'].each(function(prop) { |
|---|
| | 226 | __attachClassProperty(prop, declarations[prop], ownPrototype); |
|---|
| | 227 | }); |
|---|
| | 228 | }; |
|---|
| | 229 | |
|---|
| | 230 | var __ensureOwnPrototypeMemory = function(ownPrototype, declarations) { |
|---|
| | 231 | ownPrototype.ownPrototypeHas = function(property) { |
|---|
| | 232 | return (typeof(declarations[property]) != 'undefined' || |
|---|
| | 233 | (this._runTimeDeclarations && // present if instantiated |
|---|
| | 234 | this._runTimeDeclarations[property])); |
|---|
| | 235 | }; |
|---|
| | 236 | }; |
|---|
| | 237 | |
|---|
| | 238 | // convenient mechanism for creating accessor functions |
|---|
| | 239 | this.encap = function(__val, mutable) { |
|---|
| | 240 | if (!mutable) return function() { return __val; } |
|---|
| | 241 | else return function(/* optional new value */) { |
|---|
| | 242 | if (arguments.length > 0) __val = arguments[0]; |
|---|
| | 243 | return __val; |
|---|
| | 244 | } |
|---|
| | 245 | }; |
|---|
| | 246 | |
|---|
| | 247 | var __makeSup = function() { |
|---|
| | 248 | var __platform = false; // where to start lookup |
|---|
| | 249 | |
|---|
| | 250 | return function() { |
|---|
| | 251 | var method = __nameOfLastCalledMethod(); |
|---|
| | 252 | var curObj = __platform || this; |
|---|
| | 253 | |
|---|
| | 254 | // walk up inheritance chain until we've found the first class that |
|---|
| | 255 | // actually implements the method from which this.sup was called |
|---|
| | 256 | while ( curObj.ownPrototypeHas && |
|---|
| | 257 | !curObj.ownPrototypeHas(method) && |
|---|
| | 258 | curObj.getBaseInstance) |
|---|
| | 259 | curObj = curObj.getBaseInstance(); |
|---|
| | 260 | curObj = curObj.getBaseInstance |
|---|
| | 261 | ? curObj.getBaseInstance() |
|---|
| | 262 | : {}; // the ultimate baseInstance |
|---|
| | 263 | |
|---|
| | 264 | var prevBase = __platform; |
|---|
| | 265 | __platform = curObj; |
|---|
| | 266 | try { // don't let exceptions break the lookup |
|---|
| | 267 | var result = curObj[method].apply(this, arguments); |
|---|
| | 268 | } catch (exception) { |
|---|
| | 269 | throw exception; |
|---|
| | 270 | } finally { |
|---|
| | 271 | __platform = prevBase; |
|---|
| | 272 | } |
|---|
| | 273 | return result; |
|---|
| | 274 | }; |
|---|
| | 275 | }; |
|---|
| | 276 | |
|---|
| | 277 | })(); // good place to pass in parameters that determine what features are |
|---|
| | 278 | // enabled (production, development, debug, etc.) |
|---|
| | 279 | |
|---|
| | 280 | var createClass = Class.create; |
|---|
| | 281 | |
|---|