"use strict"; var Subscribable = require("../utils/subscribable"); var AbstcractMap = require("../wContainer/abstractmap"); var AbstractOrder = require("../wContainer/abstractorder"); var Address = require("../wType/address"); var Uint64 = require("../wType/uint64"); var Event = require("../wType/event"); var Vector = require("../wType/vector"); var Vocabulary = require("../wType/vocabulary"); var String = require("../wType/string"); var Handler = require("../wDispatcher/handler"); var Model = Subscribable.inherit({ "className": "Model", "constructor": function(address) { Subscribable.fn.constructor.call(this); var SMap = AbstcractMap.template(Uint64, Model.addressOrder); this._registered = false; this._subscribers = new SMap(false); this._handlers = []; this._models = []; this._props = new Vector(); this._address = address; this._subscribersCount = 0; this.addHandler("subscribe"); this.addHandler("unsubscribe"); }, "destructor": function() { var i; if (this._registered) { this.unregister(); } this._subscribers.destructor(); for (i = 0; i < this._models.length; ++i) { this._models[i].destructor(); } for (i = 0; i < this._handlers.length; ++i) { this._handlers[i].destructor(); } this._props.destructor(); Subscribable.fn.destructor.call(this); }, "addHandler": function(name) { if (!(this["_h_" + name] instanceof Function)) { throw new Error("An attempt to create handler without a handling method"); } var handler = new Handler(this._address["+"](new Address([name])), this, this["_h_" + name]); this._addHandler(handler); }, "_addHandler": function(handler) { this._handlers.push(handler); if (this._registered) { this._dp.registerHandler(handler); } }, "addModel": function(model) { if (!(model instanceof Model)) { throw new Error("An attempt to add not a model into " + this.className); } this._models.push(model); model.on("serviceMessage", this._onModelServiceMessage, this); if (this._registered) { model.register(this._dp, this._server); } }, "addProperty": function(property, key) { var vc = new Vocabulary(); vc.insert("property", new String(property)); vc.insert("key", new String(key)); this._props.push(vc); if (this._registered) { var nvc = new Vocabulary(); nvc.insert("properties", this._props.clone()); this.broadcast(nvc, "properties"); } }, "broadcast": function(vc, handler) { var itr = this._subscribers.begin(); var end = this._subscribers.end(); vc.insert("source", this._address.clone()); for (;!itr["=="](end); itr["++"]()) { var obj = itr["*"](); var order = obj.second; var socket = this._server.getConnection(obj.first); var oItr = order.begin(); var oEnd = order.end(); for (;!oItr["=="](oEnd); oItr["++"]()) { var addr = oItr["*"]()["+"](new Address([handler])); var ev = new Event(addr, vc.clone()); ev.setSenderId(socket.getId().clone()); socket.send(ev); ev.destructor(); } } vc.destructor(); }, "getAddress": function() { return this._address.clone(); }, "getType": function() { var type = Model.ModelType[this.className]; if (type === undefined) { throw new Error("Undefined ModelType"); } return type; }, "_h_subscribe": function(ev) { var id = ev.getSenderId(); var source = ev.getData().at("source"); var itr = this._subscribers.find(id); var ord; if (itr["=="](this._subscribers.end())) { ord = new Model.addressOrder(true); var socket = this._server.getConnection(id); socket.one("disconnected", this._onSocketDisconnected, this); this._subscribers.insert(id.clone(), ord); } else { ord = itr["*"]().second; var oItr = ord.find(source); if (!oItr["=="](ord.end())) { this.trigger("serviceMessage", "id: " + id.toString() + ", " + "source: " + source.toString() + " " + "is trying to subscribe on model " + this._address.toString() + " " + "but it's already subscribed", 1); return; } } ord.push_back(source.clone()); ++this._subscribersCount; this.trigger("serviceMessage", this._address.toString() + " has now " + this._subscribersCount + " subscribers", 0); var nvc = new Vocabulary(); nvc.insert("properties", this._props.clone()); this.response(nvc, "properties", ev); }, "_h_unsubscribe": function(ev) { var id = ev.getSenderId(); var source = ev.getData().at("source"); var itr = this._subscribers.find(id); if (itr["=="](this._subscribers.end())) { this.trigger("serviceMessage", "id: " + id.toString() + ", " + "source: " + source.toString() + " " + "is trying to unsubscribe from model " + this._address.toString() + " " + "but even this id is not registered in subscribers map", 1 ); return } var ord = itr["*"]().second; var oItr = ord.find(source); if (oItr["=="](ord.end())) { this.trigger("serviceMessage", "id: " + id.toString() + ", " + "source: " + source.toString() + " " + "is trying to unsubscribe from model " + this._address.toString() + " " + "but such address is not subscribed to this model", 1 ); return } ord.erase(oItr["*"]()); if (ord.size() === 0) { var socket = this._server.getConnection(itr["*"]().first); socket.off("disconnected", this._onSocketDisconnected, this); this._subscribers.erase(itr); ord.destructor(); } --this._subscribersCount; this.trigger("serviceMessage", this._address.toString() + " has now " + this._subscribersCount + " subscribers", 0); }, "_onModelServiceMessage": function(msg, severity) { this.trigger("serviceMessage", msg, severity); }, "_onSocketDisconnected": function(ev, socket) { var id = socket.getId(); var itr = this._subscribers.find(id); if (itr["=="](this._subscribers.end())) { this.trigger("serviceMessage", "id: " + id.toString() + ", " + "after socket disconnected trying to remove subscriptions from model " + "but id haven't been found in subscribers map", 1); return } var ord = itr["*"]().second; this._subscribersCount -= ord.size(); this._subscribers.erase(itr); ord.destructor(); this.trigger("serviceMessage", this._address.toString() + " has now " + this._subscribersCount + " subscribers", 0); }, "register": function(dp, server) { if (this._registered) { throw new Error("Model " + this._address.toString() + " is already registered"); } this._dp = dp; this._server = server; var i; for (i = 0; i < this._models.length; ++i) { this._models[i].register(dp, server); } for (i = 0; i < this._handlers.length; ++i) { dp.registerHandler(this._handlers[i]); } this._registered = true; }, "_removeHandler": function(handler) { var index = this._handlers.indexOf(handler); if (index === -1) { throw new Error("An attempt to remove non existing handler"); } this._handlers.splice(index, 1); if (this._registered) { this._dp.unregisterHandler(handler); } }, "removeModel": function(model) { if (!(model instanceof Model)) { throw new Error("An attempt to remove not a model from " + this.className); } var index = this._models.indexOf(model); if (index === -1) { throw new Error("An attempt to remove non existing model from " + this.className); } this._models.splice(index, 1); if (this._registered) { model.unregister(this._dp, this._server); } }, "response": function(vc, handler, src) { if (!this._registered) { throw new Error("An attempt to send a message from unregistered model " + this._address.toString()); } var source = src.getData().at("source").clone(); var id = src.getSenderId().clone(); var addr = source["+"](new Address([handler])); vc.insert("source", this._address.clone()); var ev = new Event(addr, vc); ev.setSenderId(id); var socket = this._server.getConnection(id); socket.send(ev); ev.destructor(); }, "unregister": function() { if (!this._registered) { throw new Error("Model " + this._address.toString() + " is not registered"); } var i; for (i = 0; i < this._models.length; ++i) { this._models[i].unregister(); } for (i = 0; i < this._handlers.length; ++i) { this._dp.unregisterHandler(this._handlers[i]); } var itr = this._subscribers.begin(); var end = this._subscribers.end(); for (;!itr["=="](end); itr["++"]()) { var socket = this._server.getConnection(itr["*"]().first); var ord = itr["*"]().second; ord.destructor(); socket.off("disconnected", this._onSocketDisconnected, this); } this._subscribers.clear(); this._subscribersCount = 0; delete this._dp; delete this._server; this._registered = false; } }); Model.getModelTypeId = function(model) { return this.ModelType[model.className]; } Model.addressOrder = AbstractOrder.template(Address); Model.ModelType = { String: 0, List: 1, Vocabulary: 2, //Catalogue: 3, Image: 4, Button: 5, Model: 6, Attributes: 50, GlobalControls: 100, Link: 101, Page: 102, PageStorage: 103, PanesList: 104, Theme: 105, ThemeStorage: 106, Player: 107 }; module.exports = Model;