<html> <head> <title>Example</title> <script type="text/javascript"> /* * xbObjects.js * $Revision: 1.2 $ $Date: 2003/02/07 16:04:20 $ */
/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Bob Clary code. * * The Initial Developer of the Original Code is * Bob Clary. * Portions created by the Initial Developer are Copyright (C) 2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): Bob Clary <bc@bclary.com> * * ***** END LICENSE BLOCK ***** */
/* ChangeLog: 2001-12-19 - bclary - changed xbException init method to remove possible exception due to permission denied issues in gecko 0.9.5+ */
function _Classes() { if (typeof(_classes) != 'undefined') throw('Only one instance of _Classes() can be created');
function registerClass(className, parentClassName) { if (!className) throw('xbObjects.js:_Classes::registerClass: className missing');
if (className in _classes) return;
if (className != 'xbObject' && !parentClassName) parentClassName = 'xbObject';
if (!parentClassName) parentClassName = null; else if ( !(parentClassName in _classes)) throw('xbObjects.js:_Classes::registerClass: parentClassName ' + parentClassName + ' not defined');
// evaluating and caching the prototype object in registerClass // works so long as we are dealing with 'normal' source files // where functions are created in the global context and then // statements executed. when evaling code blocks as in xbCOM, // this no longer works and we need to defer the prototype caching // to the defineClass method
_classes[className] = { 'classConstructor': null, 'parentClassName': parentClassName }; } _Classes.prototype.registerClass = registerClass;
function defineClass(className, prototype_func) { var p;
if (!className) throw('xbObjects.js:_Classes::defineClass: className not given');
var classRef = _classes[className]; if (!classRef) throw('xbObjects.js:_Classes::defineClass: className ' + className + ' not registered');
if (classRef.classConstructor) return;
classRef.classConstructor = eval( className ); var childPrototype = classRef.classConstructor.prototype; var parentClassName = classRef.parentClassName;
if (parentClassName) { var parentClassRef = _classes[parentClassName]; if (!parentClassRef) throw('xbObjects.js:_Classes::defineClass: parentClassName ' + parentClassName + ' not registered');
if (!parentClassRef.classConstructor) { // force parent's prototype to be created by creating a dummy instance // note constructor must handle 'default' constructor case var dummy; eval('dummy = new ' + parentClassName + '();'); }
var parentPrototype = parentClassRef.classConstructor.prototype;
for (p in parentPrototype) { switch (p) { case 'isa': case 'classRef': case 'parentPrototype': case 'parentConstructor': case 'inheritedFrom': break; default: childPrototype[p] = parentPrototype[p]; break; } } }
prototype_func();
childPrototype.isa = className; childPrototype.classRef = classRef;
// cache method implementor info childPrototype.inheritedFrom = new Object(); if (parentClassName) { for (p in parentPrototype) { switch (p) { case 'isa': case 'classRef': case 'parentPrototype': case 'parentConstructor': case 'inheritedFrom': break; default: if (childPrototype[p] == parentPrototype[p] && parentPrototype.inheritedFrom[p]) { childPrototype.inheritedFrom[p] = parentPrototype.inheritedFrom[p]; } else { childPrototype.inheritedFrom[p] = parentClassName; } break; } } } } _Classes.prototype.defineClass = defineClass; }
// create global instance var _classes = new _Classes();
// register root class xbObject _classes.registerClass('xbObject');
function xbObject() { _classes.defineClass('xbObject', _prototype_func);
this.init();
function _prototype_func() { // isa is set by defineClass() to the className // Note that this can change dynamically as the class is cast // into it's ancestors... xbObject.prototype.isa = null;
// classref is set by defineClass() to point to the // _classes entry for this class. This allows access // the original _class's entry no matter how it has // been recast. // *** This will never change!!!! *** xbObject.prototype.classRef = null;
xbObject.prototype.inheritedFrom = new Object();
function init() { } xbObject.prototype.init = init;
function destroy() {} xbObject.prototype.destroy = destroy;
function parentMethod(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) { // find who implemented this method var className = this.isa; var parentClassName = _classes[className].classConstructor.prototype.inheritedFrom[method]; var tempMethod = _classes[parentClassName].classConstructor.prototype[method]; // 'cast' this into the implementor of the method // so that if parentMethod is called by the parent's method, // the search for it's implementor will start there and not // cause infinite recursion this.isa = parentClassName; var retVal = tempMethod.call(this, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); this.isa = className;
return retVal; } xbObject.prototype.parentMethod = parentMethod;
function isInstanceOf(otherClassConstructor) { var className = this.isa; var otherClassName = otherClassConstructor.prototype.isa;
while (className) { if (className == otherClassName) return true;
className = _classes[className].parentClassName; }
return false; } xbObject.prototype.isInstanceOf = isInstanceOf; } }
// eof: xbObjects.js
</script> </head> <body> <script type="text/javascript"> _classes.registerClass("Shape");
function Shape(iSides) { _classes.defineClass("Shape", prototypeFunction); this.init(iSides); function prototypeFunction() { Shape.prototype.init = function(iSides) { this.parentMethod("init"); this.sides = iSides; }; Shape.prototype.getArea = function () { return 0; }; } }
_classes.registerClass("Triangle", "Shape");
function Triangle(iBase, iHeight) { _classes.defineClass("Triangle", prototypeFunction); this.init(iBase,iHeight); function prototypeFunction() { Triangle.prototype.init = function(iBase, iHeight) { this.parentMethod("init", 3); this.base = iBase; this.height = iHeight; }; Triangle.prototype.getArea = function () { return 0.5 * this.base * this.height; }; } }
_classes.registerClass("Rectangle", "Shape");
function Rectangle(iLength, iWidth) { _classes.defineClass("Rectangle", prototypeFunction); this.init(iLength, iWidth); function prototypeFunction() { Rectangle.prototype.init = function(iLength, iWidth) { this.parentMethod("init", 4); this.length = iLength; this.width = iWidth; } Rectangle.prototype.getArea = function () { return this.length * this.width; }; } }
var triangle = new Triangle(10, 40); var rectangle = new Rectangle(20, 50);
alert(triangle.sides); alert(triangle.getArea());
alert(rectangle.sides); alert(rectangle.getArea());
</script>
</body> </html>
|