server/src/main/webapp/static/js/paper-renderer.js
changeset 47 267d67791e05
child 48 01fb9167ad75
equal deleted inserted replaced
46:7e132e2a48ca 47:267d67791e05
       
     1 Rkns.Renderer = {
       
     2     _MARGIN_X: 80,
       
     3     _MARGIN_Y: 50,
       
     4     _MIN_DRAG_DISTANCE: 2,
       
     5     _NODE_RADIUS: 25,
       
     6     _NODE_BUTTON_INNER: 26,
       
     7     _NODE_BUTTON_OUTER: 60,
       
     8     _EDGE_BUTTON_INNER: 1,
       
     9     _EDGE_BUTTON_OUTER: 40,
       
    10     _NODE_FONT_SIZE: 10,
       
    11     _EDGE_FONT_SIZE: 9,
       
    12     _NODE_MAX_CHAR: 50,
       
    13     _EDGE_MAX_CHAR: 40,
       
    14     _ARROW_LENGTH: 16,
       
    15     _ARROW_WIDTH: 8,
       
    16     _EDITOR_ARROW_LENGTH : 20,
       
    17     _EDITOR_ARROW_WIDTH : 40,
       
    18     _EDITOR_MARGIN : 15,
       
    19     _EDITOR_PADDING : 10,
       
    20     _EDITOR_GRADIENT : new paper.Gradient(['#f0f0f0', '#d0d0d0']),
       
    21     _CLICKMODE_ADDNODE : 1,
       
    22     _CLICKMODE_STARTEDGE : 2,
       
    23     _CLICKMODE_ENDEDGE : 3
       
    24 }
       
    25 
       
    26 Rkns.Renderer.Utils = {
       
    27     drawEditBox : function(_coords, _path, _width, _xmargin, _selector) {
       
    28         _selector.css({
       
    29             width: (_width - 2* Rkns.Renderer._EDITOR_PADDING),
       
    30         })
       
    31         var _height = _selector.outerHeight() + 2* Rkns.Renderer._EDITOR_PADDING,
       
    32             _isLeft = (_coords.x < paper.view.center.x ? 1 : -1),
       
    33             _left = _coords.x + _isLeft * ( _xmargin + Rkns.Renderer._EDITOR_ARROW_LENGTH ),
       
    34             _right = _coords.x + _isLeft * ( _xmargin + Rkns.Renderer._EDITOR_ARROW_LENGTH + _width ),
       
    35             _top = _coords.y - _height / 2;
       
    36         if (_top < Rkns.Renderer._EDITOR_MARGIN) {
       
    37             _top = Math.min( Rkns.Renderer._EDITOR_MARGIN, _coords.y - Rkns.Renderer._EDITOR_ARROW_WIDTH / 2 );
       
    38         }
       
    39         var _bottom = _top + _height;
       
    40         if (_bottom > (paper.view.size.height - Rkns.Renderer._EDITOR_MARGIN)) {
       
    41             _bottom = Math.max( paper.view.size.height - Rkns.Renderer._EDITOR_MARGIN, _coords.y + Rkns.Renderer._EDITOR_ARROW_WIDTH / 2 );
       
    42             _top = _bottom - _height;
       
    43         }
       
    44         _path.segments[0].point
       
    45             = _path.segments[7].point
       
    46             = _coords.add([_isLeft * _xmargin, 0]);
       
    47         _path.segments[1].point.x
       
    48             = _path.segments[2].point.x
       
    49             = _path.segments[5].point.x
       
    50             = _path.segments[6].point.x
       
    51             = _left;
       
    52         _path.segments[3].point.x
       
    53             = _path.segments[4].point.x
       
    54             = _right;
       
    55         _path.segments[2].point.y
       
    56             = _path.segments[3].point.y
       
    57             = _top;
       
    58         _path.segments[4].point.y
       
    59             = _path.segments[5].point.y
       
    60             = _bottom;
       
    61         _path.segments[1].point.y = _coords.y - Rkns.Renderer._EDITOR_ARROW_WIDTH / 2;
       
    62         _path.segments[6].point.y = _coords.y + Rkns.Renderer._EDITOR_ARROW_WIDTH / 2;
       
    63         _path.closed = true;
       
    64         _path.fillColor = new paper.GradientColor(Rkns.Renderer._EDITOR_GRADIENT, [0,_top], [0, _bottom]);
       
    65         _selector.css({
       
    66             left: (Rkns.Renderer._EDITOR_PADDING + Math.min(_left, _right)),
       
    67             top: (Rkns.Renderer._EDITOR_PADDING + _top)
       
    68         });
       
    69     },
       
    70     sector : function(_repr, _inR, _outR, _startAngle, _endAngle, _padding, _imgsrc, _caption) {
       
    71         var _startRads = _startAngle * Math.PI / 180,
       
    72             _endRads = _endAngle * Math.PI / 180,
       
    73             _img = new Image(),
       
    74             _span = _endRads - _startRads,
       
    75             _k = .0879 * _span,
       
    76             _kin = _k * _inR,
       
    77             _kout = _k * _outR,
       
    78             _startdx = - Math.sin(_startRads),
       
    79             _startdy = Math.cos(_startRads),
       
    80             _startXIn = Math.cos(_startRads) * _inR + _padding * _startdx,
       
    81             _startYIn = Math.sin(_startRads) * _inR + _padding * _startdy,
       
    82             _startXOut = Math.cos(_startRads) * _outR + _padding * _startdx,
       
    83             _startYOut = Math.sin(_startRads) * _outR + _padding * _startdy,
       
    84             _enddx = - Math.sin(_endRads),
       
    85             _enddy = Math.cos(_endRads),
       
    86             _endXIn = Math.cos(_endRads) * _inR - _padding * _enddx,
       
    87             _endYIn = Math.sin(_endRads) * _inR - _padding * _enddy,
       
    88             _endXOut = Math.cos(_endRads) * _outR - _padding * _enddx,
       
    89             _endYOut = Math.sin(_endRads) * _outR - _padding * _enddy,
       
    90             _centerR = (_inR + _outR)/2,
       
    91             _centerRads = (_startRads + _endRads) / 2,
       
    92             _centerX = Math.cos(_centerRads) * _centerR,
       
    93             _centerY = Math.sin(_centerRads) * _centerR,
       
    94             _textX = Math.cos(_centerRads) * (_outR + 3),
       
    95             _textY = Math.sin(_centerRads) * (_outR + 3),
       
    96             _segments = [];
       
    97         _segments.push([[_startXIn, _startYIn], [0, 0], [ _kin * _startdx, _kin * _startdy ]]);
       
    98         for (var i = 1; i < 4; i++) {
       
    99             var _rads = i * _span / 4 + _startRads,
       
   100                 _dx = - Math.sin(_rads),
       
   101                 _dy = Math.cos(_rads),
       
   102                 _x = Math.cos(_rads) * _inR,
       
   103                 _y = Math.sin(_rads) * _inR;
       
   104             _segments.push([[_x, _y], [ - _kin * _dx, - _kin * _dy], [ _kin * _dx, _kin * _dy ]]);
       
   105         }
       
   106         _segments.push([[_endXIn, _endYIn], [ - _kin * _enddx, - _kin * _enddy ], [0,0]]);
       
   107         _segments.push([[_endXOut, _endYOut], [ 0,0 ], [ - _kout * _enddx, - _kout * _enddy ]]);
       
   108         for (var i = 3; i > 0; i--) {
       
   109             var _rads = i * _span / 4 + _startRads,
       
   110                 _dx = - Math.sin(_rads),
       
   111                 _dy = Math.cos(_rads),
       
   112                 _x = Math.cos(_rads) * _outR,
       
   113                 _y = Math.sin(_rads) * _outR;
       
   114             _segments.push([[_x, _y], [ _kout * _dx, _kout * _dy], [ - _kout * _dx, - _kout * _dy ]]);
       
   115         }
       
   116         _segments.push([[_startXOut, _startYOut], [ _kout * _startdx, _kout * _startdy ], [0, 0]]);
       
   117         var _path = new paper.Path();
       
   118         _path.add.apply(_path, _segments);
       
   119         _path.fillColor = "#333333";
       
   120         _path.opacity = .5;
       
   121         _path.closed = true;
       
   122         _path.__representation = _repr;
       
   123         if (_textX >= -2 && _textX <= 2) {
       
   124             if (_textY > 0) {
       
   125                 _textY += 6;
       
   126             }
       
   127         }
       
   128         var _text = new paper.PointText(_textX,_textY);
       
   129         _text.characterStyle = {
       
   130             fontSize: 9,
       
   131             fillColor: '#c000c0'
       
   132         };
       
   133         if (_textX > 2) {
       
   134             _text.paragraphStyle.justification = 'left';
       
   135         } else if (_textX < -2) {
       
   136             _text.paragraphStyle.justification = 'right';
       
   137         } else {
       
   138             _text.paragraphStyle.justification = 'center';
       
   139         }
       
   140         _text.visible = false;
       
   141         var _visible = false,
       
   142             _restPos = new paper.Point(-200, -200),
       
   143             _grp = new paper.Group([_path, _text]),
       
   144             _delta = _grp.position,
       
   145             _imgdelta = new paper.Point([_centerX, _centerY]),
       
   146             _currentPos = new paper.Point(0,0);
       
   147         _text.content = _caption;
       
   148         _grp.visible = false;
       
   149         _grp.position = _restPos;
       
   150         var _res = {
       
   151             show: function() {
       
   152                 _visible = true;
       
   153                 _grp.position = _currentPos.add(_delta);
       
   154                 _grp.visible = true;
       
   155             },
       
   156             moveTo: function(_point) {
       
   157                 _currentPos = _point;
       
   158                 if (_visible) {
       
   159                     _grp.position = _point.add(_delta);
       
   160                 }
       
   161             },
       
   162             hide: function() {
       
   163                 _visible = false;
       
   164                 _grp.visible = false;
       
   165                 _grp.position = _restPos;
       
   166             },
       
   167             select: function() {
       
   168                 _path.opacity = .8;
       
   169                 _text.visible = true;
       
   170             },
       
   171             unselect: function() {
       
   172                 _path.opacity = .5;
       
   173                 _text.visible = false;
       
   174             },
       
   175             destroy: function() {
       
   176                 _grp.remove();
       
   177             }
       
   178         }
       
   179         _img.onload = function() {
       
   180             var _h = _img.height;
       
   181             var _raster = new paper.Raster(_img);
       
   182             _raster.position = _imgdelta.add(_grp.position).subtract(_delta);
       
   183             _grp.addChild(_raster);
       
   184         }
       
   185         _img.src = _imgsrc;
       
   186         return _res
       
   187     }
       
   188 }
       
   189 
       
   190 Rkns.Renderer._BaseRepresentation = function(_renderer, _model) {
       
   191     if (typeof _renderer !== "undefined") {
       
   192         this.renderer = _renderer;
       
   193         this.project = _renderer.renkan.project;
       
   194         this.model = _model;
       
   195         if (this.model) {
       
   196             var _this = this;
       
   197             this._changeBinding = function() {
       
   198                 _this.redraw();
       
   199             }
       
   200             this._removeBinding = function() {
       
   201                 _renderer.removeRepresentation(_this);
       
   202                 _renderer.redraw();
       
   203             }
       
   204             this.model.on("change", this._changeBinding );
       
   205             this.model.on("remove", this._removeBinding );
       
   206         }
       
   207     }
       
   208 }
       
   209 
       
   210 Rkns.Renderer._BaseRepresentation.prototype.super = function(_func) {
       
   211     Rkns.Renderer._BaseRepresentation.prototype[_func].apply(this, Array.prototype.slice.call(arguments, 1));
       
   212 }
       
   213 
       
   214 Rkns.Renderer._BaseRepresentation.prototype.select = function() {}
       
   215 
       
   216 Rkns.Renderer._BaseRepresentation.prototype.unselect = function() {}
       
   217 
       
   218 Rkns.Renderer._BaseRepresentation.prototype.highlight = function() {}
       
   219 
       
   220 Rkns.Renderer._BaseRepresentation.prototype.unhighlight = function() {}
       
   221 
       
   222 Rkns.Renderer._BaseRepresentation.prototype.mouseup = function() {}
       
   223 
       
   224 Rkns.Renderer._BaseRepresentation.prototype.destroy = function() {
       
   225     if (this.model) {
       
   226         this.model.off("change", this._changeBinding );
       
   227         this.model.off("remove", this._removeBinding );
       
   228     }
       
   229 }
       
   230 
       
   231 Rkns.Renderer.Node = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   232 
       
   233 Rkns.Renderer.Node.prototype._init = function() {
       
   234     this.renderer.node_layer.activate();
       
   235     this.type = "Node";
       
   236     this.circle = new paper.Path.Circle([0, 0], Rkns.Renderer._NODE_RADIUS);
       
   237     this.circle.fillColor = '#ffffff';
       
   238     this.circle.__representation = this;
       
   239     this.title = new paper.PointText([0,0]);
       
   240     this.title.characterStyle = {
       
   241         fontSize: Rkns.Renderer._NODE_FONT_SIZE,
       
   242         fillColor: 'black'
       
   243     };
       
   244     this.edit_button = new Rkns.Renderer.NodeEditButton(this.renderer, null);
       
   245     this.edit_button.node_representation = this;
       
   246     this.remove_button = new Rkns.Renderer.NodeRemoveButton(this.renderer, null);
       
   247     this.remove_button.node_representation = this;
       
   248     this.link_button = new Rkns.Renderer.NodeLinkButton(this.renderer, null);
       
   249     this.link_button.node_representation = this;
       
   250     this.title.paragraphStyle.justification = 'center';
       
   251 }
       
   252 
       
   253 Rkns.Renderer.Node.prototype.redraw = function() {
       
   254     var _model_coords = new paper.Point(this.model.get("position"));
       
   255     this.paper_coords = this.renderer.toPaperCoords(_model_coords);
       
   256     this.circle.position = this.paper_coords;
       
   257     this.title.content = this.model.get("title");
       
   258     this.title.position = this.paper_coords.add([0, Rkns.Renderer._NODE_RADIUS + 1.5 *Rkns.Renderer._NODE_FONT_SIZE]);
       
   259     this.circle.strokeColor = this.model.get("created_by").get("color");
       
   260     this.edit_button.moveTo(this.paper_coords);
       
   261     this.remove_button.moveTo(this.paper_coords);
       
   262     this.link_button.moveTo(this.paper_coords);
       
   263     var _img = this.model.get("image");
       
   264     if (_img && _img !== this.img) {
       
   265         var _image = new Image(),
       
   266             _this = this;
       
   267         _image.onload = function() {
       
   268             if (_this.node_image) {
       
   269                 _this.node_image.remove();
       
   270             }
       
   271             _this.renderer.node_layer.activate();
       
   272             var _ratio = Math.min(1, 2 * Rkns.Renderer._NODE_RADIUS / _image.width, 2 * Rkns.Renderer._NODE_RADIUS / _image.height );
       
   273             var _raster = new paper.Raster(_image);
       
   274             var _clip = new paper.Path.Circle([0, 0], Rkns.Renderer._NODE_RADIUS);
       
   275             _raster.scale(_ratio);
       
   276             _this.node_image = new paper.Group(_clip, _raster);
       
   277             _this.node_image.opacity = .9;
       
   278             /* This is a workaround to allow clipping at group level
       
   279              * If opacity was set to 1, paper.js would merge all clipping groups in one (known bug).
       
   280             */
       
   281             _this.node_image.clipped = true;
       
   282             _this.node_image.position = _this.paper_coords;
       
   283             _this.node_image.__representation = _this;
       
   284             _clip.__representation = _this;
       
   285             paper.view.draw();
       
   286         }
       
   287         _image.src = _img;
       
   288     }
       
   289     this.img = _img;
       
   290     if (this.node_image) {
       
   291         if (!this.img) {
       
   292             this.node_image.remove();
       
   293             delete this.node_image;
       
   294         } else {
       
   295             this.node_image.position = this.paper_coords;
       
   296         }
       
   297     }
       
   298 }
       
   299 
       
   300 Rkns.Renderer.Node.prototype.paperShift = function(_delta) {
       
   301     var _coords = this.renderer.toModelCoords(this.paper_coords.add(_delta)),
       
   302         _data = {
       
   303             position: {
       
   304                 x: _coords.x,
       
   305                 y: _coords.y
       
   306             }
       
   307         };
       
   308     this.model.set(_data);
       
   309     this.renderer.redraw();
       
   310 }
       
   311 
       
   312 Rkns.Renderer.Node.prototype.openEditor = function() {
       
   313     this.renderer.removeRepresentationsOfType("editor");
       
   314     var _editor = this.renderer.addRepresentation("NodeEditor",null);
       
   315     _editor.node_representation = this;
       
   316     _editor.draw();
       
   317 }
       
   318 
       
   319 Rkns.Renderer.Node.prototype.select = function() {
       
   320     this.circle.strokeWidth = 3;
       
   321     this.edit_button.show();
       
   322     this.remove_button.show();
       
   323     this.link_button.show();
       
   324     var _uri = this.model.get("uri");
       
   325     Rkns.$('.Rk-Bin-Item').each(function() {
       
   326         var _el = Rkns.$(this);
       
   327         if (_el.attr("data-uri") == _uri) {
       
   328             _el.addClass("selected");
       
   329         }
       
   330     });
       
   331 }
       
   332 
       
   333 Rkns.Renderer.Node.prototype.unselect = function(_newTarget) {
       
   334     if (!_newTarget || _newTarget.node_representation !== this) {
       
   335         this.edit_button.hide();
       
   336         this.remove_button.hide();
       
   337         this.link_button.hide();
       
   338         this.circle.strokeWidth = 1;
       
   339         Rkns.$('.Rk-Bin-Item').removeClass("selected");
       
   340     }
       
   341 }
       
   342 
       
   343 Rkns.Renderer.Node.prototype.highlight = function() {
       
   344     this.circle.fillColor = "#ffff80";
       
   345     if (this.node_image) {
       
   346         this.node_image.opacity = .5;
       
   347     }
       
   348 }
       
   349 
       
   350 Rkns.Renderer.Node.prototype.unhighlight = function(_newTarget) {
       
   351     this.circle.fillColor = "#ffffff";
       
   352     if (this.node_image) {
       
   353         this.node_image.opacity = .9;
       
   354     }
       
   355 }
       
   356 
       
   357 Rkns.Renderer.Node.prototype.mouseup = function(_event) {
       
   358     if (!this.renderer.is_dragging) {
       
   359         this.openEditor();
       
   360     }
       
   361     this.renderer.click_target = null;
       
   362     this.renderer.is_dragging = false;
       
   363 }
       
   364 
       
   365 Rkns.Renderer.Node.prototype.destroy = function(_event) {
       
   366     this.super("destroy");
       
   367     this.edit_button.destroy();
       
   368     this.remove_button.destroy();
       
   369     this.link_button.destroy();
       
   370     this.circle.remove();
       
   371     this.title.remove();
       
   372     if (this.node_image) {
       
   373         this.node_image.remove();
       
   374     }
       
   375 }
       
   376 
       
   377 /* */
       
   378 
       
   379 Rkns.Renderer.Edge = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   380 
       
   381 Rkns.Renderer.Edge.prototype._init = function() {
       
   382     this.renderer.edge_layer.activate();
       
   383     this.type = "Edge";
       
   384     this.from_representation = this.renderer.getRepresentationByModel(this.model.get("from"));
       
   385     this.to_representation = this.renderer.getRepresentationByModel(this.model.get("to"));
       
   386     this.bundle = this.renderer.addToBundles(this);
       
   387     this.line = new paper.Path();
       
   388     this.line.add([0,0],[0,0],[0,0]);
       
   389     this.line.__representation = this;
       
   390     this.arrow = new paper.Path();
       
   391     this.arrow.add([0,0],[Rkns.Renderer._ARROW_LENGTH,Rkns.Renderer._ARROW_WIDTH / 2],[0,Rkns.Renderer._ARROW_WIDTH]);
       
   392     this.arrow.__representation = this;
       
   393     this.text = new paper.PointText();
       
   394     this.text.characterStyle = {
       
   395         fontSize: Rkns.Renderer._EDGE_FONT_SIZE,
       
   396         fillColor: 'black'
       
   397     };
       
   398     this.text.paragraphStyle.justification = 'center';
       
   399     this.text_angle = 0;
       
   400     this.arrow_angle = 0;
       
   401     this.edit_button = new Rkns.Renderer.EdgeEditButton(this.renderer, null);
       
   402     this.edit_button.edge_representation = this;
       
   403     this.remove_button = new Rkns.Renderer.EdgeRemoveButton(this.renderer, null);
       
   404     this.remove_button.edge_representation = this;
       
   405 }
       
   406 
       
   407 Rkns.Renderer.Edge.prototype.redraw = function() {
       
   408     var _p0a = this.from_representation.paper_coords,
       
   409         _p1a = this.to_representation.paper_coords,
       
   410         _v = _p1a.subtract(_p0a),
       
   411         _r = _v.length,
       
   412         _u = _v.divide(_r),
       
   413         _group_pos = this.bundle.getPosition(this),
       
   414         _delta = new paper.Point([- _u.y, _u.x]).multiply( 12 * _group_pos ),
       
   415         _p0b = _p0a.add(_delta), /* Adding a 4 px difference */
       
   416         _p1b = _p1a.add(_delta), /* to differentiate inbound and outbound links */
       
   417         _a = _v.angle,
       
   418         _handle = _v.divide(3),
       
   419         _color = this.model.get("created_by").get("color");
       
   420     this.paper_coords = _p0b.add(_p1b).divide(2);
       
   421     this.line.strokeColor = _color;
       
   422     this.line.segments[0].point = _p0a;
       
   423     this.line.segments[1].point = this.paper_coords;
       
   424     this.line.segments[1].handleIn = _handle.multiply(-1);
       
   425     this.line.segments[1].handleOut = _handle;
       
   426     this.line.segments[2].point = _p1a;
       
   427     this.arrow.rotate(_a - this.arrow_angle);
       
   428     this.arrow.fillColor = _color;
       
   429     this.arrow.position = this.paper_coords.subtract(_u.multiply(4));
       
   430     this.arrow_angle = _a;
       
   431     if (_a > 90) {
       
   432         _a -= 180;
       
   433     }
       
   434     if (_a < -90) {
       
   435         _a += 180;
       
   436     }
       
   437     this.text.rotate(_a - this.text_angle);
       
   438     this.text.content = this.model.get("title");
       
   439     this.text.position = this.paper_coords;
       
   440     this.text_angle = _a;
       
   441     this.edit_button.moveTo(this.paper_coords);
       
   442     this.remove_button.moveTo(this.paper_coords);
       
   443 }
       
   444 
       
   445 Rkns.Renderer.Edge.prototype.openEditor = function() {
       
   446     this.renderer.removeRepresentationsOfType("editor");
       
   447     var _editor = this.renderer.addRepresentation("EdgeEditor",null);
       
   448     _editor.edge_representation = this;
       
   449     _editor.draw();
       
   450 }
       
   451 
       
   452 Rkns.Renderer.Edge.prototype.select = function() {
       
   453     this.line.strokeWidth = 3;
       
   454     this.edit_button.show();
       
   455     this.remove_button.show();
       
   456 }
       
   457 
       
   458 Rkns.Renderer.Edge.prototype.unselect = function(_newTarget) {
       
   459     if (!_newTarget || _newTarget.edge_representation !== this) {
       
   460         this.edit_button.hide();
       
   461         this.remove_button.hide();
       
   462         this.line.strokeWidth = 1;
       
   463     }
       
   464 }
       
   465 
       
   466 Rkns.Renderer.Edge.prototype.mouseup = function(_event) {
       
   467     if (!this.renderer.is_dragging) {
       
   468         this.openEditor();
       
   469     }
       
   470     this.renderer.click_target = null;
       
   471     this.renderer.is_dragging = false;
       
   472 }
       
   473 
       
   474 Rkns.Renderer.Edge.prototype.paperShift = function(_delta) {
       
   475     this.from_representation.paperShift(_delta);
       
   476     this.to_representation.paperShift(_delta);
       
   477     this.renderer.redraw();
       
   478 }
       
   479 
       
   480 Rkns.Renderer.Edge.prototype.destroy = function() {
       
   481     this.super("destroy");
       
   482     this.line.remove();
       
   483     this.arrow.remove();
       
   484     this.text.remove();
       
   485     this.edit_button.destroy();
       
   486     this.remove_button.destroy();
       
   487     var _this = this;
       
   488     this.bundle.edges = Rkns._(this.bundle.edges).reject(function(_edge) {
       
   489         return _edge === _this;
       
   490     });
       
   491 }
       
   492 
       
   493 /* */
       
   494 
       
   495 Rkns.Renderer.TempEdge = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   496 
       
   497 Rkns.Renderer.TempEdge.prototype._init = function() {
       
   498     this.renderer.edge_layer.activate();
       
   499     this.type = "temp-edge";
       
   500     
       
   501     var _color = this.project.get("users").get(this.renderer.renkan.current_user).get("color");
       
   502     this.line = new paper.Path();
       
   503     this.line.strokeColor = _color;
       
   504     this.line.add([0,0],[0,0]);
       
   505     this.line.__representation = this;
       
   506     this.arrow = new paper.Path();
       
   507     this.arrow.fillColor = _color;
       
   508     this.arrow.add([0,0],[Rkns.Renderer._ARROW_LENGTH,Rkns.Renderer._ARROW_WIDTH / 2],[0,Rkns.Renderer._ARROW_WIDTH]);
       
   509     this.arrow.__representation = this;
       
   510     this.arrow_angle = 0;
       
   511 }
       
   512 
       
   513 Rkns.Renderer.TempEdge.prototype.redraw = function() {
       
   514     var _p0 = this.from_representation.paper_coords,
       
   515         _p1 = this.end_pos,
       
   516         _a = _p1.subtract(_p0).angle,
       
   517         _c = _p0.add(_p1).divide(2);
       
   518     this.line.segments[0].point = _p0;
       
   519     this.line.segments[1].point = _p1;
       
   520     this.arrow.rotate(_a - this.arrow_angle);
       
   521     this.arrow.position = _c;
       
   522     this.arrow_angle = _a;
       
   523 }
       
   524 
       
   525 Rkns.Renderer.TempEdge.prototype.paperShift = function(_delta) {
       
   526     this.end_pos = this.end_pos.add(_delta);
       
   527     var _hitResult = paper.project.hitTest(this.end_pos);
       
   528     this.renderer.findTarget(_hitResult);
       
   529     this.redraw();
       
   530 }
       
   531 
       
   532 Rkns.Renderer.TempEdge.prototype.mouseup = function(_event) {
       
   533     var _hitResult = paper.project.hitTest(_event.point),
       
   534         _model = this.from_representation.model,
       
   535         _endDrag = true;
       
   536     if (_hitResult && typeof _hitResult.item.__representation !== "undefined") {
       
   537         var _target = _hitResult.item.__representation;
       
   538         if (_target.type === "Node" && _model !== _target.model) {
       
   539             var _data = {
       
   540                 id: Rkns.Utils.getUID('edge'),
       
   541                 created_by: this.renderer.renkan.current_user,
       
   542                 from: _model.get("_id"),
       
   543                 to: _target.model.get("_id")
       
   544             };
       
   545             this.project.addEdge(_data);
       
   546         }
       
   547         if (_model === _target.model || (_target.node_representation && _target.node_representation.model === _model)) {
       
   548             _endDrag = false;
       
   549             this.renderer.is_dragging = true;
       
   550         }
       
   551     }
       
   552     if (_endDrag) {
       
   553         this.renderer.click_target = null;
       
   554         this.renderer.is_dragging = false;
       
   555         this.renderer.removeRepresentation(this);
       
   556         paper.view.draw();
       
   557     }
       
   558 }
       
   559 
       
   560 Rkns.Renderer.TempEdge.prototype.destroy = function() {
       
   561     this.arrow.remove();
       
   562     this.line.remove();
       
   563 }
       
   564 
       
   565 /* */
       
   566 
       
   567 Rkns.Renderer.NodeEditor = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   568 
       
   569 Rkns.Renderer.NodeEditor.prototype._init = function() {
       
   570     this.renderer.overlay_layer.activate();
       
   571     this.type = "editor";
       
   572     this.editor_block = new paper.Path();
       
   573     var _pts = Rkns._(Rkns._.range(8)).map(function() {return [0,0]});
       
   574     this.editor_block.add.apply(this.editor_block, _pts);
       
   575     this.editor_block.strokeWidth = 2;
       
   576     this.editor_block.strokeColor = "#999999";
       
   577     this.editor_block.fillColor = "#e0e0e0";
       
   578     this.editor_block.opacity = .8;
       
   579     this.editor_$ = Rkns.$('<div>')
       
   580         .appendTo(this.renderer.editor_$)
       
   581         .css({
       
   582             position: "absolute",
       
   583             opacity: .8
       
   584         })
       
   585         .hide();
       
   586 }
       
   587 
       
   588 Rkns.Renderer.NodeEditor.prototype.template = Rkns._.template(
       
   589     '<h2><span class="Rk-CloseX">&times;</span><%=l10n.edit_node%></span></h2>'
       
   590     + '<p><label><%=l10n.edit_title%></label><input class="Rk-Edit-Title" type="text" value="<%=node.title%>"/></p>'
       
   591     + '<p><label><%=l10n.edit_uri%></label><input class="Rk-Edit-URI" type="text" value="<%=node.uri%>"/><a class="Rk-Edit-Goto" href="<%=node.uri%>" target="_blank"></a></p>'
       
   592     + '<p><label><%=l10n.edit_description%></label><textarea class="Rk-Edit-Description"><%=node.description%></textarea></p>'
       
   593     + '<p><label><%=l10n.edit_image%></label><input class="Rk-Edit-Image" type="text" value="<%=node.image%>"/><img class="Rk-Edit-ImgPreview" src="<%=node.image%>" /></p>'
       
   594     + '<p><label><%=l10n.created_by%></label> <span class="Rk-UserColor" style="background:<%=node.created_by_color%>;"></span><%=node.created_by_title%></p>'
       
   595 );
       
   596 
       
   597 Rkns.Renderer.NodeEditor.prototype.draw = function() {
       
   598     var _model = this.node_representation.model;
       
   599     this.editor_$
       
   600         .html(this.template({
       
   601             node: {
       
   602                 title: _model.get("title"),
       
   603                 uri: _model.get("uri"),
       
   604                 description: _model.get("description"),
       
   605                 image: _model.get("image") || "",
       
   606                 created_by_color: _model.get("created_by").get("color"),
       
   607                 created_by_title: _model.get("created_by").get("title")
       
   608             },
       
   609             l10n: this.renderer.renkan.l10n
       
   610         }));
       
   611     this.redraw();
       
   612     var _this = this;
       
   613     this.editor_$.find(".Rk-CloseX").click(function() {
       
   614         _this.renderer.removeRepresentation(_this);
       
   615         paper.view.draw();
       
   616     });
       
   617     this.editor_$.find("input, textarea").bind("keyup change", function() {
       
   618         var _uri = _this.editor_$.find(".Rk-Edit-URI").val(),
       
   619             _image = _this.editor_$.find(".Rk-Edit-Image").val();
       
   620         _this.editor_$.find(".Rk-Edit-ImgPreview").attr("src", _image);
       
   621         _this.editor_$.find(".Rk-Edit-Goto").attr("href",_uri);
       
   622         var _data = {
       
   623             title: _this.editor_$.find(".Rk-Edit-Title").val(),
       
   624             description: _this.editor_$.find(".Rk-Edit-Description").val(),
       
   625             uri: _uri,
       
   626             image: _image
       
   627         }
       
   628         _model.set(_data);
       
   629         _this.redraw();
       
   630     });
       
   631     this.editor_$.find("img").load(function() {
       
   632         _this.redraw();
       
   633     })
       
   634     this.editor_$.find(".Rk-Edit-Title")[0].focus();
       
   635 }
       
   636 
       
   637 Rkns.Renderer.NodeEditor.prototype.redraw = function() {
       
   638     var _coords = this.node_representation.paper_coords;
       
   639     Rkns.Renderer.Utils.drawEditBox(_coords, this.editor_block, 250, 20, this.editor_$);
       
   640     this.editor_$.show();
       
   641     paper.view.draw();
       
   642 }
       
   643 
       
   644 Rkns.Renderer.NodeEditor.prototype.destroy = function() {
       
   645     this.editor_block.remove();
       
   646     this.editor_$.detach();
       
   647 }
       
   648 
       
   649 /* */
       
   650 
       
   651 Rkns.Renderer.EdgeEditor = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   652 
       
   653 Rkns.Renderer.EdgeEditor.prototype._init = function() {
       
   654     this.renderer.overlay_layer.activate();
       
   655     this.type = "editor";
       
   656     this.editor_block = new paper.Path();
       
   657     var _pts = Rkns._(Rkns._.range(8)).map(function() {return [0,0]});
       
   658     this.editor_block.add.apply(this.editor_block, _pts);
       
   659     this.editor_block.strokeWidth = 2;
       
   660     this.editor_block.strokeColor = "#999999";
       
   661     this.editor_block.fillColor = "#e0e0e0";
       
   662     this.editor_block.opacity = .8;
       
   663     this.editor_$ = Rkns.$('<div>')
       
   664         .appendTo(this.renderer.editor_$)
       
   665         .css({
       
   666             position: "absolute",
       
   667             opacity: .8
       
   668         })
       
   669         .hide();
       
   670 }
       
   671 
       
   672 Rkns.Renderer.EdgeEditor.prototype.template = Rkns._.template(
       
   673     '<h2><span class="Rk-CloseX">&times;</span><%=l10n.edit_edge%></span></h2>'
       
   674     + '<p><label><%=l10n.edit_title%></label><input class="Rk-Edit-Title" type="text" value="<%=edge.title%>"/></p>'
       
   675     + '<p><label><%=l10n.edit_uri%></label><input class="Rk-Edit-URI" type="text" value="<%=edge.uri%>"/><a class="Rk-Edit-Goto" href="<%=edge.uri%>" target="_blank"></a></p>'
       
   676     + '<p><label><%=l10n.edit_from%></label><span class="Rk-UserColor" style="background:<%=edge.from_created_by_color%>;"></span><%=edge.from_title%></p>'
       
   677     + '<p><label><%=l10n.edit_to%></label><span class="Rk-UserColor" style="background:<%=edge.to_created_by_color%>;"></span><%=edge.to_title%></p>'
       
   678     + '<p><label><%=l10n.created_by%> </label><span class="Rk-UserColor" style="background:<%=edge.created_by_color%>;"></span><%=edge.created_by_title%></p>'
       
   679 );
       
   680 
       
   681 Rkns.Renderer.EdgeEditor.prototype.draw = function() {
       
   682     var _model = this.edge_representation.model;
       
   683     this.editor_$
       
   684         .html(this.template({
       
   685             edge: {
       
   686                 title: _model.get("title"),
       
   687                 uri: _model.get("uri"),
       
   688                 description: _model.get("description"),
       
   689                 from_title: _model.get("from").get("title"),
       
   690                 to_title: _model.get("to").get("title"),
       
   691                 from_created_by_color: _model.get("from").get("created_by").get("color"),
       
   692                 to_created_by_color: _model.get("to").get("created_by").get("color"),
       
   693                 created_by_color: _model.get("created_by").get("color"),
       
   694                 created_by_title: _model.get("created_by").get("title")
       
   695             },
       
   696             l10n: this.renderer.renkan.l10n
       
   697         }));
       
   698     this.redraw();
       
   699     var _this = this;
       
   700     this.editor_$.find(".Rk-CloseX").click(function() {
       
   701         _this.renderer.removeRepresentation(_this);
       
   702         paper.view.draw();
       
   703     });
       
   704     this.editor_$.find("input, textarea").bind("keyup change", function() {
       
   705         _this.editor_$.find(".Rk-Edit-Goto").attr("href",_this.editor_$.find(".Rk-Edit-URI").val());
       
   706         var _data = {
       
   707             title: _this.editor_$.find(".Rk-Edit-Title").val(),
       
   708             uri: _this.editor_$.find(".Rk-Edit-URI").val()
       
   709         }
       
   710         _model.set(_data);
       
   711         _this.redraw();
       
   712     });
       
   713 }
       
   714 Rkns.Renderer.EdgeEditor.prototype.redraw = function() {
       
   715     var _coords = this.edge_representation.paper_coords;
       
   716     Rkns.Renderer.Utils.drawEditBox(_coords, this.editor_block, 250, 5, this.editor_$);
       
   717     this.editor_$.show();
       
   718     paper.view.draw();
       
   719 }
       
   720 
       
   721 Rkns.Renderer.EdgeEditor.prototype.destroy = function() {
       
   722     this.editor_block.remove();
       
   723     this.editor_$.detach();
       
   724 }
       
   725 
       
   726 /* */
       
   727 
       
   728 Rkns.Renderer.NodeEditButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   729 
       
   730 Rkns.Renderer.NodeEditButton.prototype._init = function() {
       
   731     this.renderer.overlay_layer.activate();
       
   732     this.type = "Node-edit-button";
       
   733     this.sector = Rkns.Renderer.Utils.sector(this, Rkns.Renderer._NODE_BUTTON_INNER, Rkns.Renderer._NODE_BUTTON_OUTER, - 90, 30, 1, this.renderer.renkan.static_url+'img/edit.png', this.renderer.renkan.l10n.caption_edit);
       
   734 }
       
   735 
       
   736 Rkns.Renderer.NodeEditButton.prototype.moveTo = function(_pos) {
       
   737     this.sector.moveTo(_pos);
       
   738 }
       
   739 
       
   740 Rkns.Renderer.NodeEditButton.prototype.show = function() {
       
   741     this.sector.show();
       
   742 }
       
   743 
       
   744 Rkns.Renderer.NodeEditButton.prototype.hide = function() {
       
   745     this.sector.hide();
       
   746 }
       
   747 
       
   748 Rkns.Renderer.NodeEditButton.prototype.select = function() {
       
   749     this.sector.select();
       
   750 }
       
   751 
       
   752 Rkns.Renderer.NodeEditButton.prototype.unselect = function(_newTarget) {
       
   753     this.sector.unselect();
       
   754     if (!_newTarget || (_newTarget !== this.node_representation && _newTarget.node_representation !== this.node_representation)) {
       
   755         this.node_representation.unselect();
       
   756     }
       
   757 }
       
   758 
       
   759 Rkns.Renderer.NodeEditButton.prototype.mouseup = function() {
       
   760     if (!this.renderer.is_dragging) {
       
   761         this.node_representation.openEditor();
       
   762     }
       
   763 }
       
   764 
       
   765 Rkns.Renderer.NodeEditButton.prototype.destroy = function() {
       
   766     this.sector.destroy();
       
   767 }
       
   768 
       
   769 /* */
       
   770 
       
   771 Rkns.Renderer.NodeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   772 
       
   773 Rkns.Renderer.NodeRemoveButton.prototype._init = function() {
       
   774     this.renderer.overlay_layer.activate();
       
   775     this.type = "Node-remove-button";
       
   776     this.sector = Rkns.Renderer.Utils.sector(this, Rkns.Renderer._NODE_BUTTON_INNER, Rkns.Renderer._NODE_BUTTON_OUTER, -210, -90, 1, this.renderer.renkan.static_url+'img/remove.png', this.renderer.renkan.l10n.caption_remove);
       
   777 }
       
   778 
       
   779 Rkns.Renderer.NodeRemoveButton.prototype.moveTo = function(_pos) {
       
   780     this.sector.moveTo(_pos);
       
   781 }
       
   782 
       
   783 Rkns.Renderer.NodeRemoveButton.prototype.show = function() {
       
   784     this.sector.show();
       
   785 }
       
   786 
       
   787 Rkns.Renderer.NodeRemoveButton.prototype.hide = function() {
       
   788     this.sector.hide();
       
   789 }
       
   790 
       
   791 Rkns.Renderer.NodeRemoveButton.prototype.select = function() {
       
   792     this.sector.select();
       
   793 }
       
   794 
       
   795 Rkns.Renderer.NodeRemoveButton.prototype.unselect = function(_newTarget) {
       
   796     this.sector.unselect();
       
   797     if (!_newTarget || (_newTarget !== this.node_representation && _newTarget.node_representation !== this.node_representation)) {
       
   798         this.node_representation.unselect();
       
   799     }
       
   800 }
       
   801 
       
   802 Rkns.Renderer.NodeRemoveButton.prototype.mouseup = function() {
       
   803     this.renderer.removeRepresentationsOfType("editor");
       
   804     if (confirm('Do you really wish to remove node "' + this.node_representation.model.get("title") + '"?')) {
       
   805         this.project.removeNode(this.node_representation.model);
       
   806     }
       
   807 }
       
   808 
       
   809 Rkns.Renderer.NodeRemoveButton.prototype.destroy = function() {
       
   810     this.sector.destroy();
       
   811 }
       
   812 
       
   813 /* */
       
   814 
       
   815 Rkns.Renderer.NodeLinkButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   816 
       
   817 Rkns.Renderer.NodeLinkButton.prototype._init = function() {
       
   818     this.renderer.overlay_layer.activate();
       
   819     this.type = "Node-link-button";
       
   820     this.sector = Rkns.Renderer.Utils.sector(this, Rkns.Renderer._NODE_BUTTON_INNER, Rkns.Renderer._NODE_BUTTON_OUTER, 30, 150, 1, this.renderer.renkan.static_url+'img/link.png', this.renderer.renkan.l10n.caption_link);
       
   821 }
       
   822 
       
   823 Rkns.Renderer.NodeLinkButton.prototype.moveTo = function(_pos) {
       
   824     this.sector.moveTo(_pos);
       
   825 }
       
   826 
       
   827 Rkns.Renderer.NodeLinkButton.prototype.show = function() {
       
   828     this.sector.show();
       
   829 }
       
   830 
       
   831 Rkns.Renderer.NodeLinkButton.prototype.hide = function() {
       
   832     this.sector.hide();
       
   833 }
       
   834 
       
   835 Rkns.Renderer.NodeLinkButton.prototype.select = function() {
       
   836     this.sector.select();
       
   837 }
       
   838 
       
   839 Rkns.Renderer.NodeLinkButton.prototype.unselect = function(_newTarget) {
       
   840     this.sector.unselect();
       
   841     if (!_newTarget || (_newTarget !== this.node_representation && _newTarget.node_representation !== this.node_representation)) {
       
   842         this.node_representation.unselect();
       
   843     }
       
   844 }
       
   845 
       
   846 Rkns.Renderer.NodeLinkButton.prototype.destroy = function() {
       
   847     this.sector.destroy();
       
   848 }
       
   849 
       
   850 /* */
       
   851 
       
   852 Rkns.Renderer.EdgeEditButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   853 
       
   854 Rkns.Renderer.EdgeEditButton.prototype._init = function() {
       
   855     this.renderer.overlay_layer.activate();
       
   856     this.type = "Edge-edit-button";
       
   857     this.sector = Rkns.Renderer.Utils.sector(this, Rkns.Renderer._EDGE_BUTTON_INNER, Rkns.Renderer._EDGE_BUTTON_OUTER, - 90, 90, 1, this.renderer.renkan.static_url+'img/edit.png', this.renderer.renkan.l10n.caption_edit);
       
   858 }
       
   859 
       
   860 Rkns.Renderer.EdgeEditButton.prototype.moveTo = function(_pos) {
       
   861     this.sector.moveTo(_pos);
       
   862 }
       
   863 
       
   864 Rkns.Renderer.EdgeEditButton.prototype.show = function() {
       
   865     this.sector.show();
       
   866 }
       
   867 
       
   868 Rkns.Renderer.EdgeEditButton.prototype.hide = function() {
       
   869     this.sector.hide();
       
   870 }
       
   871 
       
   872 Rkns.Renderer.EdgeEditButton.prototype.select = function() {
       
   873     this.sector.select();
       
   874 }
       
   875 
       
   876 Rkns.Renderer.EdgeEditButton.prototype.unselect = function(_newTarget) {
       
   877     this.sector.unselect();
       
   878     if (!_newTarget || (_newTarget !== this.edge_representation && _newTarget.edge_representation !== this.edge_representation)) {
       
   879         this.edge_representation.unselect();
       
   880     }
       
   881 }
       
   882 
       
   883 Rkns.Renderer.EdgeEditButton.prototype.mouseup = function() {
       
   884     if (!this.renderer.is_dragging) {
       
   885         this.edge_representation.openEditor();
       
   886     }
       
   887 }
       
   888 
       
   889 Rkns.Renderer.EdgeEditButton.prototype.destroy = function() {
       
   890     this.sector.destroy();
       
   891 }
       
   892 
       
   893 /* */
       
   894 
       
   895 Rkns.Renderer.EdgeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
       
   896 
       
   897 Rkns.Renderer.EdgeRemoveButton.prototype._init = function() {
       
   898     this.renderer.overlay_layer.activate();
       
   899     this.type = "Edge-remove-button";
       
   900     this.sector = Rkns.Renderer.Utils.sector(this, Rkns.Renderer._EDGE_BUTTON_INNER, Rkns.Renderer._EDGE_BUTTON_OUTER, - 270, -90, 1, this.renderer.renkan.static_url+'img/remove.png', this.renderer.renkan.l10n.caption_remove);
       
   901 }
       
   902 Rkns.Renderer.EdgeRemoveButton.prototype.moveTo = function(_pos) {
       
   903     this.sector.moveTo(_pos);
       
   904 }
       
   905 
       
   906 Rkns.Renderer.EdgeRemoveButton.prototype.show = function() {
       
   907     this.sector.show();
       
   908 }
       
   909 
       
   910 Rkns.Renderer.EdgeRemoveButton.prototype.hide = function() {
       
   911     this.sector.hide();
       
   912 }
       
   913 
       
   914 Rkns.Renderer.EdgeRemoveButton.prototype.select = function() {
       
   915     this.sector.select();
       
   916 }
       
   917 
       
   918 Rkns.Renderer.EdgeRemoveButton.prototype.unselect = function(_newTarget) {
       
   919     this.sector.unselect();
       
   920     if (!_newTarget || (_newTarget !== this.edge_representation && _newTarget.edge_representation !== this.edge_representation)) {
       
   921         this.edge_representation.unselect();
       
   922     }
       
   923 }
       
   924 
       
   925 Rkns.Renderer.EdgeRemoveButton.prototype.mouseup = function() {
       
   926     this.renderer.removeRepresentationsOfType("editor");
       
   927     if (confirm('Do you really wish to remove edge "' + this.edge_representation.model.get("title") + '"?')) {
       
   928         this.project.removeEdge(this.edge_representation.model);
       
   929     }
       
   930 }
       
   931 
       
   932 Rkns.Renderer.EdgeRemoveButton.prototype.destroy = function() {
       
   933     this.sector.destroy();
       
   934 }
       
   935 
       
   936 /* */
       
   937 
       
   938 Rkns.Renderer.Scene = function(_renkan) {
       
   939     this.renkan = _renkan;
       
   940     this.$ = Rkns.$(".Rk-Render");
       
   941     this.representations = [];
       
   942     this.$.html(this.template({
       
   943         l10n: _renkan.l10n
       
   944     }))
       
   945     this.canvas_$ = this.$.find(".Rk-Canvas");
       
   946     this.editor_$ = this.$.find(".Rk-Editor");
       
   947     this.notif_$ = this.$.find(".Rk-Notifications");
       
   948     paper.setup(this.canvas_$[0]);
       
   949     this.scale = 1;
       
   950     this.offset = paper.view.center;
       
   951     this.totalScroll = 0;
       
   952     this.click_target = null;
       
   953     this.selected_target = null;
       
   954     this.edge_layer = new paper.Layer();
       
   955     this.node_layer = new paper.Layer();
       
   956     this.overlay_layer = new paper.Layer();
       
   957     this.bundles = [];
       
   958     this.click_mode = false;
       
   959     var _tool = new paper.Tool(),
       
   960         _this = this;
       
   961     _tool.minDistance = Rkns.Renderer._MIN_DRAG_DISTANCE;
       
   962     _tool.onMouseMove = function(_event) {
       
   963         _this.onMouseMove(_event);
       
   964     }
       
   965     _tool.onMouseDown = function(_event) {
       
   966         _this.onMouseDown(_event);
       
   967     }
       
   968     _tool.onMouseDrag = function(_event) {
       
   969         _this.onMouseDrag(_event);
       
   970     }
       
   971     this.canvas_$.mouseup(function(_event) {
       
   972         _this.onMouseUp(_event);
       
   973     });
       
   974     this.canvas_$.mousewheel(function(_event, _delta) {
       
   975         _this.onScroll(_event, _delta);
       
   976     });
       
   977     this.canvas_$.dblclick(function(_event) {
       
   978         _this.onDoubleClick(_event);
       
   979     });
       
   980     this.canvas_$.mouseenter(function(_event) {
       
   981         _this.onMouseEnter(_event);
       
   982     });
       
   983     this.editor_$.find(".Rk-ZoomOut").click(function() {
       
   984         _this.offset = new paper.Point([
       
   985             _this.canvas_$.width(),
       
   986             _this.canvas_$.height()
       
   987         ]).multiply( .5 * ( 1 - Math.SQRT1_2 ) ).add(_this.offset.multiply( Math.SQRT1_2 ));
       
   988         _this.setScale( _this.scale * Math.SQRT1_2 );
       
   989         _this.redraw();
       
   990     });
       
   991     this.editor_$.find(".Rk-ZoomIn").click(function() {
       
   992         _this.offset = new paper.Point([
       
   993             _this.canvas_$.width(),
       
   994             _this.canvas_$.height()
       
   995         ]).multiply( .5 * ( 1 - Math.SQRT2 ) ).add(_this.offset.multiply( Math.SQRT2 ));
       
   996         _this.setScale( _this.scale * Math.SQRT2 );
       
   997         _this.redraw();
       
   998     });
       
   999     this.$.find(".Rk-CurrentUser").mouseenter(
       
  1000         function() { _this.$.find(".Rk-UserList").slideDown() }
       
  1001     );
       
  1002     this.$.find(".Rk-Users").mouseleave(
       
  1003         function() { _this.$.find(".Rk-UserList").slideUp(); }
       
  1004     );
       
  1005     this.$.find(".Rk-FullScreen-Button").click(function() {
       
  1006         var _isFull = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen,
       
  1007             _el = _this.renkan.$[0],
       
  1008             _requestMethods = ["requestFullScreen","mozRequestFullScreen","webkitRequestFullScreen"],
       
  1009             _cancelMethods = ["cancelFullScreen","mozCancelFullScreen","webkitCancelFullScreen"];
       
  1010         if (_isFull) {
       
  1011             for (var i = 0; i < _cancelMethods.length; i++) {
       
  1012                 if (typeof document[_cancelMethods[i]] === "function") {
       
  1013                     document[_cancelMethods[i]]();
       
  1014                     break;
       
  1015                 }
       
  1016             }
       
  1017         } else {
       
  1018             for (var i = 0; i < _requestMethods.length; i++) {
       
  1019                 if (typeof _el[_requestMethods[i]] === "function") {
       
  1020                     _el[_requestMethods[i]]();
       
  1021                     break;
       
  1022                 }
       
  1023             }
       
  1024         }
       
  1025     });
       
  1026     this.$.find(".Rk-AddNode-Button").click(function() {
       
  1027         if (_this.click_mode === Rkns.Renderer._CLICKMODE_ADDNODE) {
       
  1028             _this.click_mode = false;
       
  1029             _this.notif_$.hide();
       
  1030         } else {
       
  1031             _this.click_mode = Rkns.Renderer._CLICKMODE_ADDNODE;
       
  1032             _this.notif_$.html(_renkan.l10n.notif_add_node).fadeIn();
       
  1033         }
       
  1034     });
       
  1035     this.$.find(".Rk-AddEdge-Button").click(function() {
       
  1036         if (_this.click_mode === Rkns.Renderer._CLICKMODE_STARTEDGE || _this.click_mode === Rkns.Renderer._CLICKMODE_ENDEDGE) {
       
  1037             _this.click_mode = false;
       
  1038             _this.notif_$.hide();
       
  1039         } else {
       
  1040             _this.click_mode = Rkns.Renderer._CLICKMODE_STARTEDGE;
       
  1041             _this.notif_$.html(_renkan.l10n.notif_start_edge).fadeIn();
       
  1042         }
       
  1043     });
       
  1044     this.$.find(".Rk-TopBar-Button").mouseover(function() {
       
  1045         Rkns.$(this).find(".Rk-TopBar-Tooltip").show();
       
  1046     }).mouseout(function() {
       
  1047         Rkns.$(this).find(".Rk-TopBar-Tooltip").hide();
       
  1048     });
       
  1049     
       
  1050     paper.view.onResize = function(_event) {
       
  1051         _this.offset = _this.offset.add(_event.delta.divide(2));
       
  1052         _this.redraw();
       
  1053     }
       
  1054     
       
  1055     var _thRedraw = Rkns._.throttle(function() {
       
  1056         _this.redraw();
       
  1057     },50);
       
  1058     
       
  1059     this.addRepresentations("Node", this.renkan.project.get("nodes"));
       
  1060     this.addRepresentations("Edge", this.renkan.project.get("edges"));
       
  1061     this.renkan.project.get("users").each(function(_user) {
       
  1062         _this.addUser(_user);
       
  1063     })
       
  1064     
       
  1065     this.renkan.project.on("add:users", function(_user) {
       
  1066         _this.addUser(_user);
       
  1067     });
       
  1068     this.renkan.project.on("add:nodes", function(_node) {
       
  1069         _this.addRepresentation("Node", _node);
       
  1070         _thRedraw();
       
  1071     });
       
  1072     this.renkan.project.on("add:edges", function(_edge) {
       
  1073         _this.addRepresentation("Edge", _edge);
       
  1074         _thRedraw();
       
  1075     });
       
  1076     
       
  1077     this.redraw();
       
  1078 }
       
  1079 
       
  1080 Rkns.Renderer.Scene.prototype.template = Rkns._.template(
       
  1081     '<div class="Rk-TopBar"><h3 class="Rk-PadTitle"><%=l10n.untitled_project%></h3>'
       
  1082     + '<div class="Rk-Users"><div class="Rk-CurrentUser"><span class="Rk-CurrentUser-Color"></span><span class="Rk-CurrentUser-Name">&lt;unknown user&gt;</span></div><ul class="Rk-UserList"></ul></div>'
       
  1083     + '<div class="Rk-TopBar-Separator"></div><div class="Rk-TopBar-Button Rk-FullScreen-Button"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents"><%=l10n.full_screen%></div></div></div>'
       
  1084     + '<div class="Rk-TopBar-Separator"></div><div class="Rk-TopBar-Button Rk-AddNode-Button"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents"><%=l10n.add_node%></div></div></div>'
       
  1085     + '<div class="Rk-TopBar-Separator"></div><div class="Rk-TopBar-Button Rk-AddEdge-Button"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents"><%=l10n.add_edge%></div></div></div>'
       
  1086     + '<div class="Rk-TopBar-Separator"></div></div>'
       
  1087     + '<canvas class="Rk-Canvas" resize></canvas><div class="Rk-Editor"><div class="Rk-Notifications"></div>'
       
  1088     + '<div class="Rk-ZoomButtons"><div class="Rk-ZoomIn" title="<%=l10n.zoom_in%>"></div><div class="Rk-ZoomOut" title="<%=l10n.zoom_out%>"></div></div>'
       
  1089     + '</div>'
       
  1090 );
       
  1091 
       
  1092 Rkns.Renderer.Scene.prototype.addToBundles = function(_edgeRepr) {
       
  1093     var _bundle = Rkns._(this.bundles).find(function(_bundle) {
       
  1094         return ( 
       
  1095             ( _bundle.from === _edgeRepr.from_representation && _bundle.to === _edgeRepr.to_representation )
       
  1096             || ( _bundle.from === _edgeRepr.to_representation && _bundle.to === _edgeRepr.from_representation )
       
  1097         );
       
  1098     });
       
  1099     if (typeof _bundle !== "undefined") {
       
  1100         _bundle.edges.push(_edgeRepr)
       
  1101     } else {
       
  1102         _bundle = {
       
  1103             from: _edgeRepr.from_representation,
       
  1104             to: _edgeRepr.to_representation,
       
  1105             edges: [ _edgeRepr ],
       
  1106             getPosition: function(_er) {
       
  1107                 var _dir = (_er.from_representation === this.from) ? 1 : -1;
       
  1108                 return _dir * ( Rkns._(this.edges).indexOf(_er) - (this.edges.length - 1) / 2 );
       
  1109             }
       
  1110         }
       
  1111         this.bundles.push(_bundle);
       
  1112     }
       
  1113     return _bundle;
       
  1114 }
       
  1115 
       
  1116 Rkns.Renderer.Scene.prototype.setScale = function(_newScale) {
       
  1117     this.scale = _newScale;
       
  1118     this.redraw();
       
  1119 }
       
  1120 
       
  1121 Rkns.Renderer.Scene.prototype.autoScale = function() {
       
  1122     var nodes = this.renkan.project.get("nodes")
       
  1123     if (nodes.length > 1) {
       
  1124         var _xx = nodes.map(function(_node) { return _node.get("position").x }),
       
  1125             _yy = nodes.map(function(_node) { return _node.get("position").y }),
       
  1126             _minx = Math.min.apply(Math, _xx),
       
  1127             _miny = Math.min.apply(Math, _yy),
       
  1128             _maxx = Math.max.apply(Math, _xx),
       
  1129             _maxy = Math.max.apply(Math, _yy);
       
  1130         var _scale = Math.min((paper.view.size.width - 2 * Rkns.Renderer._MARGIN_X) / (_maxx - _minx), (paper.view.size.height - 2 * Rkns.Renderer._MARGIN_Y) / (_maxy - _miny));
       
  1131         this.offset = paper.view.center.subtract(new paper.Point([(_maxx + _minx) / 2, (_maxy + _miny) / 2]).multiply(_scale));
       
  1132         this.setScale(_scale);
       
  1133     }
       
  1134     if (nodes.length === 1) {
       
  1135         this.offset = paper.view.center.subtract(new paper.Point([nodes.at(0).get("position").x, nodes.at(0).get("position").y]));
       
  1136         this.setScale(1);
       
  1137     }
       
  1138 }
       
  1139 
       
  1140 Rkns.Renderer.Scene.prototype.toPaperCoords = function(_point) {
       
  1141     return _point.multiply(this.scale).add(this.offset);
       
  1142 }
       
  1143 
       
  1144 
       
  1145 Rkns.Renderer.Scene.prototype.toModelCoords = function(_point) {
       
  1146     return _point.subtract(this.offset).divide(this.scale);
       
  1147 }
       
  1148 
       
  1149 Rkns.Renderer.Scene.prototype.addRepresentation = function(_type, _model) {
       
  1150     var _repr = new Rkns.Renderer[_type](this, _model);
       
  1151     this.representations.push(_repr);
       
  1152     return _repr;
       
  1153 }
       
  1154 
       
  1155 Rkns.Renderer.Scene.prototype.addRepresentations = function(_type, _collection) {
       
  1156     var _this = this;
       
  1157     _collection.forEach(function(_model) {
       
  1158         _this.addRepresentation(_type, _model);
       
  1159     });
       
  1160 }
       
  1161 
       
  1162 Rkns.Renderer.Scene.prototype.userTemplate = Rkns._.template(
       
  1163     '<li class="Rk-User"><span class="Rk-UserColor" style="background:<%=background%>;"></span><%=name%></li>'
       
  1164 );
       
  1165 
       
  1166 Rkns.Renderer.Scene.prototype.addUser = function(_user) {
       
  1167     if (_user.get("_id") === this.renkan.current_user) {
       
  1168         this.$.find(".Rk-CurrentUser-Name").text(_user.get("title"));
       
  1169         this.$.find(".Rk-CurrentUser-Color").css("background", _user.get("color"));
       
  1170     } else {
       
  1171         this.$.find(".Rk-UserList").append(
       
  1172             Rkns.$(
       
  1173                 this.userTemplate({
       
  1174                     name: _user.get("title"),
       
  1175                     background: _user.get("color")
       
  1176                 })
       
  1177             )
       
  1178         );
       
  1179     }
       
  1180 }
       
  1181 
       
  1182 Rkns.Renderer.Scene.prototype.removeRepresentation = function(_representation) {
       
  1183     _representation.destroy();
       
  1184     this.representations = Rkns._(this.representations).reject(
       
  1185         function(_repr) {
       
  1186             return _repr == _representation
       
  1187         }
       
  1188     );
       
  1189 }
       
  1190 
       
  1191 Rkns.Renderer.Scene.prototype.getRepresentationByModel = function(_model) {
       
  1192     return Rkns._(this.representations).find(function(_repr) {
       
  1193         return _repr.model === _model;
       
  1194     });
       
  1195 }
       
  1196 
       
  1197 Rkns.Renderer.Scene.prototype.removeRepresentationsOfType = function(_type) {
       
  1198     var _representations = Rkns._(this.representations).filter(function(_repr) {
       
  1199             return _repr.type == _type;
       
  1200         }),
       
  1201         _this = this;
       
  1202     Rkns._(_representations).each(function(_repr) {
       
  1203         _this.removeRepresentation(_repr);
       
  1204     });
       
  1205 }
       
  1206 
       
  1207 Rkns.Renderer.Scene.prototype.highlightModel = function(_model) {
       
  1208     var _repr = this.getRepresentationByModel(_model);
       
  1209     if (_repr) {
       
  1210         _repr.highlight();
       
  1211     }
       
  1212 }
       
  1213 
       
  1214 Rkns.Renderer.Scene.prototype.unhighlightAll = function(_model) {
       
  1215     Rkns._(this.representations).each(function(_repr) {
       
  1216         _repr.unhighlight();
       
  1217     });
       
  1218 }
       
  1219 
       
  1220 Rkns.Renderer.Scene.prototype.redraw = function() {
       
  1221     Rkns._(this.representations).each(function(_representation) {
       
  1222         _representation.redraw();
       
  1223     });
       
  1224     paper.view.draw();
       
  1225 }
       
  1226 
       
  1227 Rkns.Renderer.Scene.prototype.addTempEdge = function(_from, _point) {
       
  1228     var _tmpEdge = this.addRepresentation("TempEdge",null);
       
  1229     _tmpEdge.end_pos = _point;
       
  1230     _tmpEdge.from_representation = _from;
       
  1231     _tmpEdge.redraw();
       
  1232     this.click_target = _tmpEdge;
       
  1233 }
       
  1234 
       
  1235 Rkns.Renderer.Scene.prototype.findTarget = function(_hitResult) {
       
  1236     if (_hitResult && typeof _hitResult.item.__representation !== "undefined") {
       
  1237         var _newTarget = _hitResult.item.__representation;
       
  1238         if (this.selected_target !== _hitResult.item.__representation) {
       
  1239             if (this.selected_target) {
       
  1240                 this.selected_target.unselect(_newTarget);
       
  1241             }
       
  1242             _newTarget.select(this.selected_target);
       
  1243             this.selected_target = _newTarget;
       
  1244         }
       
  1245     } else {
       
  1246         if (this.selected_target) {
       
  1247             this.selected_target.unselect(null);
       
  1248         }
       
  1249         this.selected_target = null;
       
  1250     }
       
  1251 }
       
  1252 
       
  1253 Rkns.Renderer.Scene.prototype.onMouseMove = function(_event) {
       
  1254     var _hitResult = paper.project.hitTest(_event.point);
       
  1255     if (this.is_dragging) {
       
  1256         if (this.click_target && typeof this.click_target.paperShift === "function") {
       
  1257             this.click_target.paperShift(_event.delta);
       
  1258         } else {
       
  1259             this.offset = this.offset.add(_event.delta);
       
  1260             this.redraw();
       
  1261         }
       
  1262     } else {
       
  1263         this.findTarget(_hitResult);
       
  1264     }
       
  1265 }
       
  1266 
       
  1267 Rkns.Renderer.Scene.prototype.onMouseDown = function(_event) {
       
  1268     if (!this.click_target || this.click_target.type !== "temp-edge") {
       
  1269         this.is_dragging = false;
       
  1270         var _hitResult = paper.project.hitTest(_event.point);
       
  1271         if (_hitResult && typeof _hitResult.item.__representation !== "undefined") {
       
  1272             this.click_target = _hitResult.item.__representation;
       
  1273             if (this.click_target.type === "Node-link-button") {
       
  1274                 this.removeRepresentationsOfType("editor");
       
  1275                 this.addTempEdge(this.click_target.node_representation, _event.point);
       
  1276             }
       
  1277         } else {
       
  1278             this.click_target = null;
       
  1279             if (this.click_mode === Rkns.Renderer._CLICKMODE_ADDNODE) {
       
  1280                 var _coords = this.toModelCoords(_event.point),
       
  1281                     _data = {
       
  1282                         id: Rkns.Utils.getUID('node'),
       
  1283                         created_by: this.renkan.current_user,
       
  1284                         position: {
       
  1285                             x: _coords.x,
       
  1286                             y: _coords.y
       
  1287                         }
       
  1288                     };
       
  1289                     _node = this.renkan.project.addNode(_data);
       
  1290                 this.getRepresentationByModel(_node).openEditor();
       
  1291             }
       
  1292         }
       
  1293     }
       
  1294     if (this.click_mode) {
       
  1295         if (this.click_mode === Rkns.Renderer._CLICKMODE_STARTEDGE && this.click_target && this.click_target.type === "Node") {
       
  1296             this.removeRepresentationsOfType("editor");
       
  1297             this.addTempEdge(this.click_target, _event.point);
       
  1298             this.click_mode = Rkns.Renderer._CLICKMODE_ENDEDGE;
       
  1299             this.notif_$.fadeOut(function() {
       
  1300                 Rkns.$(this).html(_renkan.l10n.notif_end_edge).fadeIn();
       
  1301             });
       
  1302         } else {
       
  1303             this.notif_$.hide();
       
  1304             this.click_mode = false;
       
  1305         }
       
  1306     }
       
  1307 }
       
  1308 
       
  1309 Rkns.Renderer.Scene.prototype.onMouseDrag = function(_event) {
       
  1310     this.is_dragging = true;
       
  1311     this.onMouseMove(_event);
       
  1312 }
       
  1313 
       
  1314 Rkns.Renderer.Scene.prototype.onMouseUp = function(_event) {
       
  1315     if (this.click_target) {
       
  1316         var _off = this.canvas_$.offset();
       
  1317         this.click_target.mouseup(
       
  1318             {
       
  1319                 point: new paper.Point([
       
  1320                     _event.pageX - _off.left,
       
  1321                     _event.pageY - _off.top
       
  1322                 ])
       
  1323             }
       
  1324         );
       
  1325     } else {
       
  1326         this.click_target = null;
       
  1327         this.is_dragging = false;
       
  1328     }
       
  1329 }
       
  1330 
       
  1331 Rkns.Renderer.Scene.prototype.onScroll = function(_event, _scrolldelta) {
       
  1332     this.totalScroll += _scrolldelta;
       
  1333     if (Math.abs(this.totalScroll) >= 1) {
       
  1334         var _off = this.canvas_$.offset(),
       
  1335             _delta = new paper.Point([
       
  1336                 _event.pageX - _off.left,
       
  1337                 _event.pageY - _off.top
       
  1338             ]).subtract(this.offset).multiply( Math.SQRT2 - 1 );
       
  1339         if (this.totalScroll > 0) {
       
  1340             this.offset = this.offset.subtract(_delta);
       
  1341             this.setScale( this.scale * Math.SQRT2 );
       
  1342         } else {
       
  1343             this.offset = this.offset.add(_delta.divide( Math.SQRT2 ));
       
  1344             this.setScale( this.scale * Math.SQRT1_2);
       
  1345         }
       
  1346         this.totalScroll = 0;
       
  1347         this.redraw();
       
  1348     }
       
  1349 }
       
  1350 
       
  1351 Rkns.Renderer.Scene.prototype.onDoubleClick = function(_event) {
       
  1352     var _off = this.canvas_$.offset(),
       
  1353         _point = new paper.Point([
       
  1354             _event.pageX - _off.left,
       
  1355             _event.pageY - _off.top
       
  1356         ]);
       
  1357     var _hitResult = paper.project.hitTest(_point);
       
  1358     if (!_hitResult || typeof _hitResult.item.__representation === "undefined") {
       
  1359         var _coords = this.toModelCoords(_point),
       
  1360             _data = {
       
  1361                 id: Rkns.Utils.getUID('node'),
       
  1362                 created_by: this.renkan.current_user,
       
  1363                 position: {
       
  1364                     x: _coords.x,
       
  1365                     y: _coords.y
       
  1366                 }
       
  1367             };
       
  1368             _node = this.renkan.project.addNode(_data);
       
  1369             this.getRepresentationByModel(_node).openEditor();
       
  1370     }
       
  1371     paper.view.draw();
       
  1372 }
       
  1373 
       
  1374 Rkns.Renderer.Scene.prototype.onMouseEnter = function(_event) {
       
  1375     var _newEl = this.renkan.selected_bin_item;
       
  1376     if (_newEl) {
       
  1377         var _off = this.canvas_$.offset(),
       
  1378             _point = new paper.Point([
       
  1379                 _event.pageX - _off.left,
       
  1380                 _event.pageY - _off.top
       
  1381             ]),
       
  1382             _coords = this.toModelCoords(_point),
       
  1383             _data = {
       
  1384                 id: Rkns.Utils.getUID('node'),
       
  1385                 created_by: this.renkan.current_user,
       
  1386                 uri: _newEl.uri,
       
  1387                 title: _newEl.title,
       
  1388                 description: _newEl.description,
       
  1389                 image: _newEl.image,
       
  1390                 position: {
       
  1391                     x: _coords.x,
       
  1392                     y: _coords.y
       
  1393                 }
       
  1394             };
       
  1395         var _node = this.renkan.project.addNode(_data);
       
  1396         this.renkan.selected_bin_item = null;
       
  1397         this.is_dragging = true;
       
  1398         this.click_target = this.getRepresentationByModel(_node);
       
  1399     }
       
  1400 }