function Smalltalk(){}; function SmalltalkObject(){}; function SmalltalkBehavior(){}; function SmalltalkClass(){}; function SmalltalkMetaclass(){ this.meta = true; }; function SmalltalkMethod(){}; function SmalltalkNil(){}; var nil = new SmalltalkNil(); var smalltalk = new Smalltalk(); smalltalk.klass = function(spec) { var spec = spec || {}; var that; if(spec.meta) { that = new SmalltalkMetaclass(); } else { that = new (smalltalk.klass({meta: true})).fn; that.klass.instanceClass = that; that.className = spec.className; that.klass.className = that.className + ' class'; } that.fn = spec.fn || function(){}; that.superclass = spec.superclass; that.iVarNames = spec.iVarNames || []; if(that.superclass) { that.klass.superclass = that.superclass.klass; } that.category = spec.category || ""; that.fn.prototype.methods = {}; that.fn.prototype.klass = that; return that; }; smalltalk.method = function(spec) { var that = new SmalltalkMethod(); that.selector = spec.selector; that.category = spec.category; that.source = spec.source; that.fn = spec.fn; return that }; smalltalk.init = function(klass) { var subclasses = smalltalk.subclasses(klass); for(var i=0;i<klass.iVarNames.length;i++) { klass.fn.prototype["@"+klass.iVarNames[i]] = nil; } if(klass.superclass && klass.superclass !== nil) { klass.fn.prototype.__proto__ = klass.superclass.fn.prototype; for(var i=0;i<klass.superclass.iVarNames.length;i++) { if(!klass["@"+klass.superclass.iVarNames[i]]) { klass.fn.prototype["@"+klass.superclass.iVarNames[i]] = nil; } } } for(var i=0;i<subclasses.length;i++) { smalltalk.init(subclasses[i]); } if(klass.klass && !klass.meta) { smalltalk.init(klass.klass); } }; smalltalk.classes = function() { var classes = []; for(var i in smalltalk) { if(i.search(/^[A-Z]/g) != -1) { classes.push(smalltalk[i]); } } return classes }; smalltalk.subclasses = function(klass) { var subclasses = []; var classes = smalltalk.classes(); for(var i in classes) { if(classes[i].fn) { //Metaclasses if(classes[i].klass && classes[i].klass.superclass === klass) { subclasses.push(classes[i].klass); } //Classes if(classes[i].superclass === klass) { subclasses.push(classes[i]); } } } return subclasses; }; smalltalk.mapClassName = function(className, category, fn, superclass) { smalltalk[className] = smalltalk.klass({ className: className, category: category, superclass: superclass, fn: fn }); }; smalltalk.addClass = function(className, superclass, iVarNames, category) { if(smalltalk[className]) { smalltalk[className].superclass = superclass; smalltalk[className].iVarNames = iVarNames; smalltalk[className].category = category || smalltalk[className].category; } else { smalltalk[className] = smalltalk.klass({ className: className, iVarNames: iVarNames, superclass: superclass }); smalltalk[className].category = category || ''; } }; smalltalk.addMethod = function(jsSelector, method, klass) { klass.fn.prototype[jsSelector] = method.fn; klass.fn.prototype.methods[method.selector] = method; }; smalltalk.mapClassName("Object", "Kernel", SmalltalkObject); smalltalk.mapClassName("Smalltalk", "Kernel", Smalltalk, smalltalk.Object); smalltalk.mapClassName("Behavior", "Kernel", SmalltalkBehavior, smalltalk.Object); smalltalk.mapClassName("Class", "Kernel", SmalltalkClass, smalltalk.Behavior); smalltalk.mapClassName("Metaclass", "Kernel", SmalltalkMetaclass, smalltalk.Behavior); smalltalk.mapClassName("CompiledMethod", "Kernel", SmalltalkMethod, smalltalk.Object); smalltalk.Object.klass.superclass = smalltalk.Class smalltalk.mapClassName("Number", "Kernel", Number, smalltalk.Object); smalltalk.mapClassName("BlockClosure", "Kernel", Function, smalltalk.Object); smalltalk.mapClassName("Boolean", "Kernel", Boolean, smalltalk.Object); smalltalk.mapClassName("Date", "Kernel", Date, smalltalk.Object); smalltalk.mapClassName("UndefinedObject", "Kernel", SmalltalkNil, smalltalk.Object); smalltalk.mapClassName("Collection", "Kernel", null, smalltalk.Object); smalltalk.mapClassName("String", "Kernel", String, smalltalk.Collection); smalltalk.mapClassName("RegularExpression", "Kernel", RegExp, smalltalk.String); smalltalk.mapClassName("Array", "Kernel", Array, smalltalk.Collection); if(CanvasRenderingContext2D) { smalltalk.mapClassName("CanvasRenderingContext", "Canvas", CanvasRenderingContext2D, smalltalk.Object); }