client/js/paper-renderer.js
changeset 69 f0873867143a
parent 68 803dbeb7c919
child 70 47b3125130a2
equal deleted inserted replaced
68:803dbeb7c919 69:f0873867143a
     1 Rkns.Renderer = {
     1 Rkns.Renderer = {
     2     _MARGIN_X: 80,
     2     _MINIMAP_MARGIN_X: 20,
     3     _MARGIN_Y: 50,
     3     _MINIMAP_MARGIN_Y: 20,
     4     _MIN_DRAG_DISTANCE: 2,
     4     _MIN_DRAG_DISTANCE: 2,
     5     _NODE_SIZE_BASE: 25,
     5     _NODE_SIZE_BASE: 25,
     6     _NODE_BUTTON_WIDTH: 40,
     6     _NODE_BUTTON_WIDTH: 40,
     7     _EDGE_BUTTON_INNER: 2,
     7     _EDGE_BUTTON_INNER: 2,
     8     _EDGE_BUTTON_OUTER: 40,
     8     _EDGE_BUTTON_OUTER: 40,
    20     _CLICKMODE_ADDNODE : 1,
    20     _CLICKMODE_ADDNODE : 1,
    21     _CLICKMODE_STARTEDGE : 2,
    21     _CLICKMODE_STARTEDGE : 2,
    22     _CLICKMODE_ENDEDGE : 3,
    22     _CLICKMODE_ENDEDGE : 3,
    23     _IMAGE_MAX_KB : 500,
    23     _IMAGE_MAX_KB : 500,
    24     _NODE_SIZE_STEP: Math.LN2/4,
    24     _NODE_SIZE_STEP: Math.LN2/4,
       
    25     _MINIMAP_WIDTH: 160,
       
    26     _MINIMAP_HEIGHT: 120,
    25     _USER_PLACEHOLDER : {
    27     _USER_PLACEHOLDER : {
    26         color: "#000000",
    28         color: "#000000",
    27         title: "(unknown user)",
    29         title: "(unknown user)",
    28         get: function(attr) {
    30         get: function(attr) {
    29             return this[attr] || false;
    31             return this[attr] || false;
   104             _centerYIn = Math.sin(_centerRads) * _inR,
   106             _centerYIn = Math.sin(_centerRads) * _inR,
   105             _centerYOut = Math.sin(_centerRads) * _outR,
   107             _centerYOut = Math.sin(_centerRads) * _outR,
   106             _textX = Math.cos(_centerRads) * (_outR + 3),
   108             _textX = Math.cos(_centerRads) * (_outR + 3),
   107             _textY = Math.sin(_centerRads) * (_outR + 3),
   109             _textY = Math.sin(_centerRads) * (_outR + 3),
   108             _segments = [];
   110             _segments = [];
       
   111     	_repr.renderer.buttons_layer.activate();
   109         var _path = new paper.Path();
   112         var _path = new paper.Path();
   110         _path.add([_startXIn, _startYIn]);
   113         _path.add([_startXIn, _startYIn]);
   111         _path.arcTo([_centerXIn, _centerYIn], [_endXIn, _endYIn]);
   114         _path.arcTo([_centerXIn, _centerYIn], [_endXIn, _endYIn]);
   112         _path.lineTo([_endXOut,  _endYOut]);
   115         _path.lineTo([_endXOut,  _endYOut]);
   113         _path.arcTo([_centerXOut, _centerYOut], [_startXOut, _startYOut]);
   116         _path.arcTo([_centerXOut, _centerYOut], [_startXOut, _startYOut]);
   259         this.link_button = new Rkns.Renderer.NodeLinkButton(this.renderer, null);
   262         this.link_button = new Rkns.Renderer.NodeLinkButton(this.renderer, null);
   260         this.link_button.node_representation = this;
   263         this.link_button.node_representation = this;
   261     }
   264     }
   262     this.last_circle_radius = 1;
   265     this.last_circle_radius = 1;
   263     this.title.paragraphStyle.justification = 'center';
   266     this.title.paragraphStyle.justification = 'center';
       
   267     
       
   268     this.renderer.minimap.node_layer.activate();
       
   269     this.minimap_circle = new paper.Path.Circle([0, 0], 1);
       
   270     this.renderer.minimap.node_group.addChild(this.minimap_circle);
   264 }
   271 }
   265 
   272 
   266 Rkns.Renderer.Node.prototype.redraw = function() {
   273 Rkns.Renderer.Node.prototype.redraw = function() {
       
   274     var _model_coords = new paper.Point(this.model.get("position")),
       
   275     	_baseRadius = Rkns.Renderer._NODE_SIZE_BASE * Math.exp((this.model.get("size") || 0) * Rkns.Renderer._NODE_SIZE_STEP);
   267     if (!this.paper_coords) {
   276     if (!this.paper_coords) {
   268         var _model_coords = new paper.Point(this.model.get("position"));
       
   269         this.paper_coords = this.renderer.toPaperCoords(_model_coords);
   277         this.paper_coords = this.renderer.toPaperCoords(_model_coords);
   270     }
   278     }
   271     this.circle_radius = Rkns.Renderer._NODE_SIZE_BASE * Math.exp((this.model.get("size") || 0) * Rkns.Renderer._NODE_SIZE_STEP) * this.renderer.scale;
   279     this.circle_radius = _baseRadius * this.renderer.scale;
   272     if (this.last_circle_radius !== this.circle_radius) {
   280     if (this.last_circle_radius !== this.circle_radius) {
   273     	if (!this.renderer.renkan.read_only) {
   281     	if (!this.renderer.renkan.read_only) {
   274 	    	this.edit_button.setSectorSize();
   282 	    	this.edit_button.setSectorSize();
   275 	    	this.remove_button.setSectorSize();
   283 	    	this.remove_button.setSectorSize();
   276 	    	this.link_button.setSectorSize();
   284 	    	this.link_button.setSectorSize();
   290     }
   298     }
   291     this.last_circle_radius = this.circle_radius;
   299     this.last_circle_radius = this.circle_radius;
   292     
   300     
   293     this.title.content = this.model.get("title") || this.renderer.renkan.translate("(untitled)");
   301     this.title.content = this.model.get("title") || this.renderer.renkan.translate("(untitled)");
   294     this.title.position = this.paper_coords.add([0, this.circle_radius + 1.5 *Rkns.Renderer._NODE_FONT_SIZE]);
   302     this.title.position = this.paper_coords.add([0, this.circle_radius + 1.5 *Rkns.Renderer._NODE_FONT_SIZE]);
   295     this.circle.strokeColor = this.model.get("color") || (this.model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER).get("color");
   303     var _color = this.model.get("color") || (this.model.get("created_by") || Rkns.Renderer._USER_PLACEHOLDER).get("color");
       
   304     this.circle.strokeColor = _color;
   296     this.edit_button.moveTo(this.paper_coords);
   305     this.edit_button.moveTo(this.paper_coords);
   297     this.remove_button.moveTo(this.paper_coords);
   306     this.remove_button.moveTo(this.paper_coords);
   298     this.link_button.moveTo(this.paper_coords);
   307     this.link_button.moveTo(this.paper_coords);
   299     var _img = this.model.get("image");
   308     var _img = this.model.get("image");
   300     if (_img && _img !== this.img) {
   309     if (_img && _img !== this.img) {
   328     this.img = _img;
   337     this.img = _img;
   329     if (this.node_image && !this.img) {
   338     if (this.node_image && !this.img) {
   330         this.node_image.remove();
   339         this.node_image.remove();
   331         delete this.node_image;
   340         delete this.node_image;
   332     }
   341     }
       
   342     
       
   343     this.minimap_circle.fillColor = _color;
       
   344     var minipos = this.renderer.toMinimapCoords(_model_coords),
       
   345     	miniradius = this.renderer.minimap.scale * _baseRadius,
       
   346     	minisize = new paper.Size([miniradius, miniradius]);
       
   347     this.minimap_circle.fitBounds(minipos.subtract(minisize), minisize.multiply(2));
   333         
   348         
   334     Rkns._.each(this.project.get("edges").filter(function (ed) { return ((ed.to === this.model) || (ed.from === this.model));}), function(edge, index, list){
   349     Rkns._.each(this.project.get("edges").filter(function (ed) { return ((ed.to === this.model) || (ed.from === this.model));}), function(edge, index, list){
   335         var repr = this.renderer.getRepresentationByModel(edge);
   350         var repr = this.renderer.getRepresentationByModel(edge);
   336     	if(repr != null && typeof repr.from_representation.paper_coords !== "undefined" && typeof repr.to_representation.paper_coords !== "undefined") {
   351     	if(repr != null && typeof repr.from_representation.paper_coords !== "undefined" && typeof repr.to_representation.paper_coords !== "undefined") {
   337     		repr.redraw();
   352     		repr.redraw();
   364         }
   379         }
   365     });
   380     });
   366     if (this.renderer.renkan.read_only) {
   381     if (this.renderer.renkan.read_only) {
   367         this.openEditor();
   382         this.openEditor();
   368     }
   383     }
       
   384     this.minimap_circle.fillColor = "#ff00fc";
   369 }
   385 }
   370 
   386 
   371 Rkns.Renderer.Node.prototype.unselect = function(_newTarget) {
   387 Rkns.Renderer.Node.prototype.unselect = function(_newTarget) {
   372     if (!_newTarget || _newTarget.node_representation !== this) {
   388     if (!_newTarget || _newTarget.node_representation !== this) {
   373         this.edit_button.hide();
   389         this.edit_button.hide();
   374         this.remove_button.hide();
   390         this.remove_button.hide();
   375         this.link_button.hide();
   391         this.link_button.hide();
   376         this.circle.strokeWidth = 2;
   392         this.circle.strokeWidth = 2;
   377         Rkns.$('.Rk-Bin-Item').removeClass("selected");
   393         Rkns.$('.Rk-Bin-Item').removeClass("selected");
       
   394     	this.minimap_circle.fillColor = this.circle.strokeColor;
   378     }
   395     }
   379 }
   396 }
   380 
   397 
   381 Rkns.Renderer.Node.prototype.highlight = function() {
   398 Rkns.Renderer.Node.prototype.highlight = function() {
   382     this.circle.fillColor = "#ffff80";
   399     this.circle.fillColor = "#ffff80";
   421     this.edit_button.destroy();
   438     this.edit_button.destroy();
   422     this.remove_button.destroy();
   439     this.remove_button.destroy();
   423     this.link_button.destroy();
   440     this.link_button.destroy();
   424     this.circle.remove();
   441     this.circle.remove();
   425     this.title.remove();
   442     this.title.remove();
       
   443     this.minimap_circle.remove();
   426     if (this.node_image) {
   444     if (this.node_image) {
   427         this.node_image.remove();
   445         this.node_image.remove();
   428     }
   446     }
   429 }
   447 }
   430 
   448 
   916 /* */
   934 /* */
   917 
   935 
   918 Rkns.Renderer.NodeEditButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
   936 Rkns.Renderer.NodeEditButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
   919 
   937 
   920 Rkns.Renderer.NodeEditButton.prototype._init = function() {
   938 Rkns.Renderer.NodeEditButton.prototype._init = function() {
   921     this.renderer.buttons_layer.activate();
       
   922     this.type = "Node-edit-button";
   939     this.type = "Node-edit-button";
   923     this.lastSectorInner = 0;
   940     this.lastSectorInner = 0;
   924 }
   941 }
   925 
   942 
   926 Rkns.Renderer.NodeEditButton.prototype.setSectorSize = function() {
   943 Rkns.Renderer.NodeEditButton.prototype.setSectorSize = function() {
   970 /* */
   987 /* */
   971 
   988 
   972 Rkns.Renderer.NodeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
   989 Rkns.Renderer.NodeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
   973 
   990 
   974 Rkns.Renderer.NodeRemoveButton.prototype._init = function() {
   991 Rkns.Renderer.NodeRemoveButton.prototype._init = function() {
   975     this.renderer.buttons_layer.activate();
       
   976     this.type = "Node-remove-button";
   992     this.type = "Node-remove-button";
   977     this.lastSectorInner = 0;
   993     this.lastSectorInner = 0;
   978 }
   994 }
   979 
   995 
   980 Rkns.Renderer.NodeRemoveButton.prototype.setSectorSize = function() {
   996 Rkns.Renderer.NodeRemoveButton.prototype.setSectorSize = function() {
  1025 /* */
  1041 /* */
  1026 
  1042 
  1027 Rkns.Renderer.NodeLinkButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
  1043 Rkns.Renderer.NodeLinkButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
  1028 
  1044 
  1029 Rkns.Renderer.NodeLinkButton.prototype._init = function() {
  1045 Rkns.Renderer.NodeLinkButton.prototype._init = function() {
  1030     this.renderer.buttons_layer.activate();
       
  1031     this.type = "Node-link-button";
  1046     this.type = "Node-link-button";
  1032     this.lastSectorInner = 0;
  1047     this.lastSectorInner = 0;
  1033 }
  1048 }
  1034 
  1049 
  1035 Rkns.Renderer.NodeLinkButton.prototype.setSectorSize = function() {
  1050 Rkns.Renderer.NodeLinkButton.prototype.setSectorSize = function() {
  1073 /* */
  1088 /* */
  1074 
  1089 
  1075 Rkns.Renderer.EdgeEditButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
  1090 Rkns.Renderer.EdgeEditButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
  1076 
  1091 
  1077 Rkns.Renderer.EdgeEditButton.prototype._init = function() {
  1092 Rkns.Renderer.EdgeEditButton.prototype._init = function() {
  1078     this.renderer.buttons_layer.activate();
       
  1079     this.type = "Edge-edit-button";
  1093     this.type = "Edge-edit-button";
  1080     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.translate("Edit"));
  1094     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.translate("Edit"));
  1081 }
  1095 }
  1082 
  1096 
  1083 Rkns.Renderer.EdgeEditButton.prototype.moveTo = function(_pos) {
  1097 Rkns.Renderer.EdgeEditButton.prototype.moveTo = function(_pos) {
  1116 /* */
  1130 /* */
  1117 
  1131 
  1118 Rkns.Renderer.EdgeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
  1132 Rkns.Renderer.EdgeRemoveButton = Rkns.Utils.inherit(Rkns.Renderer._BaseRepresentation);
  1119 
  1133 
  1120 Rkns.Renderer.EdgeRemoveButton.prototype._init = function() {
  1134 Rkns.Renderer.EdgeRemoveButton.prototype._init = function() {
  1121     this.renderer.buttons_layer.activate();
       
  1122     this.type = "Edge-remove-button";
  1135     this.type = "Edge-remove-button";
  1123     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.translate("Remove"));
  1136     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.translate("Remove"));
  1124 }
  1137 }
  1125 Rkns.Renderer.EdgeRemoveButton.prototype.moveTo = function(_pos) {
  1138 Rkns.Renderer.EdgeRemoveButton.prototype.moveTo = function(_pos) {
  1126     this.sector.moveTo(_pos);
  1139     this.sector.moveTo(_pos);
  1173     this.click_target = null;
  1186     this.click_target = null;
  1174     this.selected_target = null;
  1187     this.selected_target = null;
  1175     this.edge_layer = new paper.Layer();
  1188     this.edge_layer = new paper.Layer();
  1176     this.node_layer = new paper.Layer();
  1189     this.node_layer = new paper.Layer();
  1177     this.buttons_layer = new paper.Layer();
  1190     this.buttons_layer = new paper.Layer();
       
  1191     
       
  1192     this.minimap = {
       
  1193     	background_layer: new paper.Layer(),
       
  1194     	node_layer: new paper.Layer(),
       
  1195     	node_group: new paper.Group(),
       
  1196     	size: new paper.Size( Rkns.Renderer._MINIMAP_WIDTH, Rkns.Renderer._MINIMAP_HEIGHT )
       
  1197     }
       
  1198     
       
  1199     this.minimap.background_layer.activate();
       
  1200     this.minimap.topleft = paper.view.bounds.bottomRight.subtract(this.minimap.size);
       
  1201     this.minimap.rectangle = new paper.Path.Rectangle(this.minimap.topleft.subtract([2,2]), this.minimap.size.add([4,4]));
       
  1202     this.minimap.rectangle.fillColor = '#ffffff';
       
  1203     this.minimap.rectangle.strokeColor = '#cccccc';
       
  1204     this.minimap.rectangle.strokeWidth = 4;
       
  1205     this.minimap.offset = new paper.Point(this.minimap.size.divide(2));
       
  1206     this.minimap.scale = .25;
       
  1207     
       
  1208     this.node_layer.activate();
       
  1209     this.minimap.cliprectangle = new paper.Path.Rectangle(this.minimap.topleft, this.minimap.size);
       
  1210     this.minimap.node_group.addChild(this.minimap.cliprectangle);
       
  1211     this.minimap.node_group.clipped = true;
       
  1212     this.minimap.miniframe = new paper.Path.Rectangle(this.minimap.topleft, this.minimap.size);
       
  1213     this.minimap.node_group.addChild(this.minimap.miniframe);
       
  1214     this.minimap.miniframe.fillColor = '#f0f0ff';
       
  1215     this.minimap.miniframe.strokeColor = '#8080ff';
       
  1216     this.minimap.miniframe.strokeWidth = 2;
       
  1217     
  1178     this.bundles = [];
  1218     this.bundles = [];
  1179     this.click_mode = false;
  1219     this.click_mode = false;
  1180     var _tool = new paper.Tool(),
  1220     var _tool = new paper.Tool(),
  1181         _this = this;
  1221         _this = this;
  1182     _tool.minDistance = Rkns.Renderer._MIN_DRAG_DISTANCE;
  1222     _tool.minDistance = Rkns.Renderer._MIN_DRAG_DISTANCE;
  1203     	_event.preventDefault();
  1243     	_event.preventDefault();
  1204     })
  1244     })
  1205     this.canvas_$.on("drop", function(_event) {
  1245     this.canvas_$.on("drop", function(_event) {
  1206     	_event.stopPropagation();
  1246     	_event.stopPropagation();
  1207     	_event.preventDefault();
  1247     	_event.preventDefault();
       
  1248     	if (_this.renkan.read_only) {
       
  1249     		return;
       
  1250     	}
  1208     	var res = {}
  1251     	var res = {}
  1209     	Rkns._(_event.originalEvent.dataTransfer.types).each(function(t) {
  1252     	Rkns._(_event.originalEvent.dataTransfer.types).each(function(t) {
  1210     		return res[t] = _event.originalEvent.dataTransfer.getData(t);
  1253     		return res[t] = _event.originalEvent.dataTransfer.getData(t);
  1211     	});
  1254     	});
  1212     	var newNode = {};
  1255     	var newNode = {};
       
  1256     	if (res["text/x-iri-source-uri"]) {
       
  1257     		newNode.uri = res["text/x-iri-source-uri"];
       
  1258     	}
  1213     	if (res["text/plain"]) {
  1259     	if (res["text/plain"]) {
  1214     		newNode.description = res["text/plain"].replace(/[\s\n]+/gm,' ').trim();
  1260     		newNode.description = res["text/plain"].replace(/[\s\n]+/gm,' ').trim();
  1215     	}
  1261     	}
  1216     	if (res["text/html"]) {
  1262     	if (res["text/html"]) {
  1217     		var snippet = Rkns.$('<div>').html(res["text/html"]);
  1263     		var snippet = Rkns.$('<div>').html(res["text/html"]);
  1218     		newNode.image = snippet.find("img").attr("src") || '';
  1264     		newNode.image = snippet.find("img").attr("src") || '';
  1219     		newNode.uri = snippet.find("a").attr("href");
  1265     		newNode.uri = snippet.find("a").attr("href") || newNode.uri;
  1220     		newNode.title = snippet.find("[title]").attr("title");
  1266     		newNode.title = snippet.find("[title]").attr("title");
  1221     	}
  1267     	}
  1222     	if (res["text/uri-list"]) {
  1268     	if (res["text/uri-list"]) {
  1223     		newNode.uri = res["text/uri-list"];
  1269     		newNode.uri = res["text/uri-list"];
  1224     	}
  1270     	}
  1225     	if (res["text/x-moz-url"]) {
  1271     	if (res["text/x-moz-url"] && !newNode.title) {
  1226     		newNode.title = (res["text/x-moz-url"].split("\n")[1] || "").trim();
  1272     		newNode.title = (res["text/x-moz-url"].split("\n")[1] || "").trim();
  1227     		if (newNode.title === newNode.uri) {
  1273     		if (newNode.title === newNode.uri) {
  1228     			newNode.title = "";
  1274     			newNode.title = false;
  1229     		}
  1275     		}
       
  1276     	}
       
  1277     	if (res["text/x-iri-source-title"] && !newNode.title) {
       
  1278     		newNode.title = res["text/x-iri-source-title"];
  1230     	}
  1279     	}
  1231     	var fields = ["title", "description", "uri", "image"];
  1280     	var fields = ["title", "description", "uri", "image"];
  1232     	for (var i = 0; i < fields.length; i++) {
  1281     	for (var i = 0; i < fields.length; i++) {
  1233     		var f = fields[i];
  1282     		var f = fields[i];
  1234     		if (res["text/x-iri-" + f]) {
  1283     		if (res["text/x-iri-" + f]) {
  1305         if (_this.click_mode === Rkns.Renderer._CLICKMODE_ADDNODE) {
  1354         if (_this.click_mode === Rkns.Renderer._CLICKMODE_ADDNODE) {
  1306             _this.click_mode = false;
  1355             _this.click_mode = false;
  1307             _this.notif_$.hide();
  1356             _this.notif_$.hide();
  1308         } else {
  1357         } else {
  1309             _this.click_mode = Rkns.Renderer._CLICKMODE_ADDNODE;
  1358             _this.click_mode = Rkns.Renderer._CLICKMODE_ADDNODE;
  1310             _this.notif_$.html(_renkan.translate("Click on the background canvas to add a node")).fadeIn();
  1359             _this.notif_$.text(_renkan.translate("Click on the background canvas to add a node")).fadeIn();
  1311         }
  1360         }
  1312     });
  1361     });
  1313     this.$.find(".Rk-AddEdge-Button").click(function() {
  1362     this.$.find(".Rk-AddEdge-Button").click(function() {
  1314         if (_this.click_mode === Rkns.Renderer._CLICKMODE_STARTEDGE || _this.click_mode === Rkns.Renderer._CLICKMODE_ENDEDGE) {
  1363         if (_this.click_mode === Rkns.Renderer._CLICKMODE_STARTEDGE || _this.click_mode === Rkns.Renderer._CLICKMODE_ENDEDGE) {
  1315             _this.click_mode = false;
  1364             _this.click_mode = false;
  1316             _this.notif_$.hide();
  1365             _this.notif_$.hide();
  1317         } else {
  1366         } else {
  1318             _this.click_mode = Rkns.Renderer._CLICKMODE_STARTEDGE;
  1367             _this.click_mode = Rkns.Renderer._CLICKMODE_STARTEDGE;
  1319             _this.notif_$.html(_renkan.translate("Click on a first node to start the edge")).fadeIn();
  1368             _this.notif_$.text(_renkan.translate("Click on a first node to start the edge")).fadeIn();
  1320         }
  1369         }
       
  1370     });
       
  1371     this.$.find(".Rk-Bookmarklet-Button").click(function(){
       
  1372     	_this.notif_$
       
  1373     		.text(_renkan.translate("Drag this bookmarklet to your bookmark bar. When on a third-party website, click it to enable drag-and-drop from the website to Renkan."))
       
  1374     		.fadeIn()
       
  1375     		.delay(5000)
       
  1376     		.fadeOut();
       
  1377 		return false;
  1321     });
  1378     });
  1322     this.$.find(".Rk-TopBar-Button").mouseover(function() {
  1379     this.$.find(".Rk-TopBar-Button").mouseover(function() {
  1323         Rkns.$(this).find(".Rk-TopBar-Tooltip").show();
  1380         Rkns.$(this).find(".Rk-TopBar-Tooltip").show();
  1324     }).mouseout(function() {
  1381     }).mouseout(function() {
  1325         Rkns.$(this).find(".Rk-TopBar-Tooltip").hide();
  1382         Rkns.$(this).find(".Rk-TopBar-Tooltip").hide();
  1326     });
  1383     });
  1327     
  1384     
  1328     paper.view.onResize = function(_event) {
  1385     paper.view.onResize = function(_event) {
  1329         _this.offset = _this.offset.add(_event.delta.divide(2));
  1386         _this.offset = _this.offset.add(_event.delta.divide(2));
  1330         _this.resetCoords();
  1387         _this.resetCoords();
       
  1388         _this.minimap.topleft = paper.view.bounds.bottomRight.subtract(_this.minimap.size)
       
  1389         _this.minimap.rectangle.fitBounds(_this.minimap.topleft.subtract([2,2]), _this.minimap.size.add([4,4]));
       
  1390         _this.minimap.cliprectangle.fitBounds(_this.minimap.topleft, _this.minimap.size);
  1331         _this.redraw();
  1391         _this.redraw();
  1332     }
  1392     }
  1333     
  1393     
  1334     var _thRedraw = Rkns._.throttle(function() {
  1394     var _thRedraw = Rkns._.throttle(function() {
  1335         _this.redraw();
  1395         _this.redraw();
  1370             el.text(_title);
  1430             el.text(_title);
  1371         }
  1431         }
  1372     });
  1432     });
  1373     
  1433     
  1374     this.redraw();
  1434     this.redraw();
       
  1435     window.setInterval(function() {
       
  1436     	_this.rescaleMinimap()
       
  1437     }, 2000);
  1375 }
  1438 }
  1376 
  1439 
  1377 Rkns.Renderer.Scene.prototype.template = Rkns._.template(
  1440 Rkns.Renderer.Scene.prototype.template = Rkns._.template(
  1378     '<div class="Rk-TopBar"><% if (read_only) { %><h2 class="Rk-PadTitle"><%- project.get("title") || translate("Untitled project")%></h2>'
  1441     '<div class="Rk-TopBar"><% if (read_only) { %><h2 class="Rk-PadTitle"><%- project.get("title") || translate("Untitled project")%></h2>'
  1379     + '<% } else { %><input type="text" class="Rk-PadTitle" value="<%- project.get("title") || "" %>" placeholder="<%-translate("Untitled project")%>" /><% } %>'
  1442     + '<% } else { %><input type="text" class="Rk-PadTitle" value="<%- project.get("title") || "" %>" placeholder="<%-translate("Untitled project")%>" /><% } %>'
  1381     + '<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"><%-translate("Full Screen")%></div></div></div>'
  1444     + '<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"><%-translate("Full Screen")%></div></div></div>'
  1382     + '<% if (!read_only) { %>'
  1445     + '<% if (!read_only) { %>'
  1383     + '<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"><%-translate("Add Node")%></div></div></div>'
  1446     + '<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"><%-translate("Add Node")%></div></div></div>'
  1384     + '<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"><%-translate("Add Edge")%></div></div></div>'
  1447     + '<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"><%-translate("Add Edge")%></div></div></div>'
  1385     + '<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"><%-translate("Archive Project")%></div></div></div>'
  1448     + '<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"><%-translate("Archive Project")%></div></div></div>'
       
  1449     + '<div class="Rk-TopBar-Separator"></div><a class="Rk-TopBar-Button Rk-Bookmarklet-Button" href="javascript:(function(){a=document;'
       
  1450     + 'b=function(c){d=a.createElement(\'script\');d.type=\'text/javascript\';d.src=c;a.getElementsByTagName(\'head\')[0].appendChild(d);};'
       
  1451     + 'b(\'<%- bookmarklet_url %>\');})();"><div class="Rk-TopBar-Tooltip"><div class="Rk-TopBar-Tooltip-Tip"></div><div class="Rk-TopBar-Tooltip-Contents">'
       
  1452     + '<%-translate("Renkan \'Drag and Add\' bookmarklet")%></div></div></a>'
  1386     + '<div class="Rk-TopBar-Separator"></div></div>'
  1453     + '<div class="Rk-TopBar-Separator"></div></div>'
  1387     + '<% } %>'
  1454     + '<% } %>'
  1388     + '<canvas class="Rk-Canvas" resize></canvas><div class="Rk-Editor"><div class="Rk-Notifications"></div>'
  1455     + '<canvas class="Rk-Canvas" resize></canvas><div class="Rk-Editor"><div class="Rk-Notifications"></div>'
  1389     + '<div class="Rk-ZoomButtons"><div class="Rk-ZoomIn" title="<%-translate("Zoom In")%>"></div><div class="Rk-ZoomOut" title="<%-translate("Zoom Out")%>"></div></div>'
  1456     + '<div class="Rk-ZoomButtons"><div class="Rk-ZoomIn" title="<%-translate("Zoom In")%>"></div><div class="Rk-ZoomOut" title="<%-translate("Zoom Out")%>"></div></div>'
  1390     + '</div>'
  1457     + '</div>'
  1417 Rkns.Renderer.Scene.prototype.setScale = function(_newScale) {
  1484 Rkns.Renderer.Scene.prototype.setScale = function(_newScale) {
  1418     this.scale = _newScale;
  1485     this.scale = _newScale;
  1419     this.resetCoords();
  1486     this.resetCoords();
  1420     this.redraw();
  1487     this.redraw();
  1421 }
  1488 }
  1422 /*
  1489 
  1423 Rkns.Renderer.Scene.prototype.autoScale = function() {
  1490 Rkns.Renderer.Scene.prototype.redrawMiniframe = function() {
       
  1491 	var topleft = this.toMinimapCoords(this.toModelCoords(new paper.Point([0,0]))),
       
  1492 		bottomright = this.toMinimapCoords(this.toModelCoords(paper.view.bounds.bottomRight));
       
  1493 	this.minimap.miniframe.fitBounds(topleft, bottomright);
       
  1494 }
       
  1495 
       
  1496 Rkns.Renderer.Scene.prototype.rescaleMinimap = function() {
  1424     var nodes = this.renkan.project.get("nodes")
  1497     var nodes = this.renkan.project.get("nodes")
  1425     if (nodes.length > 1) {
  1498     if (nodes.length > 1) {
  1426         var _xx = nodes.map(function(_node) { return _node.get("position").x }),
  1499         var _xx = nodes.map(function(_node) { return _node.get("position").x }),
  1427             _yy = nodes.map(function(_node) { return _node.get("position").y }),
  1500             _yy = nodes.map(function(_node) { return _node.get("position").y }),
  1428             _minx = Math.min.apply(Math, _xx),
  1501             _minx = Math.min.apply(Math, _xx),
  1429             _miny = Math.min.apply(Math, _yy),
  1502             _miny = Math.min.apply(Math, _yy),
  1430             _maxx = Math.max.apply(Math, _xx),
  1503             _maxx = Math.max.apply(Math, _xx),
  1431             _maxy = Math.max.apply(Math, _yy);
  1504             _maxy = Math.max.apply(Math, _yy);
  1432         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));
  1505         var _scale = Math.min(this.scale * .8 * Rkns.Renderer._MINIMAP_WIDTH / paper.view.bounds.width, this.scale * .8 * Rkns.Renderer._MINIMAP_HEIGHT / paper.view.bounds.height, (Rkns.Renderer._MINIMAP_WIDTH - 2 * Rkns.Renderer._MINIMAP_MARGIN_X) / (_maxx - _minx), (Rkns.Renderer._MINIMAP_HEIGHT - 2 * Rkns.Renderer._MINIMAP_MARGIN_Y) / (_maxy - _miny));
  1433         this.offset = paper.view.center.subtract(new paper.Point([(_maxx + _minx) / 2, (_maxy + _miny) / 2]).multiply(_scale));
  1506         this.minimap.offset = this.minimap.size.divide(2).subtract(new paper.Point([(_maxx + _minx) / 2, (_maxy + _miny) / 2]).multiply(_scale));
  1434         this.setScale(_scale);
  1507         this.minimap.scale = _scale;
  1435     }
  1508     }
  1436     if (nodes.length === 1) {
  1509     if (nodes.length === 1) {
  1437         this.offset = paper.view.center.subtract(new paper.Point([nodes.at(0).get("position").x, nodes.at(0).get("position").y]));
  1510         this.minimap.offset = this.minimap.size.divide(2).subtract(new paper.Point([nodes.at(0).get("position").x, nodes.at(0).get("position").y]));
  1438         this.setScale(1);
  1511         this.minimap.scale = .25;
  1439     }
  1512     }
  1440 }
  1513     this.redraw();
  1441 */
  1514 }
       
  1515 
  1442 Rkns.Renderer.Scene.prototype.resetCoords = function(_point) {
  1516 Rkns.Renderer.Scene.prototype.resetCoords = function(_point) {
  1443     _(this.representations).each(function(r) {
  1517     _(this.representations).each(function(r) {
  1444         r.resetCoords();
  1518         r.resetCoords();
  1445     });
  1519     });
  1446 }
  1520 }
  1447 
  1521 
  1448 Rkns.Renderer.Scene.prototype.toPaperCoords = function(_point) {
  1522 Rkns.Renderer.Scene.prototype.toPaperCoords = function(_point) {
  1449     return _point.multiply(this.scale).add(this.offset);
  1523     return _point.multiply(this.scale).add(this.offset);
  1450 }
  1524 }
  1451 
  1525 
       
  1526 Rkns.Renderer.Scene.prototype.toMinimapCoords = function(_point) {
       
  1527     return _point.multiply(this.minimap.scale).add(this.minimap.offset).add(this.minimap.topleft);
       
  1528 }
  1452 
  1529 
  1453 Rkns.Renderer.Scene.prototype.toModelCoords = function(_point) {
  1530 Rkns.Renderer.Scene.prototype.toModelCoords = function(_point) {
  1454     return _point.subtract(this.offset).divide(this.scale);
  1531     return _point.subtract(this.offset).divide(this.scale);
  1455 }
  1532 }
  1456 
  1533 
  1527 
  1604 
  1528 Rkns.Renderer.Scene.prototype.redraw = function() {
  1605 Rkns.Renderer.Scene.prototype.redraw = function() {
  1529     Rkns._(this.representations).each(function(_representation) {
  1606     Rkns._(this.representations).each(function(_representation) {
  1530         _representation.redraw();
  1607         _representation.redraw();
  1531     });
  1608     });
       
  1609     this.redrawMiniframe();
  1532     paper.view.draw();
  1610     paper.view.draw();
  1533 }
  1611 }
  1534 
  1612 
  1535 Rkns.Renderer.Scene.prototype.addTempEdge = function(_from, _point) {
  1613 Rkns.Renderer.Scene.prototype.addTempEdge = function(_from, _point) {
  1536     var _tmpEdge = this.addRepresentation("TempEdge",null);
  1614     var _tmpEdge = this.addRepresentation("TempEdge",null);