I've read a lot of stuff about mixins, inheritance and such, and in the end I came up with this solution for extending a class with multiple mixins. I haven't seen this anywhere else...
Is this a good method? Do you see any problems with it?
What I'm trying to achieve:
I need classes that share properties and methods, and extend methods.
The code is used in two separate projects: a viewer and an editor. The editor adds code on top of the viewer's to add functionality to the classes.
Ex: img
class is a base object and a positional object. It has a setAttribute
method which also calls base.setAttribute
and pos.setAttribute
.
"page" class is a base object (but not a position object) and so on. With an additional JavaScript, these objects are expanded further for editing capabilities.
This is a part of the actual code (viewer):
var Objects = {};
Objects.extend = function () {
for (var i = 1; i < arguments.length; i++) {
Objects[arguments[i]].call(Objects[arguments[0]].prototype);
}
}
// mixin base object
Objects.__base = function () {
this.init = function (e, obj) {
this.e = e;
this.visible = true;
this.type = "";
this.container = null;
this.attributes = {};
this.obj = obj; // dictionary object
}
// check absolute visibility, containers...
this.isVisible = function () {
return (this.visible && (!this.container || this.container.isVisible()));
}
this.getAttribute = function (name) {
return this.attributes[name];
}
this.updateAttribute = function (name, value) {
if (value != null) {
this.attributes[name] = value;
return true;
} else {
return false;
}
}
this.setAttribute = function (name, value) {
var v;
// general attributes (for every kind of object)
switch (name) {
case "id":
v = value;
this.e.setAttribute("data-id",v);
break;
case "visible":
v = parseBool(value != "false" && value != false);
// show/hide?
if (this.visible != v) {
this.e.style.display = v?"inline":"none";
this.visible = v;
}
break;
} // switch general attributes
return this.updateAttribute(name, v);
} // setAttribute()
} // Objects.__base mixin
// mixin base positional object
Objects.__pos = function () {
var _setAttribute = this.setAttribute;
this.setAttribute = function (name, value) {
var v;
switch (name) {
case "x":
case "y":
v = parseInt(value);
this.e.style[name=="x"?"left":"top"] = v+"px";
break;
default:
return _setAttribute.call(this, name, value);
} // switch general attributes
return this.updateAttribute(name, v);
} // setAttribute()
} // Objects.__pos mixin
// image class
Objects.image = function () {
// set attribute
var _setAttribute = this.setAttribute;
this.setAttribute = function (name, value) {
var v;
// general attributes (for every kind of object)
switch (name) {
case "width":
case "height":
v = parseInt(value);
var img = this.e.getElementsByTagName("img")[0];
img.style[name] = v+"px";
break;
default:
return _setAttribute.call(this, name, value);
}
return this.updateAttribute(name, v);
} // setAttribute()
} // Objects.image class
Objects.extend("image","__base","__pos");
And then, in the additional editor JavaScript, I extend all of the objects to add editing capabilities that I don't need on the viewer:
Objects.__editorObj = function () {
var _init = this.init;
this.init = function (e, obj) {
if (_init) _init.call(this, e, obj);
// absolute coordinates (relative to the page), set by openContainer
this.ax = 0;
this.ay = 0;
}
this.move = function (dx, dy) {
// ...
}
}
for (var o in Objects) // extend all the objects with editing capabilities
if (o.charAt(0)!="_")
Objects.extend(o,"__editorObj");
I'd also like to have private attributes and methods in the mixins, but I don't know how. I tried to use mixins because I read that trying to use JavaScript as a class based language is not a good practice.