| 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 && this.initialize) { |
|---|
| | 27 | this._runTimeDeclarations = {}; |
|---|
| | 28 | this.initialize.apply(this, arguments); |
|---|
| | 29 | } |
|---|
| | 30 | }; |
|---|
| | 31 | |
|---|
| | 32 | if (declarations) { |
|---|
| | 33 | // runs when we call __inherit(ctor, ownPrototype) below |
|---|
| | 34 | var ownPrototype = function() { |
|---|
| | 35 | for (var i in declarations) |
|---|
| | 36 | __attachClassProperty(i, declarations[i], this); |
|---|
| | 37 | __ensureDefaults(this, declarations); |
|---|
| | 38 | }; |
|---|
| | 39 | |
|---|
| | 40 | if (declarations.extending) { |
|---|
| | 41 | var baseInstance = // a constant-time operation: |
|---|
| | 42 | __inherit(ownPrototype, declarations.extending); |
|---|
| | 43 | declarations['getBaseInstance'] = Class.encap(baseInstance); |
|---|
| | 44 | declarations['sup'] = __makeSup(); |
|---|
| | 45 | } |
|---|
| | 46 | |
|---|
| | 47 | __markCriticalFunctions(declarations); |
|---|
| | 48 | __setupAttachDelete(declarations); |
|---|
| | 49 | __inherit(ctor, ownPrototype); |
|---|
| | 52 | return ctor; |
|---|
| | 53 | }; |
|---|
| | 54 | |
|---|
| | 55 | // superclass can be constructor function or existing object |
|---|
| | 56 | var __inherit = function(subclass, superclass) { |
|---|
| | 57 | if (typeof(superclass) == 'function') { |
|---|
| | 58 | superclass.getInstance = superclass.getInstance || |
|---|
| | 59 | Class.encap(new superclass(__doNotInit)); |
|---|
| | 60 | subclass.prototype = superclass.getInstance(); |
|---|
| | 61 | } else if (typeof(superclass) == 'object') |
|---|
| | 62 | subclass.prototype = superclass; |
|---|
| | 63 | return subclass.prototype; |
|---|
| | 64 | }; |
|---|
| | 65 | |
|---|
| | 66 | // Functions marked here will be denied this.sup and privacy benefits, |
|---|
| | 67 | // but will be faster because they will be called directly. |
|---|
| | 68 | var __markCriticalFunctions = function(declarations) { |
|---|
| | 69 | var critical = declarations.critical; |
|---|
| | 70 | if (typeof(critical) == 'object') { |
|---|
| | 71 | for (var i in critical) { |
|---|
| | 72 | critical[i]._critical = true; |
|---|
| | 73 | declarations[i] = critical[i]; |
|---|
| | 74 | } |
|---|
| | 75 | } |
|---|
| | 76 | }; |
|---|
| | 77 | |
|---|
| | 78 | var __setupAttachDelete = function(declarations) { |
|---|
| | 79 | // these two methods allow runtime method attachment and deletion, |
|---|
| | 80 | // with all the benefits of this.sup and privacy restrictions |
|---|
| | 81 | declarations['attachMethod'] = function(fnName, func, |
|---|
| | 82 | /*optional*/ critical) { |
|---|
| | 83 | // can't replace existing methods |
|---|
| | 84 | if (!this.ownPrototypeHas(fnName)) { |
|---|
| | 85 | func._critical = !!critical; |
|---|
| | 86 | this._runTimeDeclarations[fnName] = true; |
|---|
| | 87 | __attachClassProperty(fnName, func, this); |
|---|
| | 88 | } |
|---|
| | 89 | }; |
|---|
| | 90 | declarations['deleteMethod'] = function(fnName) { |
|---|
| | 91 | // can only delete non-runtime additions |
|---|
| | 92 | if (this._runTimeDeclarations[fnName]) { |
|---|
| | 93 | __attachClassProperty(fnName, null, this, true); |
|---|
| | 94 | this._runTimeDeclarations[fnName] = false; |
|---|
| | 95 | } |
|---|
| | 96 | }; |
|---|
| | 97 | }; |
|---|
| | 98 | |
|---|
| | 99 | var __attachClassProperty = function(propName, prop, obj, |
|---|
| | 100 | /*optional*/ removeInstead) { |
|---|
| | 101 | if (removeInstead) { |
|---|
| | 102 | if (obj.ownPrototypeHas(propName)) delete obj[propName]; |
|---|
| | 103 | } else { |
|---|
| | 104 | obj[propName] = prop; |
|---|
| | 105 | if (!prop._critical && typeof(prop) == 'function') |
|---|
| | 106 | __addStackLogic(obj, propName); |
|---|
| | 107 | } |
|---|
| | 108 | }; |
|---|
| | 109 | |
|---|
| | 110 | // special-purpose array object (slightly faster in tests) |
|---|
| | 111 | var __callStack = new (function() { |
|---|
| | 112 | var stack = []; var index = 0; |
|---|
| | 113 | this.push = function(what) { stack[index++] = what; } |
|---|
| | 114 | this.pop = function() { return stack[--index]; } |
|---|
| | 115 | this.empty = function() { return index == 0; } |
|---|
| | 116 | this.peek = function() { return stack[index - 1]; } |
|---|
| | 117 | })(); |
|---|
| | 118 | |
|---|
| | 119 | var __lastOnCallStack = function() { |
|---|
| | 120 | if (__callStack.empty()) return ['', null]; |
|---|
| | 121 | else return __callStack.peek(); |
|---|
| | 122 | }; |
|---|
| | 123 | |
|---|
| | 124 | var __addStackLogic = function(obj, fnName) { |
|---|
| | 125 | switch (fnName) { |
|---|
| | 126 | case 'sup': case 'toString': case 'valueOf': |
|---|
| | 127 | case 'attachMethod': case 'deleteMethod': |
|---|
| | 128 | case 'getBaseInstance': return; |
|---|
| | 129 | default: { |
|---|
| | 130 | var __func = obj[fnName]; |
|---|
| | 131 | obj[fnName] = function() { |
|---|
| | 132 | // this privacy check can be omitted for slight performance |
|---|
| | 133 | // gains in production code (privacy is a development tool) |
|---|
| | 134 | var lastRef = __refOfLastCallingObject(); |
|---|
| | 135 | if (Class.followsPrivateNamingConvention(fnName) && |
|---|
| | 136 | (lastRef || {}).constructor !== this.constructor) { |
|---|
| | 137 | Class.privateCallError(fnName); |
|---|
| | 138 | } else { |
|---|
| | 139 | __callStack.push([fnName, this]); |
|---|
| | 140 | var result = __func.apply(this, arguments); |
|---|
| | 141 | __callStack.pop(); |
|---|
| | 142 | return result; |
|---|
| | 143 | } |
|---|
| | 144 | }; |
|---|
| | 145 | } |
|---|
| | 146 | obj[fnName].core = __func; // preserve original |
|---|
| | 147 | // e.g. obj[fnName].core.call(obj, arg1, ...) |
|---|
| | 148 | } |
|---|
| | 149 | }; |
|---|
| | 150 | |
|---|
| | 151 | Function.prototype.grant = function(obj) { |
|---|
| | 152 | var __func = this; |
|---|
| | 153 | var __callStackMemory = __lastOnCallStack(); |
|---|
| | 154 | return function() { |
|---|
| | 155 | __callStack.push(__callStackMemory); |
|---|
| | 156 | var result = __func.apply(obj || this, arguments); |
|---|
| | 157 | __callStack.pop(); |
|---|
| | 158 | return result; |
|---|
| | 159 | } |
|---|
| | 160 | }; |
|---|
| | 161 | |
|---|
| | 162 | // consider exposing these (they expose nothing but useful info) |
|---|
| | 163 | var __nameOfLastCalledMethod = function() { |
|---|
| | 164 | return __lastOnCallStack()[0]; |
|---|
| | 165 | }; |
|---|
| | 166 | |
|---|
| | 167 | var __refOfLastCallingObject = function() { |
|---|
| | 168 | return __lastOnCallStack()[1]; |
|---|
| | 169 | }; |
|---|
| | 170 | |
|---|
| | 171 | // change this at will |
|---|
| | 172 | this.followsPrivateNamingConvention = function(propName) { |
|---|
| | 173 | return propName.charAt(0) == '_'; |
|---|
| | 174 | }; |
|---|
| | 175 | |
|---|
| | 176 | this.privateCallError = function(fnName) { |
|---|
| | 177 | alert("Error: Tried to access private member " + fnName + |
|---|
| | 178 | " from from non-member method " + |
|---|
| | 179 | __nameOfLastCalledMethod()); |
|---|
| | 180 | }; |
|---|
| | 181 | |
|---|
| | 182 | var __ensureDefaults = function(ownPrototype, declarations) { |
|---|
| | 183 | ['toString', |
|---|
| | 184 | 'toLocaleString', |
|---|
| | 185 | 'valueOf'].each(function(prop) { |
|---|
| | 186 | if (declarations[prop]) { |
|---|
| | 187 | __attachClassProperty(prop, declarations[prop], ownPrototype); |
|---|
| | 188 | } |
|---|
| | 189 | }); |
|---|
| | 190 | ownPrototype.ownPrototypeHas = function(property) { |
|---|
| | 191 | return (typeof(declarations[property]) != 'undefined' || |
|---|
| | 192 | (this._runTimeDeclarations && // present if instantiated |
|---|
| | 193 | this._runTimeDeclarations[property])); |
|---|
| | 194 | } |
|---|
| | 195 | }; |
|---|
| | 196 | |
|---|
| | 197 | var __makeSup = function(parentPrototype) { |
|---|
| | 198 | var __platform = false; // where to start lookup |
|---|
| | 199 | |
|---|
| | 200 | return function() { |
|---|
| | 201 | var origin = __nameOfLastCalledMethod(); |
|---|
| | 202 | var curObj = __platform || this; |
|---|
| | 203 | |
|---|
| | 204 | // walk up inheritance chain until we've found the first class that |
|---|
| | 205 | // actually implements the method from which this.sup was called |
|---|
| | 206 | while (!curObj.ownPrototypeHas(origin) |
|---|
| | 207 | && curObj.getBaseInstance) |
|---|
| | 208 | curObj = curObj.getBaseInstance(); |
|---|
| | 209 | curObj = curObj.getBaseInstance |
|---|
| | 210 | ? curObj.getBaseInstance() |
|---|
| | 211 | : {}; // origin won't be found here! |
|---|
| | 212 | |
|---|
| | 213 | var prevBase = __platform; |
|---|
| | 214 | __platform = curObj; |
|---|
| | 215 | var result = curObj[origin].apply(this, arguments); |
|---|
| | 216 | __platform = prevBase; |
|---|
| | 217 | return result; |
|---|
| | 218 | }; |
|---|
| | 219 | }; |
|---|
| | 220 | |
|---|
| | 221 | // convenient mechanism for creating accessor functions |
|---|
| | 222 | this.encap = function(__val, mutable) { |
|---|
| | 223 | if (!mutable) return function() { return __val; } |
|---|
| | 224 | else return function(/* optional new value */) { |
|---|
| | 225 | if (arguments.length > 0) __val = arguments[0]; |
|---|
| | 226 | return __val; |
|---|
| | 227 | } |
|---|
| | 228 | }; |
|---|
| | 229 | |
|---|
| | 230 | })(); // construct the Class object |
|---|
| | 231 | |
|---|
| | 232 | var createClass = Class.create; |
|---|
| | 233 | |
|---|