# HG changeset patch # User veltr # Date 1345200600 -7200 # Node ID bd58970ffd168350eef89ece61caedc8215ac101 # Parent d49b927bfe6213437ebd3cbe190ba8ba6e356fdd Refactoring to better fit the MVVM pattern diff -r d49b927bfe62 -r bd58970ffd16 client/css/renkan.css --- a/client/css/renkan.css Thu Aug 16 17:24:13 2012 +0200 +++ b/client/css/renkan.css Fri Aug 17 12:50:00 2012 +0200 @@ -45,6 +45,18 @@ font-size: 10px; font-family: Arial, Helvetica, sans-serif; } +.Rk-Bins { + background: #F0F0F0; position: absolute; left: 0; top: 0; width: 200px; bottom: 0; +} + +.Rk-Render { + position: absolute; left: 200px; top: 0; right: 0; bottom: 0; +} + +.Rk-Editor { + position: absolute; left: 0; top: 0; +} + .Rk-CloseX { float: right; cursor: pointer; } @@ -91,4 +103,15 @@ .Rk-ZoomOut:hover { background-position: -21px -20px; -} \ No newline at end of file +} + +ul.Rk-TabButtons { +} + +li.Rk-TabButton { + float: left; padding: 2px; font-size: 13px; border-top-left-radius: 5px; border-top-right-radius: 5px; background: #CCCCCC; +} + +.Rk-TabDiv { + width: 100%; clear: both; overflow: auto; border-top: 1px solid #ccc; +} diff -r d49b927bfe62 -r bd58970ffd16 client/data/dynamic-data.json --- a/client/data/dynamic-data.json Thu Aug 16 17:24:13 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -{"title":"Test Graph","users":[{"id":"anonymous","title":"anonymous","uri":"","color":"#0000ff"},{"id":"u-cybunk","title":"Samuel","uri":"http://twitter.com/cybunk","color":"#e00000"},{"id":"u-raphv","title":"Raphael","uri":"http://twitter.com/raphv","color":"#00a000"}],"nodes":[{"id":"n-001","title":"連環 (Renkan)","description":"Vive Renkan !","uri":"http://ja.wikipedia.org/wiki/%E7%99%BE%E5%AD%A6%E9%80%A3%E7%92%B0","created_by":"u-cybunk","position":{"x":-26.522137994214717,"y":-210.84128058998363}},{"id":"n-002","title":"Savoir","description":"","uri":"http://fr.wikipedia.org/wiki/Savoir","created_by":"u-raphv","position":{"x":51.05688741877592,"y":2.5968600661189982}},{"id":"n-003","title":"Connaissance","description":"","uri":"http://fr.wikipedia.org/wiki/Connaissance","created_by":"u-raphv","position":{"x":289.41278995919333,"y":-148.06006021345246}},{"id":"n-005","title":"nœud","description":"","uri":"http://fr.wikipedia.org/wiki/Th%C3%A9orie_des_graphes","created_by":"u-cybunk","position":{"x":-348.6577736928668,"y":36.915363564739906}},{"id":"n-006","title":"arête","description":"","uri":"http://fr.wikipedia.org/wiki/Th%C3%A9orie_des_graphes","created_by":"u-cybunk","position":{"x":-310.04965494954644,"y":-269.5988072780878}},{"id":"node-2012-07-31-f1c2d0fd52a9d0e2-0016","title":"graphe","description":"","uri":"","created_by":"anonymous","position":{"x":-190.13786466320713,"y":-97.7768211544552}},{"id":"node-2012-07-31-6d574a0c9d5d2931-0026","title":"Test","description":"","uri":"","created_by":"anonymous","position":{"x":156.5765089679022,"y":-297.9314037374282}}],"edges":[{"id":"e-001","title":"(untitled edge)","uri":"","from":"n-001","to":"n-002","created_by":"u-raphv"},{"id":"e-002","title":"(untitled edge)","uri":"","from":"n-002","to":"n-003","created_by":"u-raphv"},{"id":"edge-2012-07-31-f1c2d0fd52a9d0e2-0013","title":"(untitled edge)","uri":"","from":"n-006","to":"n-001","created_by":"anonymous"},{"id":"edge-2012-07-31-f1c2d0fd52a9d0e2-001d","title":"(untitled edge)","uri":"","from":"node-2012-07-31-f1c2d0fd52a9d0e2-0016","to":"n-005","created_by":"anonymous"},{"id":"edge-2012-07-31-f1c2d0fd52a9d0e2-0020","title":"(untitled edge)","uri":"","from":"node-2012-07-31-f1c2d0fd52a9d0e2-0016","to":"n-001","created_by":"anonymous"},{"id":"edge-2012-07-31-f1c2d0fd52a9d0e2-0023","title":"(untitled edge)","uri":"","from":"node-2012-07-31-f1c2d0fd52a9d0e2-0016","to":"n-006","created_by":"anonymous"},{"id":"edge-2012-07-31-6d574a0c9d5d2931-002c","title":"(untitled edge)","uri":"","from":"node-2012-07-31-6d574a0c9d5d2931-0026","to":"n-001","created_by":"anonymous"}]} \ No newline at end of file diff -r d49b927bfe62 -r bd58970ffd16 client/js/full-json.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/js/full-json.js Fri Aug 17 12:50:00 2012 +0200 @@ -0,0 +1,130 @@ +/* Saves the Full JSON at each modification */ + +Rkns.RemoteModels.FullJson = Rkns.Utils.inherit(Rkns.RemoteModels._Base); + +Rkns.RemoteModels.FullJson.prototype._init = function(_project, _opts) { + this.url = _opts.url; + this.load(this.url); + if (typeof _opts.http_method == "undefined") { + _opts.http_method = 'PUT'; + } + this.http_method = _opts.http_method; + var _this = this; + this.fullSave + = this.addUser + = this.addNode + = this.addEdge + = this.updateNode + = this.updateEdge + = this.removeNode + = this.removeEdge + = Rkns._.throttle(function() { + _this._save.apply(this, Array.prototype.slice.call(arguments,0)); + }, 2000); +} + +Rkns.RemoteModels.FullJson.prototype.load = function(_url) { + var _this = this; + Rkns.$.getJSON(_url, function(_data) { + _this.deserialize(_data); + _this.handleCallbacks(); + }); +} + +Rkns.RemoteModels.FullJson.prototype.deserialize = function(_serializedData) { + if (typeof _serializedData === "string") { + _serializedData = JSON.parse(_serializedData); + } + var _proj = this._project; + _proj.title = _serializedData.title || "(untitled project)"; + if (typeof _serializedData.users === "object" && _serializedData.users) { + Rkns._(_serializedData.users).each(function(_data) { + var _userData = { + id: _data.id, + title: _data.title, + uri: _data.uri, + color: _data.color + }; + _proj.addUser(_userData); + }); + } + if (typeof _serializedData.nodes === "object" && _serializedData.nodes) { + Rkns._(_serializedData.nodes).each(function(_data) { + var _nodeData = { + id: _data.id, + title: _data.title, + description: _data.description, + uri: _data.uri, + created_by: _data.created_by, + position: { + x: _data.position.x, + y: _data.position.y + } + }; + _proj.addNode(_nodeData); + }); + } + if (typeof _serializedData.edges === "object" && _serializedData.edges) { + Rkns._(_serializedData.edges).each(function(_data) { + var _edgeData = { + id: _data.id, + title: _data.title, + uri: _data.uri, + from: _data.from, + to: _data.to, + created_by: _data.created_by + }; + _proj.addEdge(_edgeData); + }); + } +} + +Rkns.RemoteModels.FullJson.prototype.serialize = function() { + var _res = { + title: this._project.title, + users: this._project.users.map(function(_user) { + return { + id: _user.id, + title: _user.title, + uri: _user.uri, + color: _user.color + } + }), + nodes: this._project.nodes.map(function(_node) { + return { + id: _node.id, + title: _node.title, + description: _node.description, + uri: _node.uri, + created_by: _node.created_by.id, + position: { + x: _node.position.x, + y: _node.position.y + } + } + }), + edges: this._project.edges.map(function(_edge) { + return { + id: _edge.id, + title: _edge.title, + uri: _edge.uri, + from: _edge.from.id, + to: _edge.to.id, + created_by: _edge.created_by.id + } + }) + } + return _res; +} + +Rkns.RemoteModels.FullJson.prototype._save = function() { + var _data = JSON.stringify(this.serialize()); + Rkns.$.ajax({ + type: this.http_method, + url: this.url, + contentType: "application/json", + data: _data, + success: function(data, textStatus, jqXHR) { + } + }); +}; diff -r d49b927bfe62 -r bd58970ffd16 client/js/json-serializer.js --- a/client/js/json-serializer.js Thu Aug 16 17:24:13 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -Rkns.Serializers.BasicJson = Rkns.Utils.inherit(Rkns.Serializers._Base); - -Rkns.Serializers.BasicJson.prototype._init = function() { - this.load(this._project._opts.url); - var _this = this; - this.save = Rkns._.throttle(function() { - _this._save.apply(this, Array.prototype.slice.call(arguments,0)); - }, 2000); -} - -Rkns.Serializers.BasicJson.prototype.load = function(_url) { - var _this = this; - Rkns.$.getJSON(_url, function(_data) { - _this.deserialize(_data); - _this.handleCallbacks(); - }); -} - -Rkns.Serializers.BasicJson.prototype.deserialize = function(_serializedData) { - if (typeof _serializedData === "string") { - _serializedData = JSON.parse(_serializedData); - } - var _proj = this._project; - _proj.title = _serializedData.title || "(untitled project)"; - if (typeof _serializedData.users === "object" && _serializedData.users) { - Rkns._(_serializedData.users).each(function(_data) { - var _userData = { - id: _data.id, - title: _data.title, - uri: _data.uri, - color: _data.color - }; - _proj.addUser(_userData); - }); - } - if (typeof _serializedData.nodes === "object" && _serializedData.nodes) { - Rkns._(_serializedData.nodes).each(function(_data) { - var _nodeData = { - id: _data.id, - title: _data.title, - description: _data.description, - uri: _data.uri, - created_by: _data.created_by, - position: { - x: _data.position.x, - y: _data.position.y - } - }; - _proj.addNode(_nodeData); - }); - } - if (typeof _serializedData.edges === "object" && _serializedData.edges) { - Rkns._(_serializedData.edges).each(function(_data) { - var _edgeData = { - id: _data.id, - title: _data.title, - uri: _data.uri, - from: _data.from, - to: _data.to, - created_by: _data.created_by - }; - _proj.addEdge(_edgeData); - }); - } -} - -Rkns.Serializers.BasicJson.prototype.serialize = function() { - var _res = { - title: this._project.title, - users: this._project.users.map(function(_user) { - return { - id: _user.id, - title: _user.title, - uri: _user.uri, - color: _user.color - } - }), - nodes: this._project.nodes.map(function(_node) { - return { - id: _node.id, - title: _node.title, - description: _node.description, - uri: _node.uri, - created_by: _node.created_by.id, - position: { - x: _node.position.x, - y: _node.position.y - } - } - }), - edges: this._project.edges.map(function(_edge) { - return { - id: _edge.id, - title: _edge.title, - uri: _edge.uri, - from: _edge.from.id, - to: _edge.to.id, - created_by: _edge.created_by.id - } - }) - } - return _res; -} - -Rkns.Serializers.BasicJson.prototype._save = function() { - var _data = JSON.stringify(this.serialize()); - Rkns.$.ajax({ - type: 'PUT', - url: this._project._opts.url, - contentType: "application/json", - data: _data, - success: function(data, textStatus, jqXHR) { - } - }); -}; diff -r d49b927bfe62 -r bd58970ffd16 client/js/main.js --- a/client/js/main.js Thu Aug 16 17:24:13 2012 +0200 +++ b/client/js/main.js Fri Aug 17 12:50:00 2012 +0200 @@ -33,9 +33,9 @@ Rkns._ = _; -Rkns.Serializers = {}; +Rkns.RemoteModels = {}; -Rkns.Serializers._Base = function(_project) { +Rkns.RemoteModels._Base = function(_project, _opts) { if (typeof _project !== "undefined") { this._project = _project; this._callbackQueue = []; @@ -43,23 +43,31 @@ } } -Rkns.Serializers._Base.prototype.deserialize = function() {} +Rkns.RemoteModels._Base.prototype.fullSave + = Rkns.RemoteModels._Base.prototype.addUser + = Rkns.RemoteModels._Base.prototype.addNode + = Rkns.RemoteModels._Base.prototype.addEdge + = Rkns.RemoteModels._Base.prototype.updateNode + = Rkns.RemoteModels._Base.prototype.updateEdge + = Rkns.RemoteModels._Base.prototype.removeNode + = Rkns.RemoteModels._Base.prototype.removeEdge + = function() {} -Rkns.Serializers._Base.prototype.deferCallback = function(_callback) { +Rkns.RemoteModels._Base.prototype.deferCallback = function(_callback) { var _this = this; Rkns._.defer(function() { _callback.call(_this); }); } -Rkns.Serializers._Base.prototype.handleCallbacks = function() { +Rkns.RemoteModels._Base.prototype.handleCallbacks = function() { this._loaded = true; while (this._callbackQueue.length) { this.deferCallback(this._callbackQueue.splice(0,1)[0]); } } -Rkns.Serializers._Base.prototype.onLoad = function(_callback) { +Rkns.RemoteModels._Base.prototype.onLoad = function(_callback) { if (this._loaded) { this.deferCallback(_callback); } else { @@ -67,117 +75,78 @@ } } -Rkns.Serializers._Base.prototype.save = function() {} +Rkns.RemoteModels._Base.prototype.save = function() {} + +Rkns.Bins = {} + +Rkns.Bins._Base = function(_project) { + if (typeof _project !== "undefined") { + this._project = _project; + } +} + +Rkns.Bins._Base.prototype.baseTemplate = ''; -Rkns.Project = function(_opts) { - if (typeof _opts.serializer == "undefined") { - _opts.serializer = "BasicJson"; +Rkns.Bins._Base.prototype.addTab = function(_title) { + var _tabDiv = Rkns.$('
'); + _tabDiv.addClass('Rk-TabDiv') + .css("display","none") + .appendTo('.Rk-TabDivs'); + var _tabButton = Rkns.$('
  • '); + _tabButton.addClass('Rk-TabButton') + .html(_title) + .click(function() { + Rkns.$('.Rk-TabButton').removeClass("active"); + Rkns.$(this).addClass("active"); + Rkns.$('.Rk-TabDiv').hide(); + _tabDiv.show(); + }) + .appendTo('.Rk-TabButtons'); + return { + button: _tabButton, + contents: _tabDiv + } +} + +/* Point of entry */ + +Rkns.Renkan = function(_opts) { + if (typeof _opts.remotemodel == "undefined") { + _opts.remotemodel = "BasicJson"; } if (typeof _opts.language == "undefined" || typeof Rkns.i18n[_opts.language] == "undefined") { _opts.language = "en"; } - this.l10n = Rkns.i18n[_opts.language]; - this._opts = _opts; - this.users = new Rkns.Model.List(); - this.nodes = new Rkns.Model.List(); - this.edges = new Rkns.Model.List(); - if (typeof this._opts.user === "object") { - this.current_user = this.addUser(this._opts.user) + if (typeof _opts.container == "undefined") { + _opts.container = "renkan"; + } + this.project = new Rkns.ViewModel.Project(); + this.project.l10n = Rkns.i18n[_opts.language]; + if (typeof _opts.user === "object") { + this.current_user = this.project.addUser(_opts.user) } - this.serializer = new Rkns.Serializers[_opts.serializer](this); - this.renderer = new Rkns.Renderer.Scene(this); + Rkns.$("#" + _opts.container).html(this.template()); + this.project.remotemodel = new Rkns.RemoteModels[_opts.remotemodel](this.project, _opts); + this.project.renderer = new Rkns.Renderer.Scene(this.project, _opts); var _this = this; - this.serializer.onLoad(function() { - if (typeof _this.current_user === "undefined") { - _this.current_user = _proj.users[0]; +/* if (typeof _opts.bins === "object") { + this.bins = Rkns._(_opts.bins).map(function(_type) { + return new Rkns.Bins[_type](_this); + }); + Rkns.$('.Rk-TabButton:last').addClass("active"); + Rkns.$('.Rk-TabDiv:last').show(); +} */ + this.project.remotemodel.onLoad(function() { + if (typeof _this.project.current_user === "undefined") { + _this.project.current_user = _this.project.users[0]; } - _this.renderer.draw(); + _this.project.renderer.draw(); }); } -Rkns.Project.prototype.addNode = function(_props, _render_save) { - var _node = new Rkns.Model.Node(this, _props); - this.nodes.push(_node); - if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { - var _controller = this.renderer.addController("Node", _node); - _controller.redraw(); - } - if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { - this.serializer.save(); - } - return _node; -} - -Rkns.Project.prototype.addEdge = function(_props, _render_save) { - var _edge = new Rkns.Model.Edge(this, _props); - this.edges.push(_edge); - if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { - var _controller = this.renderer.addController("Edge", _edge); - _controller.redraw(); - } - if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { - this.serializer.save(); - } - return _edge; -} - -Rkns.Project.prototype.addUser = function(_props, _render_save) { - var _user = new Rkns.Model.User(this, _props); - this.users.push(_user); - return _user; -} - -Rkns.Project.prototype.updateElement = function(_element, _props, _render_save) { - Rkns._(_props).each(function(_v, _k) { - _element[_k] = _v; - }); - if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { - if (typeof _element.__controller !== "undefined") { - _element.__controller.redraw(); - } else { - this._renderer.redraw(); - } - } - if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { - this.serializer.save(); - } -} - -Rkns.Project.prototype.removeNode = function(_node, _render_save) { - this.nodes.removeId(_node.id); - if (typeof _node.__controller !== "undefined") { - this.renderer.removeController(_node.__controller); - } - var _this = this; - this.edges = this.edges.filter(function(_edge) { - var _keep = _edge.from !== _node && _edge.to !== _node; - if (!_keep && typeof _edge.__controller !== "undefined") { - _this.renderer.removeController(_edge.__controller); - } - return _keep; - }); - if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { - this.renderer.redraw(); - } - if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { - this.serializer.save(); - } - return _node; -} - -Rkns.Project.prototype.removeEdge = function(_edge, _render_save) { - this.edges.removeId(_edge.id); - if (typeof _edge.__controller !== "undefined") { - this.renderer.removeController(_edge.__controller); - } - if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { - this.renderer.redraw(); - } - if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { - this.serializer.save(); - } - return _edge; -} +Rkns.Renkan.prototype.template = Rkns._.template( + '
    ' +); /* Utility functions */ diff -r d49b927bfe62 -r bd58970ffd16 client/js/model.js --- a/client/js/model.js Thu Aug 16 17:24:13 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -Rkns.Model = {} - -Rkns.Model._BaseElement = function(_project, _props) { - if (typeof _props !== "undefined") { - this._project = _project; - this.id = _props.id || Rkns.Utils.getUID(this.type); - this.title = _props.title || "(untitled " + this.type + ")"; - this.description = _props.description || ""; - this.uri = _props.uri || ""; - } -} - -Rkns.Model._BaseElement.prototype.addReference = function(_propName, _list, _id, _default) { - var _element = _list.getElement(_id); - if (typeof _element === "undefined" && typeof _default !== "undefined") { - this[ _propName ] = _default; - this[ _propName + "_id" ] = _default.id; - } else { - this[ _propName + "_id" ] = _id; - this[ _propName ] = _element; - } -} - -Rkns.Model._BaseElement.prototype.updateGraphics = function() { - this._project.renderer.redraw(); -} - -Rkns.Model._BaseElement.prototype.updateData = function() { - this._project.serializer.save(this); -} - -/* Element Class Generator */ - -Rkns.Model._elementClass = function(_type) { - return Rkns.Utils.inherit(Rkns.Model._BaseElement, function() { - this.type = _type; - }); -} - -/* User Model */ - -Rkns.Model.User = Rkns.Model._elementClass("user"); - -Rkns.Model.User.prototype._init = function(_project, _props) { - this.color = _props.color || "#666666"; -} - -/* Node Model */ - -Rkns.Model.Node = Rkns.Model._elementClass("node"); - -Rkns.Model.Node.prototype._init = function(_project, _props) { - this.addReference("created_by", this._project.users, _props.created_by, _project.current_user); - this.position = _props.position; - this.description = _props.description || ""; -} - -Rkns.Model.Node.prototype.setPosition = function(_x, _y) { - if (typeof _x === "object") { - if (typeof _x.x !== "undefined" && typeof _x.y !== "undefined") { - this.position.x = _x.x; - this.position.y = _x.y; - } else { - if (typeof _x.length !== "undefined") { - this.position.x = _x[0]; - this.position.y = _x[1]; - } - } - } else { - if (typeof _y !== "undefined") { - this.position.x = +_x; - this.position.y = +_y; - } - } -} - -/* Edge Model */ - -Rkns.Model.Edge = Rkns.Model._elementClass("edge"); - -Rkns.Model.Edge.prototype._init = function(_project, _props) { - this.addReference("created_by", this._project.users, _props.created_by, _project.current_user); - this.addReference("from", this._project.nodes, _props.from); - this.addReference("to", this._project.nodes, _props.to); -} - -/* List Helper Functions -- See Metadataplayer */ - -Rkns.Model.List = function() { - Array.call(this); - this.idIndex = []; -} - -Rkns.Model.List.prototype = new Array(); - -Rkns.Model.List.prototype.hasId = function(_id) { - return Rkns._(this.idIndex).include(_id); -} - -Rkns.Model.List.prototype.getIds = function(_id) { - return this.idIndex; -} - -/* On recent browsers, forEach and map are defined and do what we want. - * Otherwise, we'll use the Underscore.js functions - */ -if (typeof Array.prototype.forEach === "undefined") { - Rkns.Model.List.prototype.forEach = function(_callback) { - var _this = this; - Rkns._(this).forEach(function(_value, _key) { - _callback(_value, _key, _this); - }); - } -} - -if (typeof Array.prototype.map === "undefined") { - Rkns.Model.List.prototype.map = function(_callback) { - var _this = this; - return Rkns._(this).map(function(_value, _key) { - return _callback(_value, _key, _this); - }); - } -} - -/* We override Array's filter function because it doesn't return an Rkns.Model.List - */ -Rkns.Model.List.prototype.filter = function(_callback) { - var _this = this, - _res = new Rkns.Model.List(); - _res.addElements(Rkns._(this).filter(function(_value, _key) { - return _callback(_value, _key, _this); - })); - return _res; -} - -Rkns.Model.List.prototype.slice = function(_start, _end) { - var _res = new Rkns.Model.List(); - _res.addElements(Array.prototype.slice.call(this, _start, _end)); - return _res; -} - -Rkns.Model.List.prototype.splice = function(_start, _end) { - var _res = new Rkns.Model.List(); - _res.addElements(Array.prototype.splice.call(this, _start, _end)); - this.idIndex.splice(_start, _end); - return _res; -} - -/* Array has a sort function, but it's not as interesting as Underscore.js's sortBy - * and won't return a new Rkns.Model.List - */ -Rkns.Model.List.prototype.sortBy = function(_callback) { - var _this = this, - _res = new Rkns.Model.List(); - _res.addElements(Rkns._(this).sortBy(function(_value, _key) { - return _callback(_value, _key, _this); - })); - return _res; -} - -Rkns.Model.List.prototype.push = function(_el) { - if (typeof _el === "undefined" || typeof _el.id === "undefined") { - return; - } - var _index = (Rkns._(this.idIndex).indexOf(_el.id)); - if (_index === -1) { - this.idIndex.push(_el.id); - Array.prototype.push.call(this, _el); - } else { - this[_index] = _el; - } -} - -Rkns.Model.List.prototype.addElements = function(_array) { - var _this = this; - Rkns._(_array).forEach(function(_el) { - _this.push(_el); - }); -} - -Rkns.Model.List.prototype.removeId = function(_id) { - var _index = (Rkns._(this.idIndex).indexOf(_id)); - if (_index !== -1) { - this.splice(_index,1); - } -} - -Rkns.Model.List.prototype.removeIds = function(_list) { - var _this = this; - Rkns._(_list).forEach(function(_id) { - _this.removeId(_id); - }); -} - -Rkns.Model.List.prototype.getElement = function(_id) { - var _index = Rkns._(this.idIndex).indexOf(_id); - if (_index === -1) { - return undefined; - } else { - return this[_index]; - } -} diff -r d49b927bfe62 -r bd58970ffd16 client/js/paper-renderer.js --- a/client/js/paper-renderer.js Thu Aug 16 17:24:13 2012 +0200 +++ b/client/js/paper-renderer.js Fri Aug 17 12:50:00 2012 +0200 @@ -164,7 +164,7 @@ Rkns.Renderer.Node.prototype._init = function() { this._renderer.node_layer.activate(); - this.type = "node"; + this.type = "Node"; this.circle = new paper.Path.Circle([0, 0], Rkns._NODE_RADIUS); this.circle.fillColor = '#ffffff'; this.circle.__controller = this; @@ -203,7 +203,7 @@ y: _coords.y } }; - this._project.updateElement(this._element, _data, Rkns._SAVE); + this._project.updateElement(this.type, this._element, _data, Rkns._SAVE); this._renderer.redraw(); } @@ -249,7 +249,7 @@ Rkns.Renderer.Edge.prototype._init = function() { this._renderer.edge_layer.activate(); - this.type = "edge"; + this.type = "Edge"; this.from_controller = this._element.from.__controller; this.to_node_controller = this._element.to.__controller; this.line = new paper.Path(); @@ -388,7 +388,7 @@ var _hitResult = paper.project.hitTest(_event.point); if (_hitResult && typeof _hitResult.item.__controller !== "undefined") { var _target = _hitResult.item.__controller; - if (_target.type === "node" && this.from_controller._element.id !== _target._element.id) { + if (_target.type === "Node" && this.from_controller._element.id !== _target._element.id) { this._project.addEdge({ from: this.from_controller._element.id, to: _target._element.id @@ -456,7 +456,7 @@ description: _this.editor_$.find(".Rk-Edit-Description").val(), uri: _this.editor_$.find(".Rk-Edit-URI").val() } - _this._project.updateElement( + _this._project.updateNode( _element, _data, Rkns._SAVE @@ -525,7 +525,7 @@ title: _this.editor_$.find(".Rk-Edit-Title").val(), uri: _this.editor_$.find(".Rk-Edit-URI").val() } - _this._project.updateElement( + _this._project.updateEdge( _element, _data, Rkns._SAVE @@ -546,7 +546,7 @@ Rkns.Renderer.NodeEditButton.prototype._init = function() { this._renderer.node_layer.activate(); - this.type = "node-edit-button"; + this.type = "Node-edit-button"; this.sector = Rkns.Renderer.Utils.sector(this, 1 + Rkns._NODE_RADIUS, 3 * Rkns._NODE_RADIUS, - 90, 30, 2, 'img/edit.png'); } @@ -589,7 +589,7 @@ Rkns.Renderer.NodeRemoveButton.prototype._init = function() { this._renderer.node_layer.activate(); - this.type = "node-remove-button"; + this.type = "Node-remove-button"; this.sector = Rkns.Renderer.Utils.sector(this, 1 + Rkns._NODE_RADIUS, 3 * Rkns._NODE_RADIUS, -210, -90, 2, 'img/remove.png'); } @@ -632,7 +632,7 @@ Rkns.Renderer.NodeLinkButton.prototype._init = function() { this._renderer.node_layer.activate(); - this.type = "node-link-button"; + this.type = "Node-link-button"; this.sector = Rkns.Renderer.Utils.sector(this, 1 + Rkns._NODE_RADIUS , 3 * Rkns._NODE_RADIUS, 30, 150, 2, 'img/link.png'); } @@ -669,7 +669,7 @@ Rkns.Renderer.EdgeEditButton.prototype._init = function() { this._renderer.edge_layer.activate(); - this.type = "edge-edit-button"; + this.type = "Edge-edit-button"; this.sector = Rkns.Renderer.Utils.sector(this, 5 , 2 * Rkns._NODE_RADIUS, - 60, 60, 2, 'img/edit.png'); } @@ -711,7 +711,7 @@ Rkns.Renderer.EdgeRemoveButton.prototype._init = function() { this._renderer.edge_layer.activate(); - this.type = "edge-remove-button"; + this.type = "Edge-remove-button"; this.sector = Rkns.Renderer.Utils.sector(this, 5, 2 * Rkns._NODE_RADIUS, - 240, -120, 2, 'img/remove.png'); } Rkns.Renderer.EdgeRemoveButton.prototype.moveTo = function(_pos) { @@ -752,13 +752,13 @@ this._project = _project; this._MARGIN_X = 80; this._MARGIN_Y = 50; - var _canvas_id = this._project._opts.canvas_id; - this.$ = Rkns.$("#"+_canvas_id); - this.editor_$ = Rkns.$(".Rk-Editor"); - this.editor_$.html(this.editorTemplate({ + this.$ = Rkns.$(".Rk-Render"); + this.$.html(this.template({ l10n: this._project.l10n - })); - paper.setup(document.getElementById(_canvas_id)); + })) + this.canvas_$ = this.$.find(".Rk-Canvas"); + this.editor_$ = this.$.find(".Rk-Editor"); + paper.setup(this.canvas_$[0]); this.scale = 1; this.offset = paper.view.center; this.totalScroll = 0; @@ -782,24 +782,24 @@ _tool.onMouseUp = function(_event) { _this.onMouseUp(_event); } - this.$.mousewheel(function(_event, _delta) { + this.canvas_$.mousewheel(function(_event, _delta) { _this.onScroll(_event, _delta); }) - this.$.dblclick(function(_event) { + this.canvas_$.dblclick(function(_event) { _this.onDoubleClick(_event); }); this.editor_$.find(".Rk-ZoomOut").click(function() { _this.offset = new paper.Point([ - _this.$.width(), - _this.$.height() + _this.canvas_$.width(), + _this.canvas_$.height() ]).multiply( .5 * ( 1 - Math.SQRT1_2 ) ).add(_this.offset.multiply( Math.SQRT1_2 )); _this.scale *= Math.SQRT1_2; _this.redraw(); }); this.editor_$.find(".Rk-ZoomIn").click(function() { _this.offset = new paper.Point([ - _this.$.width(), - _this.$.height() + _this.canvas_$.width(), + _this.canvas_$.height() ]).multiply( .5 * ( 1 - Math.SQRT2 ) ).add(_this.offset.multiply( Math.SQRT2 )); _this.scale *= Math.SQRT2; _this.redraw(); @@ -810,8 +810,10 @@ } } -Rkns.Renderer.Scene.prototype.editorTemplate = Rkns._.template( - '
    ' +Rkns.Renderer.Scene.prototype.template = Rkns._.template( + '
    ' + + '
    ' + + '
    ' ); Rkns.Renderer.Scene.prototype.toPaperCoords = function(_point) { @@ -833,7 +835,7 @@ _maxy = Math.max.apply(Math, _yy); this.scale = Math.min((paper.view.size.width - 2 * this._MARGIN_X) / (_maxx - _minx), (paper.view.size.height - 2 * this._MARGIN_Y) / (_maxy - _miny)); this.offset = paper.view.center.subtract(new paper.Point([(_maxx + _minx) / 2, (_maxy + _miny) / 2]).multiply(this.scale)); - this.controllers = new Rkns.Model.List(); + this.controllers = new Rkns.ViewModel.List(); this._project.nodes.forEach(function(_node) { _this.addController("Node", _node); }); @@ -904,10 +906,10 @@ var _hitResult = paper.project.hitTest(_event.point); if (_hitResult && typeof _hitResult.item.__controller !== "undefined") { this.click_target = _hitResult.item.__controller; - if (this.click_target.type === "node" && _hitResult.type === "stroke") { + if (this.click_target.type === "Node" && _hitResult.type === "stroke") { this.addTempEdge(this.click_target, _event.point); } - if (this.click_target.type === "node-link-button") { + if (this.click_target.type === "Node-link-button") { this.addTempEdge(this.click_target.node_controller, _event.point); } } else { @@ -936,7 +938,7 @@ Rkns.Renderer.Scene.prototype.onScroll = function(_event, _scrolldelta) { this.totalScroll += _scrolldelta; if (Math.abs(this.totalScroll) >= 1) { - var _off = this.$.offset(), + var _off = this.canvas_$.offset(), _delta = new paper.Point([ _event.pageX - _off.left, _event.pageY - _off.top @@ -954,7 +956,7 @@ } Rkns.Renderer.Scene.prototype.onDoubleClick = function(_event) { - var _off = this.$.offset(), + var _off = this.canvas_$.offset(), _point = new paper.Point([ _event.pageX - _off.left, _event.pageY - _off.top diff -r d49b927bfe62 -r bd58970ffd16 client/js/random-data.js --- a/client/js/random-data.js Thu Aug 16 17:24:13 2012 +0200 +++ b/client/js/random-data.js Fri Aug 17 12:50:00 2012 +0200 @@ -1,6 +1,6 @@ -Rkns.Serializers.RandomData = Rkns.Utils.inherit(Rkns.Serializers._Base); +Rkns.RemoteModels.RandomData = Rkns.Utils.inherit(Rkns.RemoteModels._Base); -Rkns.Serializers.RandomData.prototype._init = function() { +Rkns.RemoteModels.RandomData.prototype._init = function() { this._USER_COUNT = 5; this._NODE_COUNT = 20; this._EDGE_COUNT = 40; @@ -8,18 +8,18 @@ this.load(); } -Rkns.Serializers.RandomData.prototype.load = function() { +Rkns.RemoteModels.RandomData.prototype.load = function() { var _p = this._project; _p.title = "Random Generated Data"; for (var i = 0; i < this._USER_COUNT; i++) { - _p.users.push(new Rkns.Model.User(_p, { + _p.users.push(new Rkns.ViewModel.User(_p, { id: "user-"+i, title: "User #"+(1+i), color: this.user_colors[i] })); } for (var i = 0; i < this._NODE_COUNT; i++) { - _p.nodes.push(new Rkns.Model.Node(_p, { + _p.nodes.push(new Rkns.ViewModel.Node(_p, { id: "node-"+i, title: "Node #"+(1+i), created_by: "user-" + Math.floor(this._USER_COUNT*Math.random()), @@ -37,7 +37,7 @@ while(_from === _to) { _to = Math.floor(this._NODE_COUNT*Math.random()); } - _p.edges.push(new Rkns.Model.Edge(_p, { + _p.edges.push(new Rkns.ViewModel.Edge(_p, { id: "edge-"+i, title: "Edge #"+(1+i), created_by: "user-" + Math.floor(this._USER_COUNT*Math.random()), diff -r d49b927bfe62 -r bd58970ffd16 client/js/view-model.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/js/view-model.js Fri Aug 17 12:50:00 2012 +0200 @@ -0,0 +1,310 @@ +/* Defines the View Model */ + +Rkns.ViewModel = {} + +/* Project Class */ + +Rkns.ViewModel.Project = function() { + this.users = new Rkns.ViewModel.List(); + this.nodes = new Rkns.ViewModel.List(); + this.edges = new Rkns.ViewModel.List(); +} + +Rkns.ViewModel.Project.prototype.addNode = function(_props, _render_save) { + var _node = new Rkns.ViewModel.Node(this, _props); + this.nodes.push(_node); + if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { + var _controller = this.renderer.addController("Node", _node); + _controller.redraw(); + } + if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { + this.remotemodel.addNode(_node); + } + return _node; +} + +Rkns.ViewModel.Project.prototype.addEdge = function(_props, _render_save) { + var _edge = new Rkns.ViewModel.Edge(this, _props); + this.edges.push(_edge); + if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { + var _controller = this.renderer.addController("Edge", _edge); + _controller.redraw(); + } + if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { + this.remotemodel.addEdge(_edge); + } + return _edge; +} + +Rkns.ViewModel.Project.prototype.addUser = function(_props, _render_save) { + var _user = new Rkns.ViewModel.User(this, _props); + this.users.push(_user); + if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { + this.remotemodel.addUser(_user); + } + return _user; +} + + +Rkns.ViewModel.Project.prototype.updateNode = function(_element, _props, _render_save) { + this.updateElement("Node", _element, _props, _render_save) +} + +Rkns.ViewModel.Project.prototype.updateEdge = function(_element, _props, _render_save) { + this.updateElement("Edge", _element, _props, _render_save) +} + +Rkns.ViewModel.Project.prototype.updateElement = function(_type, _element, _props, _render_save) { + Rkns._(_props).each(function(_v, _k) { + _element[_k] = _v; + }); + if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { + if (typeof _element.__controller !== "undefined") { + _element.__controller.redraw(); + } else { + this._renderer.redraw(); + } + } + if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { + this.remotemodel[ "update" + _type ](_element); + } +} + +Rkns.ViewModel.Project.prototype.removeNode = function(_node, _render_save) { + this.nodes.removeId(_node.id); + if (typeof _node.__controller !== "undefined") { + this.renderer.removeController(_node.__controller); + } + var _this = this; + this.edges = this.edges.filter(function(_edge) { + var _keep = _edge.from !== _node && _edge.to !== _node; + if (!_keep && typeof _edge.__controller !== "undefined") { + _this.renderer.removeController(_edge.__controller); + } + return _keep; + }); + if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { + this.renderer.redraw(); + } + if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { + this.remotemodel.removeNode(_node.id); + } + return _node; +} + +Rkns.ViewModel.Project.prototype.removeEdge = function(_edge, _render_save) { + this.edges.removeId(_edge.id); + if (typeof _edge.__controller !== "undefined") { + this.renderer.removeController(_edge.__controller); + } + if (typeof _render_save !== "undefined" && (_render_save&Rkns._RENDER)) { + this.renderer.redraw(); + } + if (typeof _render_save !== "undefined" && (_render_save&Rkns._SAVE)) { + this.remotemodel.removeEdge(_edge.id); + } + return _edge; +} + +/* Base Element */ + +Rkns.ViewModel._BaseElement = function(_project, _props) { + if (typeof _props !== "undefined") { + this._project = _project; + this.id = _props.id || Rkns.Utils.getUID(this.type); + this.title = _props.title || "(untitled " + this.type + ")"; + this.description = _props.description || ""; + this.uri = _props.uri || ""; + } +} + +Rkns.ViewModel._BaseElement.prototype.addReference = function(_propName, _list, _id, _default) { + var _element = _list.getElement(_id); + if (typeof _element === "undefined" && typeof _default !== "undefined") { + this[ _propName ] = _default; + this[ _propName + "_id" ] = _default.id; + } else { + this[ _propName + "_id" ] = _id; + this[ _propName ] = _element; + } +} + +Rkns.ViewModel._BaseElement.prototype.updateGraphics = function() { + this._project.renderer.redraw(); +} + +Rkns.ViewModel._BaseElement.prototype.updateData = function() { + this._project.remotemodel.save(this); +} + +/* Element Class Generator */ + +Rkns.ViewModel._elementClass = function(_type) { + return Rkns.Utils.inherit(Rkns.ViewModel._BaseElement, function() { + this.type = _type; + }); +} + +/* User Model */ + +Rkns.ViewModel.User = Rkns.ViewModel._elementClass("user"); + +Rkns.ViewModel.User.prototype._init = function(_project, _props) { + this.color = _props.color || "#666666"; +} + +/* Node Model */ + +Rkns.ViewModel.Node = Rkns.ViewModel._elementClass("node"); + +Rkns.ViewModel.Node.prototype._init = function(_project, _props) { + this.addReference("created_by", this._project.users, _props.created_by, _project.current_user); + this.position = _props.position; + this.description = _props.description || ""; +} + +Rkns.ViewModel.Node.prototype.setPosition = function(_x, _y) { + if (typeof _x === "object") { + if (typeof _x.x !== "undefined" && typeof _x.y !== "undefined") { + this.position.x = _x.x; + this.position.y = _x.y; + } else { + if (typeof _x.length !== "undefined") { + this.position.x = _x[0]; + this.position.y = _x[1]; + } + } + } else { + if (typeof _y !== "undefined") { + this.position.x = +_x; + this.position.y = +_y; + } + } +} + +/* Edge Model */ + +Rkns.ViewModel.Edge = Rkns.ViewModel._elementClass("edge"); + +Rkns.ViewModel.Edge.prototype._init = function(_project, _props) { + this.addReference("created_by", this._project.users, _props.created_by, _project.current_user); + this.addReference("from", this._project.nodes, _props.from); + this.addReference("to", this._project.nodes, _props.to); +} + +/* List Helper Functions -- See Metadataplayer */ + +Rkns.ViewModel.List = function() { + Array.call(this); + this.idIndex = []; +} + +Rkns.ViewModel.List.prototype = new Array(); + +Rkns.ViewModel.List.prototype.hasId = function(_id) { + return Rkns._(this.idIndex).include(_id); +} + +Rkns.ViewModel.List.prototype.getIds = function(_id) { + return this.idIndex; +} + +/* On recent browsers, forEach and map are defined and do what we want. + * Otherwise, we'll use the Underscore.js functions + */ +if (typeof Array.prototype.forEach === "undefined") { + Rkns.ViewModel.List.prototype.forEach = function(_callback) { + var _this = this; + Rkns._(this).forEach(function(_value, _key) { + _callback(_value, _key, _this); + }); + } +} + +if (typeof Array.prototype.map === "undefined") { + Rkns.ViewModel.List.prototype.map = function(_callback) { + var _this = this; + return Rkns._(this).map(function(_value, _key) { + return _callback(_value, _key, _this); + }); + } +} + +/* We override Array's filter function because it doesn't return an Rkns.ViewModel.List + */ +Rkns.ViewModel.List.prototype.filter = function(_callback) { + var _this = this, + _res = new Rkns.ViewModel.List(); + _res.addElements(Rkns._(this).filter(function(_value, _key) { + return _callback(_value, _key, _this); + })); + return _res; +} + +Rkns.ViewModel.List.prototype.slice = function(_start, _end) { + var _res = new Rkns.ViewModel.List(); + _res.addElements(Array.prototype.slice.call(this, _start, _end)); + return _res; +} + +Rkns.ViewModel.List.prototype.splice = function(_start, _end) { + var _res = new Rkns.ViewModel.List(); + _res.addElements(Array.prototype.splice.call(this, _start, _end)); + this.idIndex.splice(_start, _end); + return _res; +} + +/* Array has a sort function, but it's not as interesting as Underscore.js's sortBy + * and won't return a new Rkns.ViewModel.List + */ +Rkns.ViewModel.List.prototype.sortBy = function(_callback) { + var _this = this, + _res = new Rkns.ViewModel.List(); + _res.addElements(Rkns._(this).sortBy(function(_value, _key) { + return _callback(_value, _key, _this); + })); + return _res; +} + +Rkns.ViewModel.List.prototype.push = function(_el) { + if (typeof _el === "undefined" || typeof _el.id === "undefined") { + return; + } + var _index = (Rkns._(this.idIndex).indexOf(_el.id)); + if (_index === -1) { + this.idIndex.push(_el.id); + Array.prototype.push.call(this, _el); + } else { + this[_index] = _el; + } +} + +Rkns.ViewModel.List.prototype.addElements = function(_array) { + var _this = this; + Rkns._(_array).forEach(function(_el) { + _this.push(_el); + }); +} + +Rkns.ViewModel.List.prototype.removeId = function(_id) { + var _index = (Rkns._(this.idIndex).indexOf(_id)); + if (_index !== -1) { + this.splice(_index,1); + } +} + +Rkns.ViewModel.List.prototype.removeIds = function(_list) { + var _this = this; + Rkns._(_list).forEach(function(_id) { + _this.removeId(_id); + }); +} + +Rkns.ViewModel.List.prototype.getElement = function(_id) { + var _index = Rkns._(this.idIndex).indexOf(_id); + if (_index === -1) { + return undefined; + } else { + return this[_index]; + } +} diff -r d49b927bfe62 -r bd58970ffd16 client/render-test.html --- a/client/render-test.html Thu Aug 16 17:24:13 2012 +0200 +++ b/client/render-test.html Fri Aug 17 12:50:00 2012 +0200 @@ -12,17 +12,16 @@ - - - + + +