Added drag-and-drop and image from local drive
authorveltr
Tue, 26 Feb 2013 17:04:24 +0100
changeset 66 9b459e41e2df
parent 65 3389e6c46936
child 67 d341117f9370
Added drag-and-drop and image from local drive
client/css/renkan.css
client/img/image-placeholder.png
client/js/i18n.js
client/js/ldtjson-bin.js
client/js/main.js
client/js/paper-renderer.js
client/js/twitter-bin.js
client/js/wikipedia-bin.js
client/render-test.html
--- a/client/css/renkan.css	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/css/renkan.css	Tue Feb 26 17:04:24 2013 +0100
@@ -202,18 +202,18 @@
 }
 
 .Rk-Editor p, .Rk-Editor-p {
-    margin: 5px 0; font-size: 12px;
+    margin: 5px 0; font-size: 12px; clear: both;
 }
 
-.Rk-Editor label {
-    float: left; width: 70px;
+.Rk-Editor-Label {
+    float: left; width: 80px;
 }
 
 a.Rk-Edit-Goto {
     display: block; float: right; width: 18px; height: 17px; margin: 1px 0; border: none; background: url(../img/goto.png);
 }
 
-.Rk-Edit-Title, .Rk-Edit-URI, .Rk-Edit-Image {
+.Rk-Edit-Title, .Rk-Edit-URI, .Rk-Edit-Image, .Rk-Edit-Image-File {
     font-size: 12px; width: 225px;
 }
 
@@ -222,7 +222,7 @@
 }
 
 .Rk-Edit-ImgPreview {
-    border: 1px solid #666; margin: 5px 0; min-height: 10px; display: block;
+    border: 1px solid #666; margin: 5px auto; display: block;
     max-width: 100% !important; max-height: 100px !important;
 }
 
Binary file client/img/image-placeholder.png has changed
--- a/client/js/i18n.js	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/js/i18n.js	Tue Feb 26 17:04:24 2013 +0100
@@ -1,64 +1,43 @@
 Rkns.i18n = {
-    en: {
-        edit_node: "Edit Node",
-        edit_edge: "Edit Edge",
-        edit_title: "Title:",
-        edit_uri: "URI:",
-        edit_description: "Description:",
-        edit_from: "From:",
-        edit_to: "To:",
-        edit_image: "Image:",
-        full_screen: "Full Screen",
-        add_node: "Add Node",
-        add_edge: "Add Edge",
-        save_project: "Archive Project",
-        created_by: "Created by:",
-        zoom_in: "Zoom In",
-        zoom_out: "Zoom Out",
-        caption_edit: "Edit",
-        caption_remove: "Remove",
-        caption_link: "Link to another node",
-        notif_add_node: "Click on the background canvas to add a node",
-        notif_start_edge: "Click on a first node to start the edge",
-        notif_end_edge: "Click on a second node to complete the edge",
-        search_on_twitter: "Twitter",
-        wiki_fr: "French Wikipedia",
-        wiki_en: "English Wikipedia",
-        wiki_ja: "Japanese Wikipedia",
-        wiki_: "Wikipedia (Other Language)",
-        untitled_project: "Untitled project",
-        search_ldt: "Lignes de Temps",
-        loading_bin: "Loading, please wait"
-    },
     fr: {
-        edit_node: "Édition d’un nœud",
-        edit_edge: "Édition d’un lien",
-        edit_title: "Titre :",
-        edit_uri: "URI :",
-        edit_description: "Description :",
-        edit_from: "De :",
-        edit_to: "Vers :",
-        edit_image: "Image :",
-        full_screen: "Mode plein écran",
-        add_node: "Ajouter un nœud",
-        add_edge: "Ajouter un lien",
-        save_project: "Archiver le projet",
-        created_by: "Créé par :",
-        zoom_in: "Agrandir l’échelle",
-        zoom_out: "Rapetisser l’échelle",
-        caption_edit: "Éditer",
-        caption_remove: "Supprimer",
-        caption_link: "Créer un lien",
-        notif_add_node: "Cliquer sur le fond du graphe pour rajouter un nœud",
-        notif_start_edge: "Cliquer sur un premier nœud pour commencer le lien",
-        notif_end_edge: "Cliquer sur un second nœud pour terminer le lien",
-        search_on_twitter: "Twitter",
-        wiki_fr: "Wikipedia en Français",
-        wiki_en: "Wikipedia en Anglais",
-        wiki_ja: "Wikipedia en Japonais",
-        wiki_: "Wikipedia (Autre langue)",
-        untitled_project: "Projet sans titre",
-        search_ldt: "Lignes de Temps",
-        loading_bin: "Chargement en cours, merci de patienter"
+        "Edit Node": "Édition d’un nœud",
+        "Edit Edge": "Édition d’un lien",
+        "Title:": "Titre :",
+        "URI:": "URI :",
+        "Description:": "Description :",
+        "From:": "De :",
+        "To:": "Vers :",
+        "Image": "Image",
+        "Image URL:": "URL d'Image",
+        "Choose Image File:": "Choisir un fichier image",
+        "Full Screen": "Mode plein écran",
+        "Add Node": "Ajouter un nœud",
+        "Add Edge": "Ajouter un lien",
+        "Archive Project": "Archiver le projet",
+        "Created by:": "Créé par :",
+        "Zoom In": "Agrandir l’échelle",
+        "Zoom Out": "Rapetisser l’échelle",
+        "Edit": "Éditer",
+        "Remove": "Supprimer",
+        "Link to another node": "Créer un lien",
+        "Click on the background canvas to add a node": "Cliquer sur le fond du graphe pour rajouter un nœud",
+        "Click on a first node to start the edge": "Cliquer sur un premier nœud pour commencer le lien",
+        "Click on a second node to complete the edge": "Cliquer sur un second nœud pour terminer le lien",
+        "Twitter": "Twitter",
+        "Wikipedia": "Wikipédia",
+        "Wikipedia in ": "Wikipédia en ",
+        "French": "Français",
+        "English": "Anglais",
+        "Japanese": "Japonais",
+        "Untitled project": "Projet sans titre",
+        "Lignes de Temps": "Lignes de Temps",
+        "Loading, please wait": "Chargement en cours, merci de patienter",
+        "Edge color:": "Couleur :",
+        "Node color:": "Couleur :",
+        "Do you really wish to remove node ": "Voulez-vous réellement supprimer le nœud ",
+        "Do you really wish to remove edge ": "Voulez-vous réellement supprimer le lien ",
+        "This file is not an image": "Ce fichier n'est pas une image",
+        "Image size must be under ": "L'image doit peser moins de ",
+        "KB": "ko"
     }
 }
--- a/client/js/ldtjson-bin.js	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/js/ldtjson-bin.js	Tue Feb 26 17:04:24 2013 +0100
@@ -135,7 +135,7 @@
 }
 
 Rkns.Ldt.Search.prototype.getSearchTitle = function() {
-    return this.renkan.l10n.search_ldt;
+    return this.renkan.translate("Lignes de Temps");
 }
 
 Rkns.Ldt.Search.prototype.search = function(_q) {
--- a/client/js/main.js	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/js/main.js	Tue Feb 26 17:04:24 2013 +0100
@@ -67,7 +67,7 @@
         this.main_$ = Rkns.$('<div>')
             .addClass("Rk-Bin-Main")
             .appendTo(this.$)
-            .html('<h4 class="Rk-Bin-Loading">' + _renkan.l10n.loading_bin + '</h4>');
+            .html('<h4 class="Rk-Bin-Loading">' + _renkan.translate("Loading, please wait") + '</h4>');
         this.title_$.html(_opts.title || '(new bin)');
         this.renkan.resizeBins();
         
@@ -112,7 +112,9 @@
     this.language = _opts.language;
     this.static_url = _opts.static_url;
     this.read_only = _opts.read_only;
-    this.l10n = Rkns.i18n[_opts.language];
+    this.translate = function(_text) {
+    	return (Rkns.i18n[_opts.language] || Rkns.i18n[_opts.language.substr(0,2)] || {})[_text] || _text;
+    }
     if (typeof _opts.user_id !== "undefined") {
         this.current_user = _opts.user_id;
     }
--- a/client/js/paper-renderer.js	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/js/paper-renderer.js	Tue Feb 26 17:04:24 2013 +0100
@@ -21,6 +21,7 @@
     _CLICKMODE_ADDNODE : 1,
     _CLICKMODE_STARTEDGE : 2,
     _CLICKMODE_ENDEDGE : 3,
+    _IMAGE_MAX_KB : 500,
     _USER_PLACEHOLDER : {
         color: "#000000",
         title: "(unknown user)",
@@ -645,21 +646,23 @@
 }
 
 Rkns.Renderer.NodeEditor.prototype.template = Rkns._.template(
-    '<h2><span class="Rk-CloseX">&times;</span><%=l10n.edit_node%></span></h2>'
-    + '<p><label><%=l10n.edit_title%></label><input class="Rk-Edit-Title" type="text" value="<%=node.title%>"/></p>'
-    + '<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>'
-    + '<p><label><%=l10n.edit_description%></label><textarea class="Rk-Edit-Description"><%=node.description%></textarea></p>'
-    + '<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">'
+    '<h2><span class="Rk-CloseX">&times;</span><%-translate("Edit Node")%></span></h2>'
+    + '<p><label><%-translate("Title:")%></label><input class="Rk-Edit-Title" type="text" value="<%=node.title%>"/></p>'
+    + '<p><label><%-translate("URI:")%></label><input class="Rk-Edit-URI" type="text" value="<%=node.uri%>"/><a class="Rk-Edit-Goto" href="<%=node.uri%>" target="_blank"></a></p>'
+    + '<p><label><%-translate("Description:")%></label><textarea class="Rk-Edit-Description"><%=node.description%></textarea></p>'
+    + '<div class="Rk-Editor-p"><span class="Rk-Editor-Label"><%-translate("Node color:")%></span><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">'
     + '<% 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>'
-    + '<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>'
-    + '<p><label><%=l10n.created_by%></label> <span class="Rk-UserColor" style="background:<%=node.created_by_color%>;"></span><%=node.created_by_title%></p>'
+    + '<img class="Rk-Edit-ImgPreview" src="<%=node.image || node.image_placeholder%>" />'
+    + '<p><label><%-translate("Image URL:")%></label><input class="Rk-Edit-Image" type="text" value="<%=node.image%>"/></p>'
+    + '<p><label><%-translate("Choose Image File:")%></label><input class="Rk-Edit-Image-File" type="file"/></p>'    
+    + '<p><span class="Rk-Editor-Label"><%-translate("Created by:")%></span> <span class="Rk-UserColor" style="background:<%=node.created_by_color%>;"></span><%=node.created_by_title%></p>'
 );
 
 Rkns.Renderer.NodeEditor.prototype.readOnlyTemplate = Rkns._.template(
     '<h2><span class="Rk-CloseX">&times;</span><span class="Rk-UserColor" style="background:<%=node.color%>;"></span><%-node.title%></span></h2>'
     + '<p><a href="<%-node.uri%>" target="_blank"><%-node.uri%></a></p>'
     + '<p><%-node.description%></p>'
-    + '<p><label><%=l10n.created_by%> </label><span class="Rk-UserColor" style="background:<%=node.created_by_color%>;"></span><%=node.created_by_title%></p>'
+    + '<p><span class="Rk-Editor-Label"><%-translate("Created by:")%> </span><span class="Rk-UserColor" style="background:<%=node.created_by_color%>;"></span><%=node.created_by_title%></p>'
 );
 
 Rkns.Renderer.NodeEditor.prototype.draw = function() {
@@ -673,11 +676,12 @@
                 uri: _model.get("uri"),
                 description: _model.get("description"),
                 image: _model.get("image") || "",
+                image_placeholder: this.renderer.renkan.static_url + "img/image-placeholder.png",
                 color: _model.get("color") || _created_by.get("color"),
                 created_by_color: _created_by.get("color"),
                 created_by_title: _created_by.get("title")
             },
-            l10n: this.renderer.renkan.l10n
+            translate: this.renderer.renkan.translate
         }));
     this.redraw();
     var _this = this;
