Skip to content

Instantly share code, notes, and snippets.

@jido
Created August 25, 2011 09:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jido/1170337 to your computer and use it in GitHub Desktop.
Save jido/1170337 to your computer and use it in GitHub Desktop.
The dodo object model: a Javascript adaptation
<!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>
@jido
Copy link
Author

jido commented Aug 26, 2011

Mobile Safari - iPhone 4
Line 28: TypeError: Type error

@jido
Copy link
Author

jido commented Aug 26, 2011

Line 220: TypeError: Return value of 'dodo.copy' [undefined] is not an object.

@jido
Copy link
Author

jido commented Aug 26, 2011

Fixed.

@jido
Copy link
Author

jido commented Aug 30, 2011

Giving up on immutability, it gives inconsistent results. Pretend dodo variables are immutable (except for links).

@jido
Copy link
Author

jido commented Sep 10, 2011

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