Created
August 25, 2011 09:47
-
-
Save jido/1170337 to your computer and use it in GitHub Desktop.
The dodo object model: a Javascript adaptation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"/> | |
<title>Test JS</title> | |
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/> | |
<link rel="stylesheet" type="text/css" href=".css"/> | |
<script> | |
"use strict"; | |
var dodo = {}; | |
if ("console" in window && "log" in console) { | |
dodo.log = function() { | |
console.log.apply(console, arguments); | |
}; | |
dodo.dumpLog = function() {}; | |
} | |
else { | |
dodo.logMessages = ""; | |
dodo.log = function() { | |
var line = ""; | |
var len = arguments.length; | |
for (var i = 0; i < len; i++) { | |
line += arguments[i]; | |
} | |
dodo.logMessages += line + "\n"; | |
}; | |
dodo.dumpLog = function() { | |
throw new Error(dodo.logMessages); | |
}; | |
} | |
dodo.log("Loading dodo object model"); | |
if ("create" in Object) { | |
dodo.create = function(o) { | |
return Object.create(o); | |
}; | |
} | |
else { | |
dodo.create = function (o) { | |
function F() {} | |
F.prototype = o; | |
return new F(); | |
}; | |
} | |
dodo.definePropertyFailback = function (o, name, descriptor) { | |
if ("value" in descriptor) { | |
o[name] = descriptor.value; | |
} | |
else if ("get" in descriptor || "set" in descriptor) { | |
throw new Error("dodo._defineProperty implementation only accepts data descriptors: property: " + name); | |
} | |
// ignore other cases | |
}; | |
dodo.canDefineProperty = false; | |
if ("defineProperty" in Object) { | |
try { | |
// The following fails in IE8 | |
Object.defineProperty({}, "dummy", {value: 1, writable: true}); | |
dodo.canDefineProperty = true; | |
} | |
catch (someException) { | |
dodo.log("Object.defineProperty defined, but fails on non-DOM objects!"); | |
} | |
} | |
if (dodo.canDefineProperty) { | |
dodo._defineProperty = function(o, name, descriptor) { | |
try { | |
Object.defineProperty(o, name, descriptor); | |
} | |
catch (someException) { | |
dodo.log("dodo._defineProperty: ", someException); | |
dodo.definePropertyFailback(o, name, descriptor); | |
} | |
}; | |
} | |
else { | |
dodo._defineProperty = dodo.definePropertyFailback; | |
dodo.log("Define object property not available!"); | |
} | |
dodo.defaultConstructor = function(constructorList, args) { | |
var constructor = constructorList[args.length]; | |
if (typeof constructor === "undefined") { | |
throw new Error("dodo.defaultConstructor: No constructor with arity: " + args.length); | |
} | |
constructor.apply(this, args); | |
}; | |
dodo.copy = function(proto) { | |
// dodo.log("in copy: proto: " + proto); | |
var superProto = this.prototype; | |
var self = dodo.create(proto); | |
var field; | |
var i = 2; | |
while (i < arguments.length) { | |
field = arguments[i-1]; | |
// dodo.log("in copy: setting field: " + field); | |
if (!(field in superProto)) { | |
throw new Error("copy: unknown field: " + field); | |
} | |
if (typeof self[field] === "function") { | |
throw new Error("copy: not an attribute of object: " + field); | |
} | |
dodo._defineProperty(self, field, {value: arguments[i], writable: false, enumerable: true}); | |
i += 2; | |
} | |
if (i === arguments.length) { | |
var extension = arguments[i-1]; | |
for (field in extension) { | |
// dodo.log("in copy: adding field: " + field); | |
if (field in superProto) { | |
throw new Error("copy: field already exists: " + field); | |
} | |
var value = extension[field]; | |
if (field.lastIndexOf("$", 0) === 0) | |
{ | |
field = field.substr(1); | |
if (!(field in superProto)) { | |
throw new Error("copy: cannot override unknown field: " + field); | |
} | |
} | |
dodo._defineProperty(self, field, {value: value, writable: false, enumerable: true}); | |
} | |
} | |
if (typeof proto === "function") { | |
// Assume constructor | |
var MakeInstance = function() { | |
proto.apply(this, arguments); | |
}; | |
MakeInstance.prototype = self.prototype; | |
MakeInstance.constructor = self.constructor; | |
return MakeInstance; | |
} | |
return self; | |
} | |
function copy(proto) { | |
var Super = proto.constructor; | |
return dodo.copy.apply(Super, arguments); | |
} | |
function Default() { | |
dodo._defineProperty(this, "constructor", {value: Default, writable: true}); | |
} | |
Default.prototype = { | |
toString: function() { | |
var desc = "{"; | |
for (var attr in this) { | |
var val = this[attr]; | |
if (typeof val === "function") { | |
if (!this.hasOwnProperty(attr)) continue; | |
val = "function()"; | |
} | |
desc += attr + ": " + val + ","; | |
} | |
return desc.replace(/,?$/, "}"); | |
} | |
}; | |
var struct = new Default(); | |
function Class(declarations) { | |
// Create the prototype of the objects of the class | |
var proto = dodo.copy.call(this, this.prototype, declarations); | |
// Class constructor | |
var MakeInstance; | |
if (arguments.length < 2) { | |
// Inherit parent constructors | |
var Super = this.constructor.prototype; | |
} | |
else { | |
// New list of constructors | |
var constructorList = arguments[1]; | |
} | |
function MakeInstanceInherited() { | |
// call super | |
Super.apply(this, arguments); | |
dodo._defineProperty(this, "constructor", {value: MakeInstanceInherited, writable: true}); | |
} | |
function MakeInstanceWithConstructors() { | |
dodo.defaultConstructor.call(this, constructorList, arguments); | |
dodo._defineProperty(this, "constructor", {value: MakeInstanceWithConstructors, writable: true}); | |
} | |
MakeInstance = (arguments.length < 2? MakeInstanceInherited: MakeInstanceWithConstructors); | |
MakeInstance.prototype = proto; | |
// Metaclass constructor (for extending the class) | |
function MakeClass() { | |
return Class.apply(this, arguments); | |
} | |
MakeClass.prototype = MakeInstance; | |
MakeInstance.constructor = MakeClass; | |
return MakeInstance; | |
} | |
Class.prototype = Default; | |
function Link(T) { | |
this.$ = (typeof T === "function"? new T(): ""); | |
} | |
Link.prototype = { | |
toString: function() { | |
return (typeof this.$ === "object"? "Link()": this.$); | |
} | |
}; | |
function linkTo(val) { | |
var ln = new Link(); | |
ln.$ = val; | |
return ln; | |
} | |
</script> | |
</head> | |
<body> | |
Script output: | |
<script> | |
var Point = new Class({x: 0, y: 0}, { | |
0: function() {}, | |
2: function(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
}); | |
var p = new Point(11, 22); | |
document.write("<br>", p); | |
var QPoint = new Point.constructor({$y: 4}); | |
document.write("<br>", new QPoint()); | |
var RPoint = new QPoint.constructor({ | |
rho2: function() { | |
var origin = this.constructor.prototype; | |
var dx = this.x - origin.x; | |
var dy = this.y - origin.y; | |
return dx * dx + dy * dy; | |
} | |
}); | |
var r = new RPoint(4, 7); | |
document.write("<br>", r, " distance from origin = ", Math.sqrt(r.rho2())); | |
var s = copy(p, 'y', 10, {rho2: r.rho2}); | |
document.write("<br>", s, " distance from origin = ", Math.sqrt(s.rho2())); | |
dodo.dumpLog(); | |
</script> | |
</body> | |
</html> |
Fixed.
Giving up on immutability, it gives inconsistent results. Pretend dodo variables are immutable (except for links).
Created repo here:
http://github.com/jido/Dodo.js
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Line 220: TypeError: Return value of 'dodo.copy' [undefined] is not an object.