@@ -686,19 +690,44 @@
         paper.view.draw();
     });
     if (!this.renderer.renkan.read_only) {
-        this.editor_$.find("input, textarea").bind("keyup change", function() {
-            var _uri = _this.editor_$.find(".Rk-Edit-URI").val(),
-                _image = _this.editor_$.find(".Rk-Edit-Image").val();
-            _this.editor_$.find(".Rk-Edit-ImgPreview").attr("src", _image);
-            _this.editor_$.find(".Rk-Edit-Goto").attr("href",_uri);
-            var _data = {
-                title: _this.editor_$.find(".Rk-Edit-Title").val(),
-                description: _this.editor_$.find(".Rk-Edit-Description").val(),
-                uri: _uri,
-                image: _image
-            }
-            _model.set(_data);
-            _this.redraw();
+    	
+    	var onFieldChange = Rkns._(function() {
+    		Rkns._(function() {
+	            var _uri = _this.editor_$.find(".Rk-Edit-URI").val(),
+	                _image = _this.editor_$.find(".Rk-Edit-Image").val();
+	            _this.editor_$.find(".Rk-Edit-ImgPreview").attr("src", _image);
+	            _this.editor_$.find(".Rk-Edit-Goto").attr("href",_uri);
+	            var _data = {
+	                title: _this.editor_$.find(".Rk-Edit-Title").val(),
+	                description: _this.editor_$.find(".Rk-Edit-Description").val(),
+	                uri: _uri,
+	                image: _image
+	            }
+	            _model.set(_data);
+	            _this.redraw();
+    		}).defer();
+    	}).throttle(500);
+    	
+    	this.editor_$.find("input, textarea").bind("change keyup paste", onFieldChange);
+    	
+        this.editor_$.find(".Rk-Edit-Image-File").bind("change", function() {
+        	if (this.files.length) {
+        		var f = this.files[0],
+        			fr = new FileReader();
+    			if (f.type.substr(0,5) !== "image") {
+    				alert(_this.renderer.renkan.translate("This file is not an image"));
+    				return;
+    			}
+    			if (f.size > (Rkns.Renderer._IMAGE_MAX_KB * 1024)) {
+    				alert(_this.renderer.renkan.translate("Image size must be under ")+Rkns.Renderer._IMAGE_MAX_KB+_this.renderer.renkan.translate("KB"));
+    				return;
+    			}
+        		fr.onload = function(e) {
+        			_this.editor_$.find(".Rk-Edit-Image").val(e.target.result);
+        			onFieldChange();
+        		}
+        		fr.readAsDataURL(f);
+        	}
         });
         this.editor_$.find(".Rk-Edit-Title")[0].focus();
         this.editor_$.find(".Rk-Edit-ColorPicker-Wrapper").hover(
@@ -754,23 +783,23 @@
 }
 
 Rkns.Renderer.EdgeEditor.prototype.template = Rkns._.template(
-    '<h2><span class="Rk-CloseX">&times;</span><%=l10n.edit_edge%></span></h2>'
-    + '<p><label><%=l10n.edit_title%></label><input class="Rk-Edit-Title" type="text" value="<%=edge.title%>"/></p>'
-    + '<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>'
-    + '<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">'
+    '<h2><span class="Rk-CloseX">&times;</span><%-translate("Edit Edge")%></span></h2>'
+    + '<p><label><%-translate("Title:")%></label><input class="Rk-Edit-Title" type="text" value="<%=edge.title%>"/></p>'
+    + '<p><label><%-translate("URI:")%></label><input class="Rk-Edit-URI" type="text" value="<%=edge.uri%>"/><a class="Rk-Edit-Goto" href="<%=edge.uri%>" target="_blank"></a></p>'
+    + '<div class="Rk-Editor-p"><span class="Rk-Editor-Label"><%-translate("Edge color:")%></span><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">'
     + '<% 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>'
-    + '<p><label><%=l10n.edit_from%></label><span class="Rk-UserColor" style="background:<%=edge.from_color%>;"></span><%=edge.from_title%></p>'
-    + '<p><label><%=l10n.edit_to%></label><span class="Rk-UserColor" style="background:<%=edge.to_color%>;"></span><%=edge.to_title%></p>'
-    + '<p><label><%=l10n.created_by%> </label><span class="Rk-UserColor" style="background:<%=edge.created_by_color%>;"></span><%=edge.created_by_title%></p>'
+    + '<p><span class="Rk-Editor-Label"><%-translate("From:")%></span><span class="Rk-UserColor" style="background:<%=edge.from_color%>;"></span><%=edge.from_title%></p>'
+    + '<p><span class="Rk-Editor-Label"><%-translate("To:")%></span><span class="Rk-UserColor" style="background:<%=edge.to_color%>;"></span><%=edge.to_title%></p>'
+    + '<p><span class="Rk-Editor-Label"><%-translate("Created by:")%></span><span class="Rk-UserColor" style="background:<%=edge.created_by_color%>;"></span><%=edge.created_by_title%></p>'
 );
 
 Rkns.Renderer.EdgeEditor.prototype.readOnlyTemplate = Rkns._.template(
     '<h2><span class="Rk-CloseX">&times;</span><span class="Rk-UserColor" style="background:<%=edge.color%>;"></span><%- edge.title %></span></h2>'
     + '<p><a href="<%-edge.uri%>" target="_blank"><%-edge.uri%></a></p>'
     + '<p><%-edge.description%></p>'
-    + '<p><label><%=l10n.edit_from%></label><span class="Rk-UserColor" style="background:<%=edge.from_color%>;"></span><%=edge.from_title%></p>'
-    + '<p><label><%=l10n.edit_to%></label><span class="Rk-UserColor" style="background:<%=edge.to_color%>;"></span><%=edge.to_title%></p>'
-    + '<p><label><%=l10n.created_by%> </label><span class="Rk-UserColor" style="background:<%=edge.created_by_color%>;"></span><%=edge.created_by_title%></p>'
+    + '<p><span class="Rk-Editor-Label"><%-translate("From:")%></span><span class="Rk-UserColor" style="background:<%=edge.from_color%>;"></span><%=edge.from_title%></p>'
+    + '<p><span class="Rk-Editor-Label"><%-translate("To:")%></span><span class="Rk-UserColor" style="background:<%=edge.to_color%>;"></span><%=edge.to_title%></p>'
+    + '<p><span class="Rk-Editor-Label"><%-translate("Created by:")%></span><span class="Rk-UserColor" style="background:<%=edge.created_by_color%>;"></span><%=edge.created_by_title%></p>'
 );
 
 Rkns.Renderer.EdgeEditor.prototype.draw = function() {
@@ -793,7 +822,7 @@
                 created_by_color: _created_by.get("color"),
                 created_by_title: _created_by.get("title")
             },
-            l10n: this.renderer.renkan.l10n
+            translate: this.renderer.renkan.translate
         }));
     this.redraw();
     var _this = this;
