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