var EventEmitter = require("events").EventEmitter var _ = require("lodash"); var async = require("async"); function uberfactory(uuid, slugger) { var ignores = [ "_eventsCount", "off", "on", "_isdirty", "uninherits", "inherits", "unextends", "extends", "unisa", "isa", "relatesAsToWith", "test", "emita", "emitas", "data2", "getStuff", "getStuff2", "getStuff3", "pingup", "data", "metas2", "metas", "metas3", "exporta", "pickdeep", "withAllSync", "withAll", "innerExport", "exportFlatSync", "exportSync", "export2", "export", "domain", "_events", "rawListeners", "_maxListeners", "setMaxListeners", "getMaxListeners", "emit", "addListener", "on", "prependListener", "once", "prependOnceListener", "removeListener", "removeAllListeners", "listeners", "listenerCount", "eventNames", "_data", "path", "name", "children", "parents", "_inherits", "_extends", "_isa", "_hasa", "_relations", "_references", "_created", "_metas", "_rootname", "exportSerialised" ] return function factory(therootname) { var seed = therootname; var ROOT = new electra(therootname, true, false) function inner(name) { if (typeof(name) == "undefined") { return ROOT; } name = slugger(name); if (!ROOT.children[name]) { ROOT.children[name] = new electra(name); } return ROOT.children[name] } function electra(name, isroot, parent) { //name = slugger(name); this._data = {} this._metas = {}; this.path = (parent ? parent.path : "") + name; this.name = name; //this._id = uuid(); this.children = {}; this.parents = {}; this._inherits = {}; this._extends = {}; this._isa = {}; this._hasa = {}; this._relations = {}; this._references = {}; this._created = new Date(); this._rootname = therootname; var self = this; if (parent) { this.parents[parent.name] = parent; this.path = parent.path + "/" + name if (parent._metas.copyonchild) { _.map(parent._metas.copyonchild, function(k) { self._metas[k] = parent._metas[k]; }) } } if (isroot) { this.isroot = true; return this; } function xinner(name) { var args = Array.prototype.slice.apply(arguments, [0]); if (args.length > 1) { return args.map(function(n) { return xinner(n) }) } if (typeof(name) == "undefined") { return self; } name = slugger(name); if (name.indexOf("://") > -1) { if (!self.children[name]) { self.children[name] = new electra(name, false, self); self.pingup("created", self.children[name](), name) self.emit("added", name); } return self.children[name] } if (name.indexOf("/") > -1) { //its a path; var arr = name.split("/"); var newname = arr.shift(); if (!self.children[newname]) { self.children[newname] = new electra(newname, false, self); self.pingup("created", self.children[newname](), newname, arr) self.emit("added", newname); } return self.children[newname](arr.join("/")) } if (!self.children[name]) { self.children[name] = new electra(name, false, self); self.pingup("created", self.children[name](), name) } return self.children[name] } return xinner } electra.prototype.__proto__ = EventEmitter.prototype electra.prototype.inherits = function(obj_) { var obj = typeof(obj_) === "function" ? obj_() : typeof(obj_) === "object" ? obj_ : typeof(obj_) === "string" ? inner(obj_)() : false if (obj) { if (obj == this) { return this; } this._inherits[obj.path] = obj; obj._extends[this.path] = this; } return this; } electra.prototype.uninherits = function(obj_) { var obj = typeof(obj_) === "function" ? obj_() : typeof(obj_) === "object" ? obj_ : typeof(obj_) === "string" ? inner(obj_)() : false if (obj && this._inherits[obj.path]) { delete this._inherits[obj.path] delete obj._extends[this.path] } return this; } electra.prototype.extends = function(obj_) { var obj = typeof(obj_) === "function" ? obj_() : typeof(obj_) === "object" ? obj_ : typeof(obj_) === "string" ? inner(obj_)() : false if (obj) { if (obj == this) { return this; } this._extends[obj.path] = obj; obj._inherits[this.path] = this; } return this; } electra.prototype.unextends = function(obj_) { var obj = typeof(obj_) === "function" ? obj_() : typeof(obj_) === "object" ? obj_ : typeof(obj_) === "string" ? inner(obj_)() : false if (obj && this._extends[obj.path]) { delete this._extends[obj.path] delete obj._inherits[this.path] } return this; } electra.prototype.isa = function(elef) { var self = this; var args = Array.prototype.slice.apply(arguments, [0]); if (args.length > 1) { args.map(function(n) { var ele = typeof(n) === "function" ? n() : n; self._isa[ele.path] = ele ele._hasa[self.path] = self; self.pingup("isa", self, ele.path) ele.pingup("isa", self, ele.path) }) } else { if (typeof(elef) === "object" && elef.length) { elef.map(function(n) { var ele = typeof(n) === "function" ? n() : n; self._isa[ele.path] = ele ele._hasa[self.path] = self; self.pingup("isa", self, ele.path) ele.pingup("isa", self, ele.path) }) } else { var ele = typeof(elef) === "function" ? elef() : typeof(elef) === "object" ? elef : typeof(elef) === "string" ? inner(elef)() : false if (ele) { this._isa[ele.path] = ele ele._hasa[this.path] = this; self.pingup("isa", self, ele.path) ele.pingup("isa", self, ele.path) } } } return this; } electra.prototype.unisa = function(elef) { var self = this; var args = Array.prototype.slice.apply(arguments, [0]); if (args.length > 1) { args.map(function(n) { var ele = typeof(n) === "function" ? n() : n; delete self._isa[ele.path] delete ele._hasa[self.path] self.pingup("isa", self, ele.path) ele.pingup("isa", self, ele.path) }) } else { if (typeof(elef) === "object" && elef.length) { elef.map(function(n) { var ele = typeof(n) === "function" ? n() : n; delete self._isa[ele.path] delete ele._hasa[self.path] self.pingup("isa", self, ele.path) ele.pingup("isa", self, ele.path) }) } else { var ele = typeof(elef) === "function" ? elef() : elef; delete this._isa[ele.path] delete ele._hasa[this.path] self.pingup("isa", self, ele.path) ele.pingup("isa", self, ele.path) } } return this; } electra.prototype.relatesAsToWith = function(relationout, elef, relationto, withstuff) { var self = this; if (typeof(relationto) === "undefined") { relationto = relationout; } if (typeof(withstuff) === "undefined") { withstuff = ""; } var withstuffOut = Object.assign({ "out": relationout, "in": relationto }, withstuff); var withstuffTo = Object.assign({ "in": relationout, "out": relationto }, withstuff); if (typeof(elef) === "object" && elef.length) { elef.map(function(n) { var ele = typeof(n) === "function" ? n() : n; self._relations[relationout] = self._relations[relationout] || {} self._relations[relationout][ele.path] = { ref: ele, context: withstuffOut } ele._relations[relationto] = ele._relations[relationto] || {} ele._relations[relationto][self.path] = { ref: self, context: withstuffTo } }) } else { var ele = typeof(elef) === "function" ? elef() : elef; self._relations[relationout] = self._relations[relationout] || {} self._relations[relationout][ele.path] = { ref: ele, context: withstuffOut } ele._relations[relationto] = ele._relations[relationto] || {} ele._relations[relationto][self.path] = { ref: self, context: withstuffTo } } return this; } electra.prototype.test = function() { return this; } electra.prototype.emita = function() { var self = this; var args = Array.prototype.slice.apply(arguments, [0]); var kk = _.keys(this.parents); if (kk.length > 0) { kk.map(function(k) { self.parents[k].emita.apply(self.parents[k], args) }) } else {} var kk = _.keys(this.children); if (kk.length > 0) { kk.map(function(k) { //self.children[k].emita.apply(self.children[k],args) }) } else {} /* _.map(this.children,function(child,name){ child().emita.apply(child(),args); }) **/ } electra.prototype.emitas = function() { var self = this; var args = Array.prototype.slice.apply(arguments, [0]); var kk = _.keys(this.parents); if (kk.length > 0) { kk.map(function(k) { self.parents[k].emita.apply(self.parents[k], args) }) } else {} var kk = _.keys(this.children); if (kk.length > 0) { kk.map(function(k) { //self.children[k].emita.apply(self.children[k],args) }) } else {} /* _.map(this.children,function(child,name){ child().emita.apply(child(),args); }) **/ } electra.prototype.data2 = function(key, value) { var self = this; if (typeof(key) === "undefined") { return this._data; } else { if (typeof(value) === "undefined") { if (typeof(key) === "object") { var oldvalue = this._data; this._data = Object.assign(this._data, key); _.map(this.parents, function(par, nam) { par.emit("changed", self, "[[object]]", self._data, oldvalue); }) this.emit("changed", this, "[[object]]", this._data, oldvalue); return this; } else { return this._data[key]; } } else { var oldvalue = this._data[key]; this._data[key] = value; _.map(this.parents, function(par, nam) { par.emit("changed", self, key, value, oldvalue); }) this.emit("changed", this, key, value, oldvalue); return this._data[key]; } } } electra.prototype.getStuff = function(stuff, applybefore, named_, dd) { var named = named_ || "data"; var dd = dd || false; var obj = { name: this.path, from: named, data: applybefore(getpath(this, stuff)) } var arr = [obj]; if (dd) { return arr } arr = [].concat.apply(arr, _.map(this._inherits, function(par, nam) { return par.getStuff.apply(par, [stuff, applybefore, "inherit_" + named_]); })) arr = [].concat.apply(arr, _.map(this.parents, function(par, nam) { return par.getStuff.apply(par, [stuff, applybefore, "parent_" + named_]); })) arr = [].concat.apply(arr, _.map(this._isa, function(par, nam) { return par.getStuff.apply(par, [stuff, applybefore, "isa_" + named_, true]); })) arr = arr.filter(function(a) { return a.data ? (typeof(a.data) === "function" || a.data.length > 0) : false }); return arr } electra.prototype.getStuff2 = function(stuff, applybefore, named_, dd, inas) { var self = this; var named = named_ || "data"; var dd = dd || false; var inass = inas || []; var obj = { name: this.path, from: named, data: applybefore(getpath(this, stuff)), inas: inass } var arr = [obj]; if (dd) { return arr } arr = [].concat.apply(arr, _.map(this._inherits, function(par, nam) { return par.getStuff2.apply(par, [stuff, applybefore, "inherit_" + named_, false, inass.concat([self.path])]); })) arr = [].concat.apply(arr, _.map(this.parents, function(par, nam) { return par.getStuff2.apply(par, [stuff, applybefore, "parent_" + named_, false, inass.concat([self.path])]); })) arr = [].concat.apply(arr, _.map(this._isa, function(par, nam) { return par.getStuff2.apply(par, [stuff, applybefore, "isa_" + named_, false, inass.concat([self.path])]); })) arr = arr.filter(function(a) { return a.data ? (typeof(a.data) === "function" || a.data.length > 0) : false }); return arr } electra.prototype.getStuff3 = function(stuff, applybefore, named_, dd) { var named = named_ || "data"; var dd = dd || false; var obj = { name: this.path, from: named, data: applybefore(getpath(this, stuff)) } var arr = [obj]; if (dd) { return arr } arr = [].concat.apply(arr, _.map(this._inherits, function(par, nam) { return par.getStuff.apply(par, [stuff, applybefore, "inherit_" + named_]); })) arr = [].concat.apply(arr, _.map(this.parents, function(par, nam) { return par.getStuff.apply(par, [stuff, applybefore, "parent_" + named_]); })) arr = arr.filter(function(a) { return a.data ? (typeof(a.data) === "function" || a.data.length > 0) : false }); return arr } electra.prototype.pingup = function() { var args = Array.prototype.slice.apply(arguments, [0]); _.map(this.parents, function(par, nam) { return par.pingup.apply(par, args); }) this.emit.apply(this, args); } electra.prototype.data = function(key, value) { var self = this; if (typeof(key) === "undefined") { return this._data; } else { if (typeof(value) === "undefined") { if (typeof(key) === "object") { var oldvalue = this._data; this._data = Object.assign(this._data, key); this.pingup("changed", self, "[[object]]", self._data, oldvalue) return this; } else { return this._data[key]; } } else { var oldvalue = getpath(this._data, key); //this._data[key]; setpath(this._data, key, value) // this._data[key] = value; this.pingup("changed", self, key, value, oldvalue) return getpath(this._data, key); //this._data[key]; } } } electra.prototype.metas = function(key, value) { var self = this; if (typeof(key) === "undefined") { return this._metas; } else { if (typeof(value) === "undefined") { if (typeof(key) === "object") { var oldvalue = this._metas; this._metas = Object.assign(this._metas, key); this.pingup("changed:meta", self, "[[object]]", self._metas, oldvalue) return this; } else { return this._metas[key]; } } else { var oldvalue = this._metas[key]; this._metas[key] = value; this.pingup("changed:meta", self, key, value, oldvalue) return this._metas[key]; } } } /* electra.prototype.metas = function() { var self = this; var args = Array.prototype.slice.apply(arguments, [0]); if (args.length === 0) { return this._metas; } args.map(function(obj_) { var obj = typeof(obj_) === "function" ? obj_() : typeof(obj_) === "object" ? obj_ : typeof(obj_) === "string" ? inner(obj_)() : false if (obj) { self._metas.push({ "meta": obj, "data": {} }) } }) return this; } electra.prototype.metas3 = function(arr) { var self = this; self._metas = self._metas.concat(arr); return self; } */ electra.prototype.exporta = function(path) { this.export(path, function(err, arr) { // console.log("EXPORTED", err, JSON.stringify(arr, true, 2)); }) } electra.prototype.pickdeep = function(prop, cb) { var self = this; async.mapLimit(this.children, 10, function(obj, next) { //setTimeout(function(){ obj().pickdeep(prop, next) //},1) // next(null,) }, function(err, collect) { cb(err, [].concat.apply([self[prop]], collect)); }) return this; } electra.prototype.withAllSync = function(func, cb) { var self = this; async.mapLimit(this.children, 10, function(obj, next) { //setTimeout(function(){ obj().withAllSync(func, next) //},1) // next(null,) }, function(err, collect) { cb(err, [].concat.apply([func(self)], collect)); }) return this; } electra.prototype.withAll = function(func, cb) { var self = this; async.mapLimit(this.children, 10, function(obj, next) { obj().withAll(func, next) }, function(err, collect) { func(self, function(err2, result) { cb(err || err2, [].concat.apply([result], collect)); }) }) return this; } electra.prototype.exportSync = function(path, serializers) { var self = this; if (self._metas.unsaveable) { return undefined; } var obj = self.innerExport(path, serializers); var collect = _.map(self.children, function(child, name) { return child().exportSync(path + "/" + name, serializers) }) collect.sort(function(a, b) { return a.name > b.name ? 1 : a.name < b.name ? -1 : 0 }) obj.children = collect; return obj; } electra.prototype.exportFlatSync = function(path) { var self = this; return [].concat.apply([self.innerExport(path)], _.map(self.children, function(child, name) { return child().exportFlatSync(path + "/" + name) })) } electra.prototype.export2 = function(path, cb) { var self = this; var obj = self.innerExport(path); obj.children = _.map(self.children, function(a, n) { return a().path }); cb(null, obj); } electra.prototype.innerExport = function(path, serializers) { var self = this; if (self._metas.unsaveable) { return {}; } var obj = {}; // obj._id = self._id; obj.name = self.name; obj.path = self.path; obj.created = self._created.toJSON(); obj.data = self._data; obj.meta = self._metas; obj.inherits = _.map(self._inherits, function(a, n) { return a.path }); obj.extends = _.map(self._extends, function(a, n) { return a.path }); /*obj.parents = _.map(self.parents, function(a, n) { return a.path });*/ obj.isa = _.map(self._isa, function(a, n) { return a.path }); /*obj.hasa = _.map(self._hasa, function(a, n) { return a.path });*/ obj.rel = {}; _.map(self._relations, function(a, n) { obj.rel[n] = {} _.map(a, function(aa, nn) { obj.rel[n][aa.ref.path] = aa.context; }); }); //clean up obj.inherits = obj.inherits.length ? obj.inherits : undefined; obj.extends = obj.extends.length ? obj.extends : undefined; //obj.parents = obj.parents.length ? obj.parents : undefined; obj.isa = obj.isa.length ? obj.isa : undefined; //obj.hasa = obj.hasa.length ? obj.hasa : undefined; obj.data = _.size(obj.data) ? obj.data : undefined; obj.meta = _.size(obj.meta) ? obj.meta : undefined; obj.rel = _.size(obj.rel) ? obj.rel : undefined; _.keys(obj).map(function(k) { if (obj[k] === undefined) { delete obj[k]; //obj[k] = null /* nope */ } }) var therest = _.omit(self, ignores); //seriadeep(therest, obj, serializers,1); _.map(therest, function(val, ke) { if (typeof(val) === "function") { obj[ke] = "$$$FUNCTION$$$" + val; } else { if (serializers && serializers[ke]) { obj[ke] = serializers[ke](val); } else { _.map(val, function(cval, cprop) {}) obj[ke] = val; } } }) return obj } function seriadeep(rest, obj, serializers, one) { if (one > 2) { return } _.map(rest, function(val, ke) { if (typeof(val) === "function") { obj[ke] = "$$$FUNCTION$$$" + val; } else if (typeof(val) === "object") { obj[ke] = seriadeep(val, obj, serializers, (one || 1) + 1); } else { if (serializers && serializers[ke]) { obj[ke] = serializers[ke](val) } else { obj[ke] = val; } } }) } electra.prototype.exportSerialised = function(serializers) { var self = this; return function(path, cb) { async.mapLimit(this.children, 10, function(obj, next) { obj().exportSerialised(serializers)(path + obj().name, next) // next(null,) }, function(err, collect) { var obj = self.innerExport(path, serializers); collect.sort(function(a, b) { return a.name > b.name ? 1 : a.name < b.name ? -1 : 0 }) obj.children = collect; cb(err, obj); }) return this; } } electra.prototype.export = function(path, cb) { var self = this; async.mapLimit(this.children, 10, function(obj, next) { obj().export(path + obj().name, next) }, function(err, collect) { var obj = self.innerExport(path); if (collect.sort) { collect.sort(function(a, b) { return a.name > b.name ? 1 : a.name < b.name ? -1 : 0 }) } obj.children = collect; cb(err, obj); }) return this; } return inner } } function getpath(obj, path) { path = path || ""; var arr = path.split("."); var t = obj; var done = false; if (arr.length) { while (arr.length && !done) { var check = arr.shift(); if (check !== "") { t = t[check]; } if (typeof t !== "object") { done = true; } } } return t; } function setpath(obj, path, value) { if (typeof obj !== "object" || !obj) { throw new Error("obj is not Object"); } if (typeof path !== "string" || path === "") { throw new Error("path must be string with length > 0"); } var arr = path.split("."); var done = false; var t = obj; if (arr.length > 1) { while (arr.length && t && !done) { var check = arr.shift(); if (typeof t[check] === "object" && arr.length > 0) { t = t[check]; } else { done = true; arr.unshift(check); } } var xt = t; while (arr.length) { var tt = arr.shift(); if (arr.length) { //go deeper xt = xt[tt] = {}; } else { //last xt[tt] = value; } } } else { if (arr.length === 1 && arr[0] !== "") { t[arr[0]] = value; } } } uberfactory.ignores = ignores = [ "_eventsCount", "off", "on", "_isdirty", "inherits", "uninherits", "unextends", "extends", "isa", "unisa", "relatesAsToWith", "test", "emita", "emitas", "data2", "getStuff", "getStuff2", "getStuff3", "pingup", "data", "metas2", "metas", "metas3", "exporta", "pickdeep", "withAllSync", "withAll", "innerExport", "exportFlatSync", "exportSync", "export2", "export", "domain", "_events", "rawListeners", "_maxListeners", "setMaxListeners", "getMaxListeners", "emit", "addListener", "on", "prependListener", "once", "prependOnceListener", "removeListener", "removeAllListeners", "listeners", "listenerCount", "eventNames", "_data", "path", "name", "children", "parents", "_inherits", "_extends", "_isa", "_hasa", "_relations", "_references", "_created", "_metas", "_rootname", "exportSerialised" ] module.exports = uberfactory;