@@ -802,15 +831,20 @@
         paper.view.draw();
     });
     if (!this.renderer.renkan.read_only) {
-        this.editor_$.find("input, textarea").bind("keyup change", function() {
-            _this.editor_$.find(".Rk-Edit-Goto").attr("href",_this.editor_$.find(".Rk-Edit-URI").val());
-            var _data = {
-                title: _this.editor_$.find(".Rk-Edit-Title").val(),
-                uri: _this.editor_$.find(".Rk-Edit-URI").val()
-            }
-            _model.set(_data);
-            _this.redraw();
-        });
+    	
+    	var onFieldChange = Rkns._(function() {
+    		Rkns._(function() {
+    			_this.editor_$.find(".Rk-Edit-Goto").attr("href",_this.editor_$.find(".Rk-Edit-URI").val());
+	            var _data = {
+	                title: _this.editor_$.find(".Rk-Edit-Title").val(),
+	                uri: _this.editor_$.find(".Rk-Edit-URI").val()
+	            }
+	            _model.set(_data);
+	            _this.redraw();
+    		}).defer();
+    	}).throttle(500);
+    	
+        this.editor_$.find("input, textarea").bind("keyup change paste", onFieldChange);
         this.editor_$.find(".Rk-Edit-ColorPicker-Wrapper").hover(
             function() { _this.editor_$.find(".Rk-Edit-ColorPicker").show(); },
             function() { _this.editor_$.find(".Rk-Edit-ColorPicker").hide(); }
@@ -844,7 +878,7 @@
 Rkns.Renderer.NodeEditButton.prototype._init = function() {
     this.renderer.overlay_layer.activate();
     this.type = "Node-edit-button";
-    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);
+    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.translate("Edit"));
 }
 
 Rkns.Renderer.NodeEditButton.prototype.moveTo = function(_pos) {
@@ -887,7 +921,7 @@
 Rkns.Renderer.NodeRemoveButton.prototype._init = function() {
     this.renderer.overlay_layer.activate();
     this.type = "Node-remove-button";
-    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);
+    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.translate("Remove"));
 }
 
 Rkns.Renderer.NodeRemoveButton.prototype.moveTo = function(_pos) {
@@ -915,7 +949,7 @@
 
 Rkns.Renderer.NodeRemoveButton.prototype.mouseup = function() {
     this.renderer.removeRepresentationsOfType("editor");
-    if (confirm('Do you really wish to remove node "' + this.node_representation.model.get("title") + '"?')) {
+    if (confirm(this.renderer.renkan.translate('Do you really wish to remove node ') + '"' + this.node_representation.model.get("title") + '"?')) {
         this.project.removeNode(this.node_representation.model);
     }
 }
@@ -931,7 +965,7 @@
 Rkns.Renderer.NodeLinkButton.prototype._init = function() {
     this.renderer.overlay_layer.activate();
     this.type = "Node-link-button";
-    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);
+    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.translate("Link to another node"));
 }
 
 Rkns.Renderer.NodeLinkButton.prototype.moveTo = function(_pos) {
@@ -968,7 +1002,7 @@
 Rkns.Renderer.EdgeEditButton.prototype._init = function() {
     this.renderer.overlay_layer.activate();
     this.type = "Edge-edit-button";
-    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);
+    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"));
 }
 
 Rkns.Renderer.EdgeEditButton.prototype.moveTo = function(_pos) {
@@ -1011,7 +1045,7 @@
 Rkns.Renderer.EdgeRemoveButton.prototype._init = function() {
     this.renderer.overlay_layer.activate();
     this.type = "Edge-remove-button";
-    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);
+    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"));
 }
 Rkns.Renderer.EdgeRemoveButton.prototype.moveTo = function(_pos) {
     this.sector.moveTo(_pos);
@@ -1038,7 +1072,7 @@
 
 Rkns.Renderer.EdgeRemoveButton.prototype.mouseup = function() {
     this.renderer.removeRepresentationsOfType("editor");
-    if (confirm('Do you really wish to remove edge "' + this.edge_representation.model.get("title") + '"?')) {
+    if (confirm(this.renderer.renkan.translate('Do you really wish to remove edge ') + '"' + this.edge_representation.model.get("title") + '"?')) {
         this.project.removeEdge(this.edge_representation.model);
     }
 }
@@ -1092,6 +1126,59 @@
     this.canvas_$.mouseenter(function(_event) {
         _this.onMouseEnter(_event);
     });
+    this.canvas_$.on("dragover", function(_event) {
+    	_event.stopPropagation();
+    	_event.preventDefault();
+    })
+    this.canvas_$.on("drop", function(_event) {
+    	_event.stopPropagation();
+    	_event.preventDefault();
+    	var res = {}
+    	Rkns._(_event.originalEvent.dataTransfer.types).each(function(t) {
+    		return res[t] = _event.originalEvent.dataTransfer.getData(t);
+    	});
+    	var newNode = {};
+    	if (res["text/plain"]) {
+    		newNode.description = res["text/plain"].replace(/[\s\n]+/gm,' ').trim();
+    	}
+    	if (res["text/html"]) {
+    		var snippet = Rkns.$('<div>').html(res["text/html"]);
+    		newNode.image = snippet.find("img").attr("src") || '';
+    		newNode.uri = snippet.find("a").attr("href");
+    	}
+    	if (res["text/uri-list"]) {
+    		newNode.uri = res["text/uri-list"];
+    	}
+    	if (res["text/x-moz-url"]) {
+    		newNode.title = (res["text/x-moz-url"].split("\n")[1] || "").trim();
+    		if (newNode.title === newNode.uri) {
+    			newNode.title = "";
+    		}
+    	}
+    	if (newNode.title || newNode.description || newNode.uri) {
+    		console.log(newNode);
+    		var _off = _this.canvas_$.offset(),
+            _point = new paper.Point([
+                _event.originalEvent.pageX - _off.left,
+                _event.originalEvent.pageY - _off.top
+            ]),
+            _coords = _this.toModelCoords(_point),
+            _data = {
+                id: Rkns.Utils.getUID('node'),
+                created_by: _this.renkan.current_user,
+                uri: newNode.uri || "",
+                title: newNode.title || _this.renkan.translate("Dragged resource"),
+                description: newNode.description || "",
+                image: newNode.image || "",
+                position: {
+                    x: _coords.x,
+                    y: _coords.y
+                }
+            };
+        	var _node = _this.renkan.project.addNode(_data);
+            _this.getRepresentationByModel(_node).openEditor();
+    	}
+    })
     this.editor_$.find(".Rk-ZoomOut").click(function() {
         _this.offset = new paper.Point([
             _this.canvas_$.width(),
@@ -1141,7 +1228,7 @@
             _this.notif_$.hide();
         } else {
             _this.click_mode = Rkns.Renderer._CLICKMODE_ADDNODE;
-            _this.notif_$.html(_renkan.l10n.notif_add_node).fadeIn();
+            _this.notif_$.html(_renkan.translate("Click on the background canvas to add a node")).fadeIn();
         }
     });
     this.$.find(".Rk-AddEdge-Button").click(function() {
@@ -1150,7 +1237,7 @@
             _this.notif_$.hide();
         } else {
             _this.click_mode = Rkns.Renderer._CLICKMODE_STARTEDGE;
-            _this.notif_$.html(_renkan.l10n.notif_start_edge).fadeIn();
+            _this.notif_$.html(_renkan.translate("Click on a first node to start the edge")).fadeIn();
         }
     });
     this.$.find(".Rk-TopBar-Button").mouseover(function() {
@@ -1209,18 +1296,18 @@
 }
 
 Rkns.Renderer.Scene.prototype.template = Rkns._.template(
-    '<div class="Rk-TopBar"><% if (read_only) { %><h2 class="Rk-PadTitle"><%- project.get("title") || l10n.untitled_project%></h2>'
-    + '<% } else { %><input type="text" class="Rk-PadTitle" value="<%- project.get("title") || "" %>" placeholder="<%=l10n.untitled_project%>" /><% } %>'
+    '<div class="Rk-TopBar"><% if (read_only) { %><h2 class="Rk-PadTitle"><%- project.get("title") || translate("Untitled project")%></h2>'
+    + '<% } else { %><input type="text" class="Rk-PadTitle" value="<%- project.get("title") || "" %>" placeholder="<%-translate("Untitled project")%>" /><% } %>'
     + '<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>'
-    + '<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>'
+    + '<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>'
     + '<% if (!read_only) { %>'
-    + '<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>'
-    + '<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>'
-    + '<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>'
+    + '<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>'
+    + '<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>'
+    + '<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>'
     + '<div class="Rk-TopBar-Separator"></div></div>'
     + '<% } %>'
     + '<canvas class="Rk-Canvas" resize></canvas><div class="Rk-Editor"><div class="Rk-Notifications"></div>'
-    + '<div class="Rk-ZoomButtons"><div class="Rk-ZoomIn" title="<%=l10n.zoom_in%>"></div><div class="Rk-ZoomOut" title="<%=l10n.zoom_out%>"></div></div>'
+    + '<div class="Rk-ZoomButtons"><div class="Rk-ZoomIn" title="<%-translate("Zoom In")%>"></div><div class="Rk-ZoomOut" title="<%-translate("Zoom Out")%>"></div></div>'
     + '</div>'
 );
 
@@ -1441,7 +1528,7 @@
             this.addTempEdge(this.click_target, _event.point);
             this.click_mode = Rkns.Renderer._CLICKMODE_ENDEDGE;
             this.notif_$.fadeOut(function() {
-                Rkns.$(this).html(_renkan.l10n.notif_end_edge).fadeIn();
+                Rkns.$(this).html(_renkan.translate("Click on a second node to complete the edge")).fadeIn();
             });
         } else {
             this.notif_$.hide();
--- a/client/js/twitter-bin.js	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/js/twitter-bin.js	Tue Feb 26 17:04:24 2013 +0100
@@ -11,7 +11,7 @@
 }
 
 Rkns.Twitter.Search.prototype.getSearchTitle = function() {
-	return this.renkan.l10n.search_on_twitter;
+	return this.renkan.translate("Twitter");
 }
 
 Rkns.Twitter.Search.prototype.search = function(_q) {
--- a/client/js/wikipedia-bin.js	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/js/wikipedia-bin.js	Tue Feb 26 17:04:24 2013 +0100
@@ -11,7 +11,16 @@
 }
 
 Rkns.Wikipedia.Search.prototype.getSearchTitle = function() {
-    return ( this.renkan.l10n["wiki_" + this.lang] || this.renkan.l10n.wiki_) ;
+	var langs = {
+		"fr": "French",
+		"en": "English",
+		"ja": "Japanese"
+	}
+	if (langs[this.lang]) {
+		return this.renkan.translate("Wikipedia in ") + this.renkan.translate(langs[this.lang]);
+	} else {
+		return this.renkan.translate("Wikipedia") + " [" + this.lang + "]";
+	}
 }
 
 Rkns.Wikipedia.Search.prototype.search = function(_q) {
--- a/client/render-test.html	Tue Feb 26 11:09:57 2013 +0100
+++ b/client/render-test.html	Tue Feb 26 17:04:24 2013 +0100
@@ -47,7 +47,8 @@
                             type: Rkns.Twitter.Search
                         }
                     ],
-                    user_id: "u-iri"
+                    user_id: "u-iri",
+                    language: "fr"
                 });
             });
         </